之前看到過很多人寫的飛機大戰,當然了之前我也寫過多個版本,總體來說功能是實現了,但總感覺不夠“炫”
今天瀏覽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/197.html
3. 切換到安裝有pygame模組的python虛擬環境(如果沒有pygame可以pip install pygame安裝)
4. 使用命令執行 python3 xxxx.py
別忘了點贊哦,謝謝大家支援。如果想要做更多的pygame相關的程式,請發私信或者留言 1小時必回覆