Collision Stress Test#

Screenshot of stress test example
stress_test_collision_arcade.py#
  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()