1"""
2Moving Sprite Stress Test
3
4Simple program to test how fast we can draw sprites that are moving
5
6Artwork from http://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"""
11
12import arcade
13import random
14import os
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 # Set the working directory (where we expect to find files) to the same
68 # directory this .py file is in. You can leave this out of your own
69 # code, but it is needed to easily run the examples using "python -m"
70 # as mentioned at the top of this program.
71 file_path = os.path.dirname(os.path.abspath(__file__))
72 os.chdir(file_path)
73
74 # Variables that will hold sprite lists
75 self.coin_list = None
76 self.player_list = None
77 self.player = None
78
79 self.processing_time = 0
80 self.draw_time = 0
81 self.program_start_time = timeit.default_timer()
82 self.sprite_count_list = []
83 self.fps_list = []
84 self.processing_time_list = []
85 self.drawing_time_list = []
86 self.last_fps_reading = 0
87 self.fps = FPSCounter()
88
89 arcade.set_background_color(arcade.color.AMAZON)
90
91 # Open file to save timings
92 self.results_file = open(RESULTS_FILE, "w")
93
94 def add_coins(self):
95
96 # Create the coins
97 for i in range(COIN_COUNT_INCREMENT):
98 # Create the coin instance
99 # Coin image from kenney.nl
100 coin = arcade.Sprite(":resources:images/items/coinGold.png", SPRITE_SCALING_COIN)
101
102 # Position the coin
103 coin.center_x = random.randrange(SPRITE_SIZE, SCREEN_WIDTH - SPRITE_SIZE)
104 coin.center_y = random.randrange(SPRITE_SIZE, SCREEN_HEIGHT - SPRITE_SIZE)
105
106 # Add the coin to the lists
107 self.coin_list.append(coin)
108
109 def setup(self):
110 """ Set up the game and initialize the variables. """
111
112 # Sprite lists
113 self.coin_list = arcade.SpriteList(use_spatial_hash=USE_SPATIAL_HASHING)
114 self.player_list = arcade.SpriteList()
115 self.player = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png", SPRITE_SCALING_PLAYER)
116 self.player.center_x = random.randrange(SCREEN_WIDTH)
117 self.player.center_y = random.randrange(SCREEN_HEIGHT)
118 self.player.change_x = 3
119 self.player.change_y = 5
120 self.player_list.append(self.player)
121
122 def on_draw(self):
123 """ Draw everything """
124
125 # Start timing how long this takes
126 draw_start_time = timeit.default_timer()
127
128 arcade.start_render()
129 self.coin_list.draw()
130 self.player_list.draw()
131
132 # Display info on sprites
133 output = f"Sprite count: {len(self.coin_list):,}"
134 arcade.draw_text(output, 20, SCREEN_HEIGHT - 20, arcade.color.BLACK, 16)
135
136 # Display timings
137 output = f"Processing time: {self.processing_time:.3f}"
138 arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.BLACK, 16)
139
140 output = f"Drawing time: {self.draw_time:.3f}"
141 arcade.draw_text(output, 20, SCREEN_HEIGHT - 60, arcade.color.BLACK, 16)
142
143 fps = self.fps.get_fps()
144 output = f"FPS: {fps:3.0f}"
145 arcade.draw_text(output, 20, SCREEN_HEIGHT - 80, arcade.color.BLACK, 16)
146
147 self.draw_time = timeit.default_timer() - draw_start_time
148 self.fps.tick()
149
150 def update(self, delta_time):
151 # Start update timer
152
153 start_time = timeit.default_timer()
154
155 self.player_list.update()
156 if self.player.center_x < 0 and self.player.change_x < 0:
157 self.player.change_x *= -1
158 if self.player.center_y < 0 and self.player.change_y < 0:
159 self.player.change_y *= -1
160
161 if self.player.center_x > SCREEN_WIDTH and self.player.change_x > 0:
162 self.player.change_x *= -1
163 if self.player.center_y > SCREEN_HEIGHT and self.player.change_y > 0:
164 self.player.change_y *= -1
165
166 coin_hit_list = arcade.check_for_collision_with_list(self.player, self.coin_list)
167 for coin in coin_hit_list:
168 coin.center_x = random.randrange(SCREEN_WIDTH)
169 coin.center_y = random.randrange(SCREEN_HEIGHT)
170
171 # Save the time it took to do this.
172 self.processing_time = timeit.default_timer() - start_time
173
174 # Total time program has been running
175 total_program_time = int(timeit.default_timer() - self.program_start_time)
176
177 # Print out stats, or add more sprites
178 if total_program_time > self.last_fps_reading:
179 self.last_fps_reading = total_program_time
180
181 # It takes the program a while to "warm up", so the first
182 # few seconds our readings will be off. So wait some time
183 # before taking readings
184 if total_program_time > 5:
185
186 # We want the program to run for a while before taking
187 # timing measurements. We don't want the time it takes
188 # to add new sprites to be part of that measurement. So
189 # make sure we have a clear second of nothing but
190 # running the sprites, and not adding the sprites.
191 if total_program_time % 2 == 1:
192
193 output = f"{total_program_time}, {len(self.coin_list)}, {self.fps.get_fps():.1f}, " \
194 f"{self.processing_time:.4f}, {self.draw_time:.4f}\n"
195 print(output, end="")
196 self.results_file.write(output)
197
198 if len(self.coin_list) >= STOP_COUNT:
199 self.results_file.close()
200 pyglet.app.exit()
201 return
202
203 # Take timings
204 print(f"{total_program_time}, {len(self.coin_list)}, {self.fps.get_fps():.1f}, "
205 f"{self.processing_time:.4f}, {self.draw_time:.4f}")
206 self.sprite_count_list.append(len(self.coin_list))
207 self.fps_list.append(round(self.fps.get_fps(), 1))
208 self.processing_time_list.append(self.processing_time)
209 self.drawing_time_list.append(self.draw_time)
210
211 # Now add the coins
212 self.add_coins()
213
214
215def main():
216 """ Main method """
217 window = MyGame()
218 window.setup()
219 arcade.run()
220
221
222if __name__ == "__main__":
223 main()