Sprite Explosions Particles

Screenshot of using sprites to shoot things
sprite_explosion_particles.py
  1"""
  2Sprite Explosion
  3
  4Simple program to show creating explosions with sprites.
  5There are more performant ways to do this, but for simple games this
  6is a good way to get started.
  7
  8Artwork from https://kenney.nl
  9
 10If Python and Arcade are installed, this example can be run from the command line with:
 11python -m arcade.examples.sprite_explosion_particles
 12"""
 13import random
 14import math
 15import arcade
 16
 17SPRITE_SCALING_PLAYER = 0.5
 18SPRITE_SCALING_COIN = 0.3
 19SPRITE_SCALING_LASER = 0.8
 20ENEMY_COUNT = 50
 21
 22WINDOW_WIDTH = 1280
 23WINDOW_HEIGHT = 720
 24WINDOW_TITLE = "Sprite Explosion Example"
 25
 26BULLET_SPEED = 5
 27
 28# --- Explosion Particles Related
 29
 30# How fast the particle will accelerate down. Make 0 if not desired
 31PARTICLE_GRAVITY = 0.05
 32
 33# How fast to fade the particle
 34PARTICLE_FADE_RATE = 8
 35
 36# How fast the particle moves. Range is from 2.5 <--> 5 with 2.5 and 2.5 set.
 37PARTICLE_MIN_SPEED = 2.5
 38PARTICLE_SPEED_RANGE = 2.5
 39
 40# How many particles per explosion
 41PARTICLE_COUNT = 20
 42
 43# How big the particle
 44PARTICLE_RADIUS = 3
 45
 46# Possible particle colors
 47PARTICLE_COLORS = [arcade.color.ALIZARIN_CRIMSON,
 48                   arcade.color.COQUELICOT,
 49                   arcade.color.LAVA,
 50                   arcade.color.KU_CRIMSON,
 51                   arcade.color.DARK_TANGERINE]
 52
 53# Chance we'll flip the texture to white and make it 'sparkle'
 54PARTICLE_SPARKLE_CHANCE = 0.02
 55
 56# --- Smoke
 57# Note: Adding smoke trails makes for a lot of sprites and can slow things
 58# down. If you want a lot, it will be necessary to move processing to GPU
 59# using transform feedback. If to slow, just get rid of smoke.
 60
 61# Start scale of smoke, and how fast is scales up
 62SMOKE_START_SCALE = 0.25
 63SMOKE_EXPANSION_RATE = 0.03
 64
 65# Rate smoke fades, and rises
 66SMOKE_FADE_RATE = 7
 67SMOKE_RISE_RATE = 0.5
 68
 69# Chance we leave smoke trail
 70SMOKE_CHANCE = 0.25
 71
 72
 73class Smoke(arcade.SpriteCircle):
 74    """Particle with smoke like behavior."""
 75    def __init__(self, size):
 76        super().__init__(size, arcade.color.LIGHT_GRAY, soft=True)
 77        self.change_y = SMOKE_RISE_RATE
 78        self.scale = SMOKE_START_SCALE
 79
 80    def update(self, delta_time: float = 1/60):
 81        """Update this particle"""
 82        # Take delta_time into account
 83        time_step = 60 * delta_time
 84
 85        if self.alpha <= PARTICLE_FADE_RATE:
 86            # Remove faded out particles
 87            self.remove_from_sprite_lists()
 88        else:
 89            # Update values
 90            self.alpha -= int(SMOKE_FADE_RATE * time_step)
 91            self.center_x += self.change_x * time_step
 92            self.center_y += self.change_y * time_step
 93            self.add_scale(SMOKE_EXPANSION_RATE * time_step)
 94
 95
 96class Particle(arcade.SpriteCircle):
 97    """ Explosion particle"""
 98    def __init__(self):
 99        """
100        Simple particle sprite based on circle sprite.
101        """
102        # Make the particle
103        super().__init__(PARTICLE_RADIUS, random.choice(PARTICLE_COLORS))
104
105        # Set direction/speed
106        speed = random.random() * PARTICLE_SPEED_RANGE + PARTICLE_MIN_SPEED
107        direction = random.randrange(360)
108        self.change_x = math.sin(math.radians(direction)) * speed
109        self.change_y = math.cos(math.radians(direction)) * speed
110
111    def update(self, delta_time: float = 1 / 60):
112        """Update the particle"""
113        # Take delta_time into account
114        time_step = 60 * delta_time
115
116        if self.alpha == 0:
117            # Faded out, remove
118            self.remove_from_sprite_lists()
119        else:
120            # Gradually fade out the particle. Don't go below 0
121            self.alpha = max(0, self.alpha - PARTICLE_FADE_RATE)
122            # Move the particle
123            self.center_x += self.change_x * time_step
124            self.center_y += self.change_y * time_step
125            self.change_y -= PARTICLE_GRAVITY * time_step
126
127            # Should we sparkle this?
128            if random.random() <= PARTICLE_SPARKLE_CHANCE:
129                self.alpha = 255
130                self.color = arcade.color.WHITE
131
132            # Leave a smoke particle?
133            if random.random() <= SMOKE_CHANCE:
134                smoke = Smoke(5)
135                smoke.position = self.position
136                # Add a smoke particle to the spritelist this sprite is in
137                self.sprite_lists[0].append(smoke)
138
139
140class GameView(arcade.View):
141    """ Main application class. """
142
143    def __init__(self):
144        """ Initializer """
145        # Call the parent class initializer
146        super().__init__()
147
148        # Variables that will hold sprite lists
149        self.player_list = arcade.SpriteList()
150        self.enemy_list = arcade.SpriteList()
151        self.bullet_list = arcade.SpriteList()
152        self.explosions_list = arcade.SpriteList()
153
154        # Set up the player info. Image from kenney.nl
155        self.player_sprite = arcade.Sprite(
156            ":resources:images/space_shooter/playerShip2_orange.png",
157            scale=SPRITE_SCALING_PLAYER,
158        )
159        self.player_sprite.center_x = 50
160        self.player_sprite.center_y = 70
161        self.player_list.append(self.player_sprite)
162
163        self.score = 0
164
165        # Don't show the mouse cursor
166        self.window.set_mouse_visible(False)
167
168        # Load sounds. Sounds from kenney.nl
169        self.gun_sound = arcade.sound.load_sound(":resources:sounds/laser2.wav")
170        self.hit_sound = arcade.sound.load_sound(":resources:sounds/explosion2.wav")
171
172        self.background_color = arcade.color.BLACK
173        self.score_display = arcade.Text("", 10, 20, arcade.color.WHITE, 14)
174
175        self.spawn_enemies()
176
177    def reset(self):
178        """Restart the game"""
179        # Reset score
180        self.score = 0
181
182        self.enemy_list.clear()
183        self.bullet_list.clear()
184        self.explosions_list.clear()
185
186        self.spawn_enemies()
187
188    def spawn_enemies(self):
189        # Spawn enemies
190        for index in range(ENEMY_COUNT):
191            # Create the coin instance. Image from kenney.nl
192            enemy = arcade.Sprite(
193                ":resources:images/space_shooter/playerShip1_green.png",
194                scale=SPRITE_SCALING_COIN,
195                angle=180,
196                center_x=random.randrange(25, WINDOW_WIDTH - 25),
197                center_y=random.randrange(150, WINDOW_HEIGHT)
198            )
199            # Add the ship to the lists
200            self.enemy_list.append(enemy)
201
202    def on_draw(self):
203        """
204        Render the screen.
205        """
206        # This command has to happen before we start drawing
207        self.clear()
208
209        # Draw all the sprites.
210        self.enemy_list.draw()
211        self.bullet_list.draw()
212        self.player_list.draw()
213        self.explosions_list.draw()
214
215        # Render the text
216        self.score_display.text = f"Score: {self.score}"
217        self.score_display.draw()
218
219    def on_mouse_motion(self, x, y, dx, dy):
220        """
221        Called whenever the mouse moves.
222        """
223        self.player_sprite.center_x = x
224
225    def on_mouse_press(self, x, y, button, modifiers):
226        """
227        Called whenever the mouse button is clicked.
228        """
229        # Gunshot sound
230        arcade.sound.play_sound(self.gun_sound)
231
232        # Create a bullet
233        bullet = arcade.Sprite(
234            ":resources:images/space_shooter/laserBlue01.png",
235            scale=SPRITE_SCALING_LASER,
236        )
237
238        # The image points to the right, and we want it to point up. So
239        # rotate it.
240        bullet.angle = 270
241
242        # Give it a speed
243        bullet.change_y = BULLET_SPEED
244
245        # Position the bullet
246        bullet.center_x = self.player_sprite.center_x
247        bullet.bottom = self.player_sprite.top
248
249        # Add the bullet to the appropriate lists
250        self.bullet_list.append(bullet)
251
252    def on_key_press(self, symbol: int, modifiers: int):
253        if symbol == arcade.key.R:
254            self.reset()
255        # Close the window
256        elif symbol == arcade.key.ESCAPE:
257            self.window.close()
258
259    def on_update(self, delta_time):
260        """ Movement and game logic """
261
262        # Call update on bullet sprites
263        self.bullet_list.update(delta_time)
264        self.explosions_list.update(delta_time)
265
266        # Loop through each bullet
267        for bullet in self.bullet_list:
268
269            # Check this bullet to see if it hit a coin
270            hit_list = arcade.check_for_collision_with_list(bullet, self.enemy_list)
271
272            # If it did...
273            if len(hit_list) > 0:
274
275                # Get rid of the bullet
276                bullet.remove_from_sprite_lists()
277
278            # For every coin we hit, add to the score and remove the coin
279            for coin in hit_list:
280                # Make an explosion
281                for i in range(PARTICLE_COUNT):
282                    particle = Particle()
283                    particle.position = coin.position
284                    self.explosions_list.append(particle)
285
286                smoke = Smoke(50)
287                smoke.position = coin.position
288                self.explosions_list.append(smoke)
289
290                coin.remove_from_sprite_lists()
291                self.score += 1
292
293                # Hit Sound
294                arcade.sound.play_sound(self.hit_sound)
295
296            # If the bullet flies off-screen, remove it.
297            if bullet.bottom > WINDOW_HEIGHT:
298                bullet.remove_from_sprite_lists()
299
300
301def main():
302    """ Main function """
303    # Create a window class. This is what actually shows up on screen
304    window = arcade.Window(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE)
305
306    # Create the GameView
307    game = GameView()
308
309    # Show GameView on screen
310    window.show_view(game)
311
312    # Start the arcade game loop
313    arcade.run()
314
315
316if __name__ == "__main__":
317    main()