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