Python3+Pygame实现的射击游戏,很流畅,有音效

之前看到过很多人写的飞机大战,当然了之前我也写过多个版本,总体来说功能是实现了,但总感觉不够“炫”

今天浏览Python资料的时候,意外发现了这个很好的“射击”类游戏,看上去类似飞机大战,但更好玩

一、游戏特点

1. 运行非常流畅

2. 默认有3条命,每条命的HP可以增加(吃补品)也可以减少(被击中)

3. 有碰撞时的音效

4. 有碰撞时的爆炸效果

 

二、运行效果展示

射击游戏

 

三、完整代码

  1 from __future__ import division
  2 import pygame
  3 import random
  4 from os import path
  5 
  6 ## assets folder
  7 img_dir = path.join(path.dirname(__file__), 'assets')
  8 sound_folder = path.join(path.dirname(__file__), 'sounds')
  9 
 10 ###############################
 11 ## to be placed in "constant.py" later
 12 WIDTH = 480
 13 HEIGHT = 600
 14 FPS = 60
 15 POWERUP_TIME = 5000
 16 BAR_LENGTH = 100
 17 BAR_HEIGHT = 10
 18 
 19 # Define Colors 
 20 WHITE = (255, 255, 255)
 21 BLACK = (0, 0, 0)
 22 RED = (255, 0, 0)
 23 GREEN = (0, 255, 0)
 24 BLUE = (0, 0, 255)
 25 YELLOW = (255, 255, 0)
 26 ###############################
 27 
 28 ###############################
 29 ## to placed in "__init__.py" later
 30 ## initialize pygame and create window
 31 pygame.init()
 32 pygame.mixer.init()  ## For sound
 33 screen = pygame.display.set_mode((WIDTH, HEIGHT))
 34 pygame.display.set_caption("Space Shooter")
 35 clock = pygame.time.Clock()     ## For syncing the FPS
 36 ###############################
 37 
 38 font_name = pygame.font.match_font('arial')
 39 
 40 def main_menu():
 41     global screen
 42 
 43     menu_song = pygame.mixer.music.load(path.join(sound_folder, "menu.ogg"))
 44     pygame.mixer.music.play(-1)
 45 
 46     title = pygame.image.load(path.join(img_dir, "main.png")).convert()
 47     title = pygame.transform.scale(title, (WIDTH, HEIGHT), screen)
 48 
 49     screen.blit(title, (0,0))
 50     pygame.display.update()
 51 
 52     while True:
 53         ev = pygame.event.poll()
 54         if ev.type == pygame.KEYDOWN:
 55             if ev.key == pygame.K_RETURN:
 56                 break
 57             elif ev.key == pygame.K_q:
 58                 pygame.quit()
 59                 quit()
 60         else:
 61             draw_text(screen, "Press [ENTER] To Begin", 30, WIDTH/2, HEIGHT/2)
 62             draw_text(screen, "or [Q] To Quit", 30, WIDTH/2, (HEIGHT/2)+40)
 63             pygame.display.update()
 64 
 65     #pygame.mixer.music.stop()
 66     ready = pygame.mixer.Sound(path.join(sound_folder,'getready.ogg'))
 67     ready.play()
 68     screen.fill(BLACK)
 69     draw_text(screen, "GET READY!", 40, WIDTH/2, HEIGHT/2)
 70     pygame.display.update()
 71     
 72 
 73 def draw_text(surf, text, size, x, y):
 74     ## selecting a cross platform font to display the score
 75     font = pygame.font.Font(font_name, size)
 76     text_surface = font.render(text, True, WHITE)       ## True denotes the font to be anti-aliased 
 77     text_rect = text_surface.get_rect()
 78     text_rect.midtop = (x, y)
 79     surf.blit(text_surface, text_rect)
 80 
 81 
 82 def draw_shield_bar(surf, x, y, pct):
 83     # if pct < 0:
 84     #     pct = 0
 85     pct = max(pct, 0) 
 86     ## moving them to top
 87     # BAR_LENGTH = 100
 88     # BAR_HEIGHT = 10
 89     fill = (pct / 100) * BAR_LENGTH
 90     outline_rect = pygame.Rect(x, y, BAR_LENGTH, BAR_HEIGHT)
 91     fill_rect = pygame.Rect(x, y, fill, BAR_HEIGHT)
 92     pygame.draw.rect(surf, GREEN, fill_rect)
 93     pygame.draw.rect(surf, WHITE, outline_rect, 2)
 94 
 95 
 96 def draw_lives(surf, x, y, lives, img):
 97     for i in range(lives):
 98         img_rect= img.get_rect()
 99         img_rect.x = x + 30 * i
