Drawing Shapes with Classes - Faster¶
shapes_buffered.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/index.html#shape-lists for some examples.
12
13Also, any Sprite class put in a SpriteList and drawn with the SpriteList will
14be drawn using Vertex Buffer Objects for better performance.
15
16If Python and Arcade are installed, this example can be run from the command line with:
17python -m arcade.examples.shapes
18
19"""
20
21import arcade
22import random
23import timeit
24
25# Set up the constants
26SCREEN_WIDTH = 800
27SCREEN_HEIGHT = 600
28SCREEN_TITLE = "Shapes! 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 self.shape_list = None
50
51 def move(self):
52 self.x += self.delta_x
53 self.y += self.delta_y
54 self.angle += self.delta_angle
55 if self.x < 0 and self.delta_x < 0:
56 self.delta_x *= -1
57 if self.y < 0 and self.delta_y < 0:
58 self.delta_y *= -1
59 if self.x > SCREEN_WIDTH and self.delta_x > 0:
60 self.delta_x *= -1
61 if self.y > SCREEN_HEIGHT and self.delta_y > 0:
62 self.delta_y *= -1
63
64 def draw(self):
65 self.shape_list.center_x = self.x
66 self.shape_list.center_y = self.y
67 # self.shape_list.angle = self.angle
68 self.shape_list.draw()
69
70class Ellipse(Shape):
71
72 def __init__(self, x, y, width, height, angle, delta_x, delta_y,
73 delta_angle, color):
74
75 super().__init__(x, y, width, height, angle, delta_x, delta_y,
76 delta_angle, color)
77
78 shape = arcade.create_ellipse_filled(0, 0,
79 self.width, self.height,
80 self.color, self.angle)
81 self.shape_list = arcade.ShapeElementList()
82 self.shape_list.append(shape)
83
84
85class Rectangle(Shape):
86
87 def __init__(self, x, y, width, height, angle, delta_x, delta_y,
88 delta_angle, color):
89
90 super().__init__(x, y, width, height, angle, delta_x, delta_y,
91 delta_angle, color)
92
93 shape = arcade.create_rectangle_filled(0, 0,
94 self.width, self.height,
95 self.color, self.angle)
96 self.shape_list = arcade.ShapeElementList()
97 self.shape_list.append(shape)
98
99class Line(Shape):
100
101 def __init__(self, x, y, width, height, angle, delta_x, delta_y,
102 delta_angle, color):
103
104 super().__init__(x, y, width, height, angle, delta_x, delta_y,
105 delta_angle, color)
106
107 shape = arcade.create_line(0, 0,
108 self.width, self.height,
109 self.color, 2)
110 self.shape_list = arcade.ShapeElementList()
111 self.shape_list.append(shape)
112
113class MyGame(arcade.Window):
114 """ Main application class. """
115
116 def __init__(self):
117 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
118 self.shape_list = None
119
120 self.processing_time = 0
121 self.draw_time = 0
122 self.frame_count = 0
123 self.fps_start_timer = None
124 self.fps = None
125
126 def setup(self):
127 """ Set up the game and initialize the variables. """
128 self.shape_list = []
129
130 for i in range(NUMBER_OF_SHAPES):
131 x = random.randrange(0, SCREEN_WIDTH)
132 y = random.randrange(0, SCREEN_HEIGHT)
133 width = random.randrange(10, 30)
134 height = random.randrange(10, 30)
135 angle = random.randrange(0, 360)
136
137 d_x = random.randrange(-3, 4)
138 d_y = random.randrange(-3, 4)
139 d_angle = random.randrange(-3, 4)
140
141 red = random.randrange(256)
142 green = random.randrange(256)
143 blue = random.randrange(256)
144 alpha = random.randrange(256)
145
146 shape_type = random.randrange(3)
147 # shape_type = 2
148
149 if shape_type == 0:
150 shape = Rectangle(x, y, width, height, angle, d_x, d_y,
151 d_angle, (red, green, blue, alpha))
152 elif shape_type == 1:
153 shape = Ellipse(x, y, width, height, angle, d_x, d_y,
154 d_angle, (red, green, blue, alpha))
155 elif shape_type == 2:
156 shape = Line(x, y, width, height, angle, d_x, d_y,
157 d_angle, (red, green, blue, alpha))
158
159 self.shape_list.append(shape)
160
161 def on_update(self, dt):
162 """ Move everything """
163 start_time = timeit.default_timer()
164
165 for shape in self.shape_list:
166 shape.move()
167
168 self.processing_time = timeit.default_timer() - start_time
169
170 def on_draw(self):
171 """
172 Render the screen.
173 """
174 # Start timing how long this takes
175 draw_start_time = timeit.default_timer()
176
177 if self.frame_count % 60 == 0:
178 if self.fps_start_timer is not None:
179 total_time = timeit.default_timer() - self.fps_start_timer
180 self.fps = 60 / total_time
181 self.fps_start_timer = timeit.default_timer()
182 self.frame_count += 1
183
184 arcade.start_render()
185
186 for shape in self.shape_list:
187 shape.draw()
188
189 # Display timings
190 output = f"Processing time: {self.processing_time:.3f}"
191 arcade.draw_text(output, 20, SCREEN_HEIGHT - 20, arcade.color.WHITE, 16)
192
193 output = f"Drawing time: {self.draw_time:.3f}"
194 arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.WHITE, 16)
195
196 if self.fps is not None:
197 output = f"FPS: {self.fps:.0f}"
198 arcade.draw_text(output, 20, SCREEN_HEIGHT - 60, arcade.color.WHITE, 16)
199
200 self.draw_time = timeit.default_timer() - draw_start_time
201
202
203def main():
204 window = MyGame()
205 window.setup()
206 arcade.run()
207
208
209if __name__ == "__main__":
210 main()