1"""
2Moving Sprite Stress Test
3
4Simple program to test how fast we can draw sprites that are moving
5
6Artwork from https://kenney.nl
7
8If Python and Arcade are installed, this example can be run from the command line with:
9python -m arcade.examples.stress_test_draw_moving
10"""
11from __future__ import annotations
12
13import arcade
14import random
15import timeit
16import time
17import collections
18import pyglet
19
20# --- Constants ---
21SPRITE_SCALING_COIN = 0.09
22SPRITE_SCALING_PLAYER = 0.5
23SPRITE_NATIVE_SIZE = 128
24SPRITE_SIZE = int(SPRITE_NATIVE_SIZE * SPRITE_SCALING_COIN)
25COIN_COUNT_INCREMENT = 500
26
27STOP_COUNT = 12000
28
29SCREEN_WIDTH = 1800
30SCREEN_HEIGHT = 1000
31SCREEN_TITLE = "Moving Sprite Stress Test"
32
33USE_SPATIAL_HASHING = True
34if USE_SPATIAL_HASHING:
35 RESULTS_FILE = "stress_test_collision_arcade_spatial.csv"
36else:
37 RESULTS_FILE = "stress_test_collision_arcade.csv"
38
39
40class FPSCounter:
41 def __init__(self):
42 self.time = time.perf_counter()
43 self.frame_times = collections.deque(maxlen=60)
44
45 def tick(self):
46 t1 = time.perf_counter()
47 dt = t1 - self.time
48 self.time = t1
49 self.frame_times.append(dt)
50
51 def get_fps(self):
52 total_time = sum(self.frame_times)
53 if total_time == 0:
54 return 0
55 else:
56 return len(self.frame_times) / sum(self.frame_times)
57
58
59class MyGame(arcade.Window):
60 """ Our custom Window Class"""
61
62 def __init__(self):
63 """ Initializer """
64 # Call the parent class initializer
65 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
66
67 # Variables that will hold sprite lists
68 self.coin_list = None
69 self.player_list = None
70 self.player = None
71
72 self.processing_time = 0
73 self.draw_time = 0
74 self.program_start_time = timeit.default_timer()
75 self.sprite_count_list = []
76 self.fps_list = []
77 self.processing_time_list = []
78 self.drawing_time_list = []
79 self.last_fps_reading = 0
80 self.fps = FPSCounter()
81
82 self.background_color = arcade.color.AMAZON
83
84 # Open file to save timings
85 self.results_file = open(RESULTS_FILE, "w")
86
87 def add_coins(self):
88
89 # Create the coins
90 for i in range(COIN_COUNT_INCREMENT):
91 # Create the coin instance
92 # Coin image from kenney.nl
93 coin = arcade.Sprite(":resources:images/items/coinGold.png", SPRITE_SCALING_COIN)
94
95 # Position the coin
96 coin.center_x = random.randrange(SPRITE_SIZE, SCREEN_WIDTH - SPRITE_SIZE)
97 coin.center_y = random.randrange(SPRITE_SIZE, SCREEN_HEIGHT - SPRITE_SIZE)
98
99 # Add the coin to the lists
100 self.coin_list.append(coin)
101
102 def setup(self):
103 """ Set up the game and initialize the variables. """
104
105 # Sprite lists
106 self.coin_list = arcade.SpriteList(use_spatial_hash=USE_SPATIAL_HASHING)
107 self.player_list = arcade.SpriteList()
108 self.player = arcade.Sprite(
109 ":resources:images/animated_characters/female_person/femalePerson_idle.png",
110 SPRITE_SCALING_PLAYER,
111 )
112 self.player.center_x = random.randrange(SCREEN_WIDTH)
113 self.player.center_y = random.randrange(SCREEN_HEIGHT)
114 self.player.change_x = 3
115 self.player.change_y = 5
116 self.player_list.append(self.player)
117
118 def on_draw(self):
119 """ Draw everything """
120
121 # Start timing how long this takes
122 draw_start_time = timeit.default_timer()
123
124 self.clear()
125 self.coin_list.draw()
126 self.player_list.draw()
127
128 # Display info on sprites
129 output = f"Sprite count: {len(self.coin_list):,}"
130 arcade.draw_text(output, 20, SCREEN_HEIGHT - 20, arcade.color.BLACK, 16)
131
132 # Display timings
133 output = f"Processing time: {self.processing_time:.3f}"
134 arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.BLACK, 16)
135
136 output = f"Drawing time: {self.draw_time:.3f}"
137 arcade.draw_text(output, 20, SCREEN_HEIGHT - 60, arcade.color.BLACK, 16)
138
139 fps = self.fps.get_fps()
140 output = f"FPS: {fps:3.0f}"
141 arcade.draw_text(output, 20, SCREEN_HEIGHT - 80, arcade.color.BLACK, 16)
142
143 self.draw_time = timeit.default_timer() - draw_start_time
144 self.fps.tick()
145
146 def on_update(self, delta_time):
147 # Start update timer
148
149 start_time = timeit.default_timer()
150
151 self.player_list.update()
152 if self.player.center_x < 0 and self.player.change_x < 0:
153 self.player.change_x *= -1
154 if self.player.center_y < 0 and self.player.change_y < 0:
155 self.player.change_y *= -1
156
157 if self.player.center_x > SCREEN_WIDTH and self.player.change_x > 0:
158 self.player.change_x *= -1
159 if self.player.center_y > SCREEN_HEIGHT and self.player.change_y > 0:
160 self.player.change_y *= -1
161
162 coin_hit_list = arcade.check_for_collision_with_list(self.player, self.coin_list)
163 for coin in coin_hit_list:
164 coin.center_x = random.randrange(SCREEN_WIDTH)
165 coin.center_y = random.randrange(SCREEN_HEIGHT)
166
167 # Save the time it took to do this.
168 self.processing_time = timeit.default_timer() - start_time
169
170 # Total time program has been running
171 total_program_time = int(timeit.default_timer() - self.program_start_time)
172
173 # Print out stats, or add more sprites
174 if total_program_time > self.last_fps_reading:
175 self.last_fps_reading = total_program_time
176
177 # It takes the program a while to "warm up", so the first
178 # few seconds our readings will be off. So wait some time
179 # before taking readings
180 if total_program_time > 5:
181
182 # We want the program to run for a while before taking
183 # timing measurements. We don't want the time it takes
184 # to add new sprites to be part of that measurement. So
185 # make sure we have a clear second of nothing but
186 # running the sprites, and not adding the sprites.
187 if total_program_time % 2 == 1:
188
189 output = f"{total_program_time}, {len(self.coin_list)}, {self.fps.get_fps():.1f}, " \
190 f"{self.processing_time:.4f}, {self.draw_time:.4f}\n"
191 print(output, end="")
192 self.results_file.write(output)
193
194 if len(self.coin_list) >= STOP_COUNT:
195 self.results_file.close()
196 pyglet.app.exit()
197 return
198
199 # Take timings
200 print(f"{total_program_time}, {len(self.coin_list)}, {self.fps.get_fps():.1f}, "
201 f"{self.processing_time:.4f}, {self.draw_time:.4f}")
202 self.sprite_count_list.append(len(self.coin_list))
203 self.fps_list.append(round(self.fps.get_fps(), 1))
204 self.processing_time_list.append(self.processing_time)
205 self.drawing_time_list.append(self.draw_time)
206
207 # Now add the coins
208 self.add_coins()
209
210
211def main():
212 """ Main function """
213 window = MyGame()
214 window.setup()
215 arcade.run()
216
217
218if __name__ == "__main__":
219 main()