100         img_rect.y = y
101         surf.blit(img, img_rect)
102 
103 
104 
105 def newmob():
106     mob_element = Mob()
107     all_sprites.add(mob_element)
108     mobs.add(mob_element)
109 
110 class Explosion(pygame.sprite.Sprite):
111     def __init__(self, center, size):
112         pygame.sprite.Sprite.__init__(self)
113         self.size = size
114         self.image = explosion_anim[self.size][0]
115         self.rect = self.image.get_rect()
116         self.rect.center = center
117         self.frame = 0 
118         self.last_update = pygame.time.get_ticks()
119         self.frame_rate = 75
120 
121     def update(self):
122         now = pygame.time.get_ticks()
123         if now - self.last_update > self.frame_rate:
124             self.last_update = now
125             self.frame += 1
126             if self.frame == len(explosion_anim[self.size]):
127                 self.kill()
128             else:
129                 center = self.rect.center
130                 self.image = explosion_anim[self.size][self.frame]
131                 self.rect = self.image.get_rect()
132                 self.rect.center = center
133 
134 
135 class Player(pygame.sprite.Sprite):
136     def __init__(self):
137         pygame.sprite.Sprite.__init__(self)
138         ## scale the player img down
139         self.image = pygame.transform.scale(player_img, (50, 38))
140         self.image.set_colorkey(BLACK)
141         self.rect = self.image.get_rect()
142         self.radius = 20
143         self.rect.centerx = WIDTH / 2
144         self.rect.bottom = HEIGHT - 10
145         self.speedx = 0 
146         self.shield = 100
147         self.shoot_delay = 250
148         self.last_shot = pygame.time.get_ticks()
149         self.lives = 3
150         self.hidden = False
151         self.hide_timer = pygame.time.get_ticks()
152         self.power = 1
153         self.power_timer = pygame.time.get_ticks()
154 
155     def update(self):
156         ## time out for powerups
157         if self.power >=2 and pygame.time.get_ticks() - self.power_time > POWERUP_TIME:
158             self.power -= 1
159             self.power_time = pygame.time.get_ticks()
160 
161         ## unhide 
162         if self.hidden and pygame.time.get_ticks() - self.hide_timer > 1000:
163             self.hidden = False
164             self.rect.centerx = WIDTH / 2
165             self.rect.bottom = HEIGHT - 30
166 
167         self.speedx = 0     ## makes the player static in the screen by default. 
168         # then we have to check whether there is an event hanlding being done for the arrow keys being 
169         ## pressed 
170 
171         ## will give back a list of the keys which happen to be pressed down at that moment
172         keystate = pygame.key.get_pressed()     
173         if keystate[pygame.K_LEFT]:
174             self.speedx = -5
175         elif keystate[pygame.K_RIGHT]:
176             self.speedx = 5
177 
178         #Fire weapons by holding spacebar
179         if keystate[pygame.K_SPACE]:
180             self.shoot()
181 
182         ## check for the borders at the left and right
183         if self.rect.right > WIDTH:
184             self.rect.right = WIDTH
185         if self.rect.left < 0:
186             self.rect.left = 0
187 
188         self.rect.x += self.speedx
189 
190     def shoot(self):
191         ## to tell the bullet where to spawn
192         now = pygame.time.get_ticks()
193         if now - self.last_shot > self.shoot_delay:
194             self.last_shot = now
195             if self.power == 1:
196                 bullet = Bullet(self.rect.centerx, self.rect.top)
197                 all_sprites.add(bullet)
198                 bullets.add(bullet)
199                 shooting_sound.play()
200             if self.power == 2:
201                 bullet1 = Bullet(self.rect.left, self.rect.centery)
202                 bullet2 = Bullet(self.rect.right, self.rect.centery)
203                 all_sprites.add(bullet1)
204                 all_sprites.add(bullet2)
205                 bullets.add(bullet1)
206                 bullets.add(bullet2)
207                 shooting_sound.play()
208 
209             """ MOAR POWAH """
210             if self.power >= 3:
211                 bullet1 = Bullet(self.rect.left, self.rect.centery)
212                 bullet2 = Bullet(self.rect.right, self.rect.centery)
213                 missile1 = Missile(self.rect.centerx, self.rect.top) # Missile shoots from center of ship
214                 all_sprites.add(bullet1)
215                 all_sprites.add(bullet2)
216                 all_sprites.add(missile1)
217                 bullets.add(bullet1)
218                 bullets.add(bullet2)
219                 bullets.add(missile1)
220                 shooting_sound.play()
221                 missile_sound.play()
222 
223     def powerup(self):
224         self.power += 1
225         self.power_time = pygame.time.get_ticks()
226 
227     def hide(self):
228         self.hidden = True
229         self.hide_timer = pygame.time.get_ticks()
230         self.rect.center = (WIDTH / 2, HEIGHT + 200)
231 
232 
233 # defines the enemies
234 class Mob(pygame.sprite.Sprite):
235     def __init__(self):
236         pygame.sprite.Sprite.__init__(self)
237         self.image_orig = random.choice(meteor_images)
238         self.image_orig.set_colorkey(BLACK)
239         self.image = self.image_orig.copy()
240         self.rect = self.image.get_rect()
241         self.radius = int(self.rect.width *.90 / 2)
242         self.rect.x = random.randrange(0, WIDTH - self.rect.width)
243         self.rect.y = random.randrange(-150, -100)
244         self.speedy = random.randrange(5, 20)        ## for randomizing the speed of the Mob
245 
246         ## randomize the movements a little more 
247         self.speedx = random.randrange(-3, 3)
248 
249         ## adding rotation to the mob element
250         self.rotation = 0
251         self.rotation_speed = random.randrange(-8, 8)
252         self.last_update = pygame.time.get_ticks()  ## time when the rotation has to happen
253         
254     def rotate(self):
255         time_now = pygame.time.get_ticks()
256         if time_now - self.last_update > 50: # in milliseconds
257             self.last_update = time_now
258             self.rotation = (self.rotation + self.rotation_speed) % 360 
259             new_image = pygame.transform.rotate(self.image_orig, self.rotation)
260             old_center = self.rect.center
261             self.image = new_image
262             self.rect = self.image.get_rect()
263             self.rect.center = old_center
264 
265     def update(self):
266         self.rotate()
267         self.rect.x += self.speedx
268         self.rect.y += self.speedy
269         ## now what if the mob element goes out of the screen
270 
271         if (self.rect.top > HEIGHT + 10) or (self.rect.left < -25) or (self.rect.right > WIDTH + 20):
272             self.rect.x = random.randrange(0, WIDTH - self.rect.width)
273             self.rect.y = random.randrange(-100, -40)
274             self.speedy = random.randrange(1, 8)        ## for randomizing the speed of the Mob
275 
276 ## defines the sprite for Powerups
277 class Pow(pygame.sprite.Sprite):
278     def __init__(self, center):
279         pygame.sprite.Sprite.__init__(self)
280         self.type = random.choice(['shield', 'gun'])
281         self.image = powerup_images[self.type]
282         self.image.set_colorkey(BLACK)
283         self.rect = self.image.get_rect()
284         ## place the bullet according to the current position of the player
285         self.rect.center = center
286         self.speedy = 2
287 
288     def update(self):
289         """should spawn right in front of the player"""
290         self.rect.y += self.speedy
291         ## kill the sprite after it moves over the top border
292         if self.rect.top > HEIGHT:
293             self.kill()
294 
295             
296 
297 ## defines the sprite for bullets
298 class Bullet(pygame.sprite.Sprite):
299     def __init__(self, x, y):
300         pygame.sprite.Sprite.__init__(self)
301         self.image = bullet_img
302         self.image.set_colorkey(BLACK)
303         self.rect = self.image.get_rect()
304         ## place the bullet according to the current position of the player
305         self.rect.bottom = y 
306         self.rect.centerx = x
307         self.speedy = -10
308 
309     def update(self):
310         """should spawn right in front of the player"""
311         self.rect.y += self.speedy
312         ## kill the sprite after it moves over the top border
313         if self.rect.bottom < 0:
314             self.kill()
315 
316         ## now we need a way to shoot
317         ## lets bind it to "spacebar".
318         ## adding an event for it in Game loop
319 
320 ## FIRE ZE MISSILES
321 class Missile(pygame.sprite.Sprite):
322     def __init__(self, x, y):
323         pygame.sprite.Sprite.__init__(self)
324         self.image = missile_img
325         self.image.set_colorkey(BLACK)
326         self.rect = self.image.get_rect()
327         self.rect.bottom = y
328         self.rect.centerx = x
329         self.speedy = -10
330 
331     def update(self):
332         """should spawn right in front of the player"""
333         self.rect.y += self.speedy
334         if self.rect.bottom < 0:
335             self.kill()
336 
337 
338 ###################################################
339 ## Load all game images
340 
341 background = pygame.image.load(path.join(img_dir, 'starfield.png')).convert()
342 background_rect = background.get_rect()
343 ## ^^ draw this rect first 
344 
345 player_img = pygame.image.load(path.join(img_dir, 'playerShip1_orange.png')).convert()
346 player_mini_img = pygame.transform.scale(player_img, (25, 19))
347 player_mini_img.set_colorkey(BLACK)
348 bullet_img = pygame.image.load(path.join(img_dir, 'laserRed16.png')).convert()
349 missile_img = pygame.image.load(path.join(img_dir, 'missile.png')).convert_alpha()
350 # meteor_img = pygame.image.load(path.join(img_dir, 'meteorBrown_med1.png')).convert()
351 meteor_images = []
352 meteor_list = [
353     'meteorBrown_big1.png',
354     'meteorBrown_big2.png', 
355     'meteorBrown_med1.png', 
356     'meteorBrown_med3.png',
357     'meteorBrown_small1.png',
358     'meteorBrown_small2.png',
359     'meteorBrown_tiny1.png'
360 ]
361 
362 for image in meteor_list:
363     meteor_images.append(pygame.image.load(path.join(img_dir, image)).convert())
364 
365 ## meteor explosion
366 explosion_anim = {}
367 explosion_anim['lg'] = []
368 explosion_anim['sm'] = []
369 explosion_anim['player'] = []
370 for i in range(9):
371     filename = 'regularExplosion0{}.png'.format(i)
372     img = pygame.image.load(path.join(img_dir, filename)).convert()
373     img.set_colorkey(BLACK)
374     ## resize the explosion
375     img_lg = pygame.transform.scale(img, (75, 75))
376     explosion_anim['lg'].append(img_lg)
377     img_sm = pygame.transform.scale(img, (32, 32))
378     explosion_anim['sm'].append(img_sm)
379 
380     ## player explosion
381     filename = 'sonicExplosion0{}.png'.format(i)
382     img = pygame.image.load(path.join(img_dir, filename)).convert()
383     img.set_colorkey(BLACK)
384     explosion_anim['player'].append(img)
385 
386 ## load power ups
387 powerup_images = {}
388 powerup_images['shield'] = pygame.image.load(path.join(img_dir, 'shield_gold.png')).convert()
389 powerup_images['gun'] = pygame.image.load(path.join(img_dir, 'bolt_gold.png')).convert()
390 
391 
392 ###################################################
393 
394 
395 ###################################################
396 ### Load all game sounds
397 shooting_sound = pygame.mixer.Sound(path.join(sound_folder, 'pew.wav'))
398 missile_sound = pygame.mixer.Sound(path.join(sound_folder, 'rocket.ogg'))
399 expl_sounds = []
400 for sound in ['expl3.wav', 'expl6.wav']:
401     expl_sounds.append(pygame.mixer.Sound(path.join(sound_folder, sound)))
402 ## main background music
403 #pygame.mixer.music.load(path.join(sound_folder, 'tgfcoder-FrozenJam-SeamlessLoop.ogg'))
404 pygame.mixer.music.set_volume(0.2)      ## simmered the sound down a little
405 
406 player_die_sound = pygame.mixer.Sound(path.join(sound_folder, 'rumble1.ogg'))
407 ###################################################
408 
409 ## group all the sprites together for ease of update
410 all_sprites = pygame.sprite.Group()
411 player = Player()
412 all_sprites.add(player)
413 
414 ## spawn a group of mob
415 mobs = pygame.sprite.Group()
416 for i in range(8):      ## 8 mobs
417     # mob_element = Mob()
418     # all_sprites.add(mob_element)
419     # mobs.add(mob_element)
420     newmob()
421 
422 ## group for bullets
423 bullets = pygame.sprite.Group()
424 powerups = pygame.sprite.Group()
425 
426 #### Score board variable
427 score = 0
428 
429 ## TODO: make the game music loop over again and again. play(loops=-1) is not working
430 # Error : 
431 # TypeError: play() takes no keyword arguments
432 #pygame.mixer.music.play()
433 
434 #############################
435 ## Game loop
436 running = True
437 menu_display = True
438 while running:
439     if menu_display:
440         main_menu()
441         pygame.time.wait(3000)
442 
443         #Stop menu music
444         pygame.mixer.music.stop()
445         #Play the gameplay music
446         pygame.mixer.music.load(path.join(sound_folder, 'tgfcoder-FrozenJam-SeamlessLoop.ogg'))
447         pygame.mixer.music.play(-1)     ## makes the gameplay sound in an endless loop
448         
449         menu_display = False
450         
451     #1 Process input/events
452     clock.tick(FPS)     ## will make the loop run at the same speed all the time
453     for event in pygame.event.get():        # gets all the events which have occured till now and keeps tab of them.
454         ## listening for the the X button at the top
455         if event.type == pygame.QUIT:
456             running = False
457 
458         ## Press ESC to exit game
459         if event.type == pygame.KEYDOWN:
460             if event.key == pygame.K_ESCAPE:
461                 running = False
462         # ## event for shooting the bullets
463         # elif event.type == pygame.KEYDOWN:
464         #     if event.key == pygame.K_SPACE:
465         #         player.shoot()      ## we have to define the shoot()  function
466 
467     #2 Update
468     all_sprites.update()
469 
470 
471     ## check if a bullet hit a mob
472     ## now we have a group of bullets and a group of mob
473     hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
474     ## now as we delete the mob element when we hit one with a bullet, we need to respawn them again
475     ## as there will be no mob_elements left out 
476     for hit in hits:
477         score += 50 - hit.radius         ## give different scores for hitting big and small metoers
478         random.choice(expl_sounds).play()
479         # m = Mob()
480         # all_sprites.add(m)
481         # mobs.add(m)
482         expl = Explosion(hit.rect.center, 'lg')
483         all_sprites.add(expl)
484         if random.random() > 0.9:
485             pow = Pow(hit.rect.center)
486             all_sprites.add(pow)
487             powerups.add(pow)
488         newmob()        ## spawn a new mob
489 
490     ## ^^ the above loop will create the amount of mob objects which were killed spawn again
491     #########################
492 
493     ## check if the player collides with the mob
494     hits = pygame.sprite.spritecollide(player, mobs, True, pygame.sprite.collide_circle)        ## gives back a list, True makes the mob element disappear
495     for hit in hits:
496         player.shield -= hit.radius * 2
497         expl = Explosion(hit.rect.center, 'sm')
498         all_sprites.add(expl)
499         newmob()
500         if player.shield <= 0: 
501             player_die_sound.play()
502             death_explosion = Explosion(player.rect.center, 'player')
503             all_sprites.add(death_explosion)
504             # running = False     ## GAME OVER 3:D
505             player.hide()
506             player.lives -= 1
507             player.shield = 100
508 
509     ## if the player hit a power up
510     hits = pygame.sprite.spritecollide(player, powerups, True)
511     for hit in hits:
512         if hit.type == 'shield':
513             player.shield += random.randrange(10, 30)
514             if player.shield >= 100:
515                 player.shield = 100
516         if hit.type == 'gun':
517             player.powerup()
518 
519     ## if player died and the explosion has finished, end game
520     if player.lives == 0 and not death_explosion.alive():
521         running = False
522         # menu_display = True
523         # pygame.display.update()
524 
525     #3 Draw/render
526     screen.fill(BLACK)
527     ## draw the stargaze.png image
528     screen.blit(background, background_rect)
529 
530     all_sprites.draw(screen)
531     draw_text(screen, str(score), 18, WIDTH / 2, 10)     ## 10px down from the screen
532     draw_shield_bar(screen, 5, 5, player.shield)
533 
534     # Draw lives
535     draw_lives(screen, WIDTH - 100, 5, player.lives, player_mini_img)
536 
537     ## Done after drawing everything to the screen
538     pygame.display.flip()       
539 
540 pygame.quit()

 

四、运行方式

如果想要运行本程序,流程如下

1. 下载上述代码 例如存储为xxxx.py

2. 下载素材(图片、声音等)https://www.itprojects.cn/web/material/details.html?id=90

3. 切换到安装有pygame模块的python虚拟环境(如果没有pygame可以pip install pygame安装)

4. 使用命令运行 python3 xxxx.py

 

别忘了点赞哦,谢谢大家支持。如果想要做更多的pygame相关的程序,请发私信或者留言  1小时必回复

 

posted @ 2021-03-17 19:48  dong4716138  阅读(725)  评论(2编辑  收藏  举报