Drawing Shapes with Classes - Slow

Screenshot of Shapes example program
shapes.py
  1"""
  2This simple animation example shows how to use classes to animate
  3multiple objects on the screen at the same time.
  4
  5Because this is redraws the shapes from scratch each frame, this is SLOW
  6and inefficient.
  7
  8Using buffered drawing commands (Vertex Buffer Objects) is a bit more complex,
  9but faster.
 10
 11See http://arcade.academy/examples/shapes_buffered.html for this same example
 12using shape element lists.
 13
 14Also, any Sprite class put in a SpriteList and drawn with the SpriteList will
 15be drawn using Vertex Buffer Objects for better performance.
 16
 17If Python and Arcade are installed, this example can be run from the command line with:
 18python -m arcade.examples.shapes_buffered
 19"""
 20
 21import arcade
 22import random
 23import timeit
 24
 25# Set up the constants
 26SCREEN_WIDTH = 800
 27SCREEN_HEIGHT = 600
 28SCREEN_TITLE = "Shapes! Non-buffered"
 29
 30RECT_WIDTH = 50
 31RECT_HEIGHT = 50
 32
 33NUMBER_OF_SHAPES = 500
 34
 35
 36class Shape:
 37    """ Generic base shape class """
 38    def __init__(self, x, y, width, height, angle, delta_x, delta_y,
 39                 delta_angle, color):
 40        self.x = x
 41        self.y = y
 42        self.width = width
 43        self.height = height
 44        self.angle = angle
 45        self.delta_x = delta_x
 46        self.delta_y = delta_y
 47        self.delta_angle = delta_angle
 48        self.color = color
 49
 50    def move(self):
 51        self.x += self.delta_x
 52        self.y += self.delta_y
 53        self.angle += self.delta_angle
 54        if self.x < 0 and self.delta_x < 0:
 55            self.delta_x *= -1
 56        if self.y < 0 and self.delta_y < 0:
 57            self.delta_y *= -1
 58        if self.x > SCREEN_WIDTH and self.delta_x > 0:
 59            self.delta_x *= -1
 60        if self.y > SCREEN_HEIGHT and self.delta_y > 0:
 61            self.delta_y *= -1
 62
 63class Ellipse(Shape):
 64
 65    def draw(self):
 66        arcade.draw_ellipse_filled(self.x, self.y, self.width, self.height,
 67                                   self.color, self.angle)
 68
 69
 70class Rectangle(Shape):
 71
 72    def draw(self):
 73        arcade.draw_rectangle_filled(self.x, self.y, self.width, self.height,
 74                                     self.color, self.angle)
 75
 76class Line(Shape):
 77
 78    def draw(self):
 79        arcade.draw_line(self.x, self.y,
 80                         self.x + self.width, self.y + self.height,
 81                         self.color, 2)
 82
 83
 84class MyGame(arcade.Window):
 85    """ Main application class. """
 86
 87    def __init__(self):
 88        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
 89        self.shape_list = None
 90
 91        self.processing_time = 0
 92        self.draw_time = 0
 93        self.frame_count = 0
 94        self.fps_start_timer = None
 95        self.fps = None
 96
 97    def setup(self):
 98        """ Set up the game and initialize the variables. """
 99        self.shape_list = []
100
101        for i in range(NUMBER_OF_SHAPES):
102            x = random.randrange(0, SCREEN_WIDTH)
103            y = random.randrange(0, SCREEN_HEIGHT)
104            width = random.randrange(10, 30)
105            height = random.randrange(10, 30)
106            angle = random.randrange(0, 360)
107
108            d_x = random.randrange(-3, 4)
109            d_y = random.randrange(-3, 4)
110            d_angle = random.randrange(-3, 4)
111
112            red = random.randrange(256)
113            green = random.randrange(256)
114            blue = random.randrange(256)
115            alpha = random.randrange(256)
116
117            shape_type = random.randrange(3)
118            # shape_type = 2
119
120            if shape_type == 0:
121                shape = Rectangle(x, y, width, height, angle, d_x, d_y,
122                                  d_angle, (red, green, blue, alpha))
123            elif shape_type == 1:
124                shape = Ellipse(x, y, width, height, angle, d_x, d_y,
125                            d_angle, (red, green, blue, alpha))
126            elif shape_type == 2:
127                shape = Line(x, y, width, height, angle, d_x, d_y,
128                             d_angle, (red, green, blue, alpha))
129
130            self.shape_list.append(shape)
131
132    def on_update(self, dt):
133        """ Move everything """
134        start_time = timeit.default_timer()
135
136        for shape in self.shape_list:
137            shape.move()
138
139        self.processing_time = timeit.default_timer() - start_time
140
141    def on_draw(self):
142        """
143        Render the screen.
144        """
145        # Start timing how long this takes
146        draw_start_time = timeit.default_timer()
147
148        if self.frame_count % 60 == 0:
149            if self.fps_start_timer is not None:
150                total_time = timeit.default_timer() - self.fps_start_timer
151                self.fps = 60 / total_time
152            self.fps_start_timer = timeit.default_timer()
153        self.frame_count += 1
154
155        arcade.start_render()
156
157        for shape in self.shape_list:
158            shape.draw()
159
160        # Display timings
161        output = f"Processing time: {self.processing_time:.3f}"
162        arcade.draw_text(output, 20, SCREEN_HEIGHT - 20, arcade.color.WHITE, 16)
163
164        output = f"Drawing time: {self.draw_time:.3f}"
165        arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.WHITE, 16)
166
167        if self.fps is not None:
168            output = f"FPS: {self.fps:.0f}"
169            arcade.draw_text(output, 20, SCREEN_HEIGHT - 60, arcade.color.WHITE, 16)
170
171        self.draw_time = timeit.default_timer() - draw_start_time
172
173
174def main():
175    window = MyGame()
176    window.setup()
177    arcade.run()
178
179
180if __name__ == "__main__":
181    main()