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