Sprite Explosions Particles

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()