黏液入侵者#

slime_invaders.py#
1"""
2Slime Invaders
3
4Artwork from https://kenney.nl
5
6This example shows how to:
7
8* Get sprites to move as a group
9* Change texture of sprites as a group
10* Only have the bottom sprite in the group fire lasers
11* Create 'shields' like in space invaders
12
13If Python and Arcade are installed, this example can be run from the command line with:
14python -m arcade.examples.slime_invaders
15"""
16import random
17import arcade
18
19SPRITE_SCALING_PLAYER = 0.5
20SPRITE_SCALING_enemy = 0.5
21SPRITE_SCALING_LASER = 0.8
22
23SCREEN_WIDTH = 800
24SCREEN_HEIGHT = 600
25SCREEN_TITLE = "Slime Invaders"
26
27BULLET_SPEED = 5
28ENEMY_SPEED = 2
29
30MAX_PLAYER_BULLETS = 3
31
32# This margin controls how close the enemy gets to the left or right side
33# before reversing direction.
34ENEMY_VERTICAL_MARGIN = 15
35RIGHT_ENEMY_BORDER = SCREEN_WIDTH - ENEMY_VERTICAL_MARGIN
36LEFT_ENEMY_BORDER = ENEMY_VERTICAL_MARGIN
37
38# How many pixels to move the enemy down when reversing
39ENEMY_MOVE_DOWN_AMOUNT = 30
40
41# Game state
42GAME_OVER = 1
43PLAY_GAME = 0
44
45
46class MyGame(arcade.Window):
47 """ Main application class. """
48
49 def __init__(self):
50 """ Initializer """
51 # Call the parent class initializer
52 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
53
54 # Variables that will hold sprite lists
55 self.player_list = None
56 self.enemy_list = None
57 self.player_bullet_list = None
58 self.enemy_bullet_list = None
59 self.shield_list = None
60
61 # Textures for the enemy
62 self.enemy_textures = None
63
64 # State of the game
65 self.game_state = PLAY_GAME
66
67 # Set up the player info
68 self.player_sprite = None
69 self.score = 0
70
71 # Enemy movement
72 self.enemy_change_x = -ENEMY_SPEED
73
74 # Don't show the mouse cursor
75 self.set_mouse_visible(False)
76
77 # Load sounds. Sounds from kenney.nl
78 self.gun_sound = arcade.load_sound(":resources:sounds/hurt5.wav")
79 self.hit_sound = arcade.load_sound(":resources:sounds/hit5.wav")
80
81 self.background_color = arcade.color.AMAZON
82
83 # arcade.configure_logging()
84
85 def setup_level_one(self):
86 # Load the textures for the enemies, one facing left, one right
87 self.enemy_textures = arcade.load_texture_pair(":resources:images/enemies/slimeBlue.png")
88
89 # Create rows and columns of enemies
90 x_count = 7
91 x_start = 380
92 x_spacing = 60
93 y_count = 5
94 y_start = 420
95 y_spacing = 40
96 for x in range(x_start, x_spacing * x_count + x_start, x_spacing):
97 for y in range(y_start, y_spacing * y_count + y_start, y_spacing):
98 # Create the enemy instance
99 # enemy image from kenney.nl
100 enemy = arcade.Sprite(
101 self.enemy_textures[1],
102 scale=SPRITE_SCALING_enemy,
103 center_x=x,
104 center_y=y
105 )
106 # Add the enemy to the lists
107 self.enemy_list.append(enemy)
108
109 def make_shield(self, x_start):
110 """
111 Make a shield, which is just a 2D grid of solid color sprites
112 stuck together with no margin so you can't tell them apart.
113 """
114 shield_block_width = 5
115 shield_block_height = 10
116 shield_width_count = 20
117 shield_height_count = 5
118 y_start = 150
119 for x in range(x_start,
120 x_start + shield_width_count * shield_block_width,
121 shield_block_width):
122 for y in range(y_start,
123 y_start + shield_height_count * shield_block_height,
124 shield_block_height):
125 shield_sprite = arcade.SpriteSolidColor(shield_block_width,
126 shield_block_height,
127 color=arcade.color.WHITE)
128 shield_sprite.center_x = x
129 shield_sprite.center_y = y
130 self.shield_list.append(shield_sprite)
131
132 def setup(self):
133 """
134 Set up the game and initialize the variables.
135 Call this method if you implement a 'play again' feature.
136 """
137
138 self.game_state = PLAY_GAME
139
140 # Sprite lists
141 self.player_list = arcade.SpriteList()
142 self.enemy_list = arcade.SpriteList()
143 self.player_bullet_list = arcade.SpriteList()
144 self.enemy_bullet_list = arcade.SpriteList()
145 self.shield_list = arcade.SpriteList()
146
147 # Set up the player
148 self.score = 0
149
150 # Image from kenney.nl
151 self.player_sprite = arcade.Sprite(
152 ":resources:images/animated_characters/female_person/femalePerson_idle.png",
153 scale=SPRITE_SCALING_PLAYER)
154 self.player_sprite.center_x = 50
155 self.player_sprite.center_y = 40
156 self.player_list.append(self.player_sprite)
157
158 # Make each of the shields
159 for x in range(75, 800, 190):
160 self.make_shield(x)
161
162 # Set the background color
163 self.background_color = arcade.color.AMAZON
164
165 self.setup_level_one()
166
167 def on_draw(self):
168 """ Render the screen. """
169
170 # This command has to happen before we start drawing
171 self.clear()
172
173 # Draw all the sprites.
174 self.enemy_list.draw()
175 self.player_bullet_list.draw()
176 self.enemy_bullet_list.draw()
177 self.shield_list.draw()
178 self.player_list.draw()
179
180 # Render the text
181 arcade.draw_text(f"Score: {self.score}", 10, 20, arcade.color.WHITE, 14)
182
183 # Draw game over if the game state is such
184 if self.game_state == GAME_OVER:
185 arcade.draw_text("GAME OVER", 250, 300, arcade.color.WHITE, 55)
186 self.set_mouse_visible(True)
187
188 def on_mouse_motion(self, x, y, dx, dy):
189 """
190 Called whenever the mouse moves.
191 """
192
193 # Don't move the player if the game is over
194 if self.game_state == GAME_OVER:
195 return
196
197 self.player_sprite.center_x = x
198
199 def on_mouse_press(self, x, y, button, modifiers):
200 """
201 Called whenever the mouse button is clicked.
202 """
203
204 # Only allow the user so many bullets on screen at a time to prevent
205 # them from spamming bullets.
206 if len(self.player_bullet_list) < MAX_PLAYER_BULLETS:
207
208 # Gunshot sound
209 arcade.play_sound(self.gun_sound)
210
211 # Create a bullet
212 bullet = arcade.Sprite(":resources:images/space_shooter/laserBlue01.png", scale=SPRITE_SCALING_LASER)
213
214 # The image points to the right, and we want it to point up. So
215 # rotate it.
216 bullet.angle = 90
217
218 # Give the bullet a speed
219 bullet.change_y = BULLET_SPEED
220
221 # Position the bullet
222 bullet.center_x = self.player_sprite.center_x
223 bullet.bottom = self.player_sprite.top
224
225 # Add the bullet to the appropriate lists
226 self.player_bullet_list.append(bullet)
227
228 def update_enemies(self):
229
230 # Move the enemy vertically
231 for enemy in self.enemy_list:
232 enemy.center_x += self.enemy_change_x
233
234 # Check every enemy to see if any hit the edge. If so, reverse the
235 # direction and flag to move down.
236 move_down = False
237 for enemy in self.enemy_list:
238 if enemy.right > RIGHT_ENEMY_BORDER and self.enemy_change_x > 0:
239 self.enemy_change_x *= -1
240 move_down = True
241 if enemy.left < LEFT_ENEMY_BORDER and self.enemy_change_x < 0:
242 self.enemy_change_x *= -1
243 move_down = True
244
245 # Did we hit the edge above, and need to move t he enemy down?
246 if move_down:
247 # Yes
248 for enemy in self.enemy_list:
249 # Move enemy down
250 enemy.center_y -= ENEMY_MOVE_DOWN_AMOUNT
251 # Flip texture on enemy so it faces the other way
252 if self.enemy_change_x > 0:
253 enemy.texture = self.enemy_textures[0]
254 else:
255 enemy.texture = self.enemy_textures[1]
256
257 def allow_enemies_to_fire(self):
258 """
259 See if any enemies will fire this frame.
260 """
261 # Track which x values have had a chance to fire a bullet.
262 # Since enemy list is build from the bottom up, we can use
263 # this to only allow the bottom row to fire.
264 x_spawn = []
265 for enemy in self.enemy_list:
266 # Adjust the chance depending on the number of enemies. Fewer
267 # enemies, more likely to fire.
268 chance = 4 + len(self.enemy_list) * 4
269
270 # Fire if we roll a zero, and no one else in this column has had
271 # a chance to fire.
272 if random.randrange(chance) == 0 and enemy.center_x not in x_spawn:
273 # Create a bullet
274 bullet = arcade.Sprite(":resources:images/space_shooter/laserRed01.png", scale=SPRITE_SCALING_LASER)
275
276 # Angle down.
277 bullet.angle = 180
278
279 # Give the bullet a speed
280 bullet.change_y = -BULLET_SPEED
281
282 # Position the bullet so its top id right below the enemy
283 bullet.center_x = enemy.center_x
284 bullet.top = enemy.bottom
285
286 # Add the bullet to the appropriate list
287 self.enemy_bullet_list.append(bullet)
288
289 # Ok, this column has had a chance to fire. Add to list so we don't
290 # try it again this frame.
291 x_spawn.append(enemy.center_x)
292
293 def process_enemy_bullets(self):
294
295 # Move the bullets
296 self.enemy_bullet_list.update()
297
298 # Loop through each bullet
299 for bullet in self.enemy_bullet_list:
300 # Check this bullet to see if it hit a shield
301 hit_list = arcade.check_for_collision_with_list(bullet, self.shield_list)
302
303 # If it did, get rid of the bullet and shield blocks
304 if len(hit_list) > 0:
305 bullet.remove_from_sprite_lists()
306 for shield in hit_list:
307 shield.remove_from_sprite_lists()
308 continue
309
310 # See if the player got hit with a bullet
311 if arcade.check_for_collision_with_list(self.player_sprite, self.enemy_bullet_list):
312 self.game_state = GAME_OVER
313
314 # If the bullet falls off the screen get rid of it
315 if bullet.top < 0:
316 bullet.remove_from_sprite_lists()
317
318 def process_player_bullets(self):
319
320 # Move the bullets
321 self.player_bullet_list.update()
322
323 # Loop through each bullet
324 for bullet in self.player_bullet_list:
325
326 # Check this bullet to see if it hit a enemy
327 hit_list = arcade.check_for_collision_with_list(bullet, self.shield_list)
328 # If it did, get rid of the bullet
329 if len(hit_list) > 0:
330 bullet.remove_from_sprite_lists()
331 for shield in hit_list:
332 shield.remove_from_sprite_lists()
333 continue
334
335 # Check this bullet to see if it hit a enemy
336 hit_list = arcade.check_for_collision_with_list(bullet, self.enemy_list)
337
338 # If it did, get rid of the bullet
339 if len(hit_list) > 0:
340 bullet.remove_from_sprite_lists()
341
342 # For every enemy we hit, add to the score and remove the enemy
343 for enemy in hit_list:
344 enemy.remove_from_sprite_lists()
345 self.score += 1
346
347 # Hit Sound
348 arcade.play_sound(self.hit_sound)
349
350 # If the bullet flies off-screen, remove it.
351 if bullet.bottom > SCREEN_HEIGHT:
352 bullet.remove_from_sprite_lists()
353
354 def on_update(self, delta_time):
355 """ Movement and game logic """
356
357 if self.game_state == GAME_OVER:
358 return
359
360 self.update_enemies()
361 self.allow_enemies_to_fire()
362 self.process_enemy_bullets()
363 self.process_player_bullets()
364
365 if len(self.enemy_list) == 0:
366 self.setup_level_one()
367
368
369def main():
370 window = MyGame()
371 window.setup()
372 arcade.run()
373
374
375if __name__ == "__main__":
376 main()