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