Python實現飛機大戰遊戲

最近開始用Python製作遊戲,真心覺得開發遊戲比玩遊戲有趣多了,雖然Python的主業不是可視化和遊戲製作,但通過遊戲開發的經歷來檢驗學習的效果是很棒的體驗。

一、遊戲效果

在這裏插入圖片描述
在這裏插入圖片描述
遊戲是有音效的,在這裏沒法給大家展示了

二、遊戲代碼

#導入模塊
import pygame
import random
from pygame.locals import *
from os import path

#######################################基本參數配置#######################################

#獲取圖片庫和聲音庫路徑
img_dir = path.join(path.dirname(__file__),'pic')
sound_folder = path.join(path.dirname(__file__),'sounds')

#定義遊戲窗口、玩家血量條尺寸,遊戲運行速度、炮火持續時間等參數
WIDTH = 480
HEIGHT = 600
FPS = 60
POWERUP_TIME = 5000
BAR_LENGTH = 100
BAR_HEIGHT = 10

#定義白、黑、紅、綠、藍、黃的RGB參數 
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)

#初始化pygame模塊,創建遊戲窗口、遊戲窗口命名、創建跟蹤時間對象
pygame.init()
pygame.mixer.init()  #初始化音效
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("Aircraft Battle")
clock = pygame.time.Clock()

#定義文本字體
font_name = pygame.font.match_font('arial')

#######################################加載圖片#######################################

#加載遊戲進行中背景圖片
background = pygame.image.load(path.join(img_dir,'starfield.png')).convert()
background = pygame.transform.scale(background,(WIDTH,1536))
height = -936

#加載玩家圖片
player_img = pygame.image.load(path.join(img_dir,'player.png')).convert()
player_mini_img = pygame.transform.scale(player_img,(25, 19))
player_mini_img.set_colorkey(BLACK)

#加載玩家炮彈、導彈圖片
bullet_img = pygame.image.load(path.join(img_dir,'bullet.png')).convert()
missile_img = pygame.image.load(path.join(img_dir,'missile.png')).convert_alpha()

#加載敵機炮彈圖片
enemies_bullet_img = pygame.image.load(path.join(img_dir,'enemies_bullet.png')).convert()

#加載盾牌、閃電圖片
powerup_images = {}
powerup_images['shield'] = pygame.image.load(path.join(img_dir, 'shield.png')).convert()
powerup_images['gun'] = pygame.image.load(path.join(img_dir, 'bolt.png')).convert()

#加載敵機和火山石圖片
enemies_images = []
lava_images = []
#敵機
enemies_list = [
    'enemies1.png',
    'enemies2.png',
    'enemies3.png'
]
#火山石
lava_list = [
    'lava_med.png',
    'lava_small1.png',
    'lava_small2.png',
    'lava_tiny.png'
]
for image in enemies_list:
    enemies_img = pygame.image.load(path.join(img_dir,image)).convert()
    enemies_img = pygame.transform.scale(enemies_img,(80, 60))
    enemies_images.append(enemies_img)
for image in lava_list:
    lava_images.append(pygame.image.load(path.join(img_dir,image)).convert())

#加載爆炸圖片
explosion_anim = {}
explosion_anim['lg'] = []
explosion_anim['sm'] = []
explosion_anim['player'] = []
for i in range(9):
    #敵機、火山石爆炸
    filename = 'regularExplosion0{}.png'.format(i)
    img = pygame.image.load(path.join(img_dir,filename)).convert()
    img.set_colorkey(BLACK)
    #大爆炸    
    img_lg = pygame.transform.scale(img,(75,75))
    explosion_anim['lg'].append(img_lg)
    #小爆炸
    img_sm = pygame.transform.scale(img,(32,32))
    explosion_anim['sm'].append(img_sm)
    #玩家爆炸
    filename = 'sonicExplosion0{}.png'.format(i)
    img = pygame.image.load(path.join(img_dir,filename)).convert()
    img.set_colorkey(BLACK)
    explosion_anim['player'].append(img)

#######################################加載聲音#######################################

#加載炮彈、導彈發射聲音
shooting_sound = pygame.mixer.Sound(path.join(sound_folder,'pew.wav'))
missile_sound = pygame.mixer.Sound(path.join(sound_folder,'rocket.ogg'))

#加載敵機、火山石爆炸聲音
expl_sounds = []
for sound in ['expl3.wav', 'expl6.wav']:
    expl_sounds.append(pygame.mixer.Sound(path.join(sound_folder,sound)))
#調低音量
pygame.mixer.music.set_volume(0.2)

#加載玩家爆炸聲音
player_die_sound = pygame.mixer.Sound(path.join(sound_folder,'rumble1.ogg'))

#######################################函數區#######################################

#遊戲初始界面和準備開始界面函數
def main_menu():
    global screen
    #加載遊戲初始界面背景音樂
    menu_song = pygame.mixer.music.load(path.join(sound_folder,"menu.ogg"))
    #循環播放
    pygame.mixer.music.play(-1)
    #加載遊戲初始界面背景圖片
    title = pygame.image.load(path.join(img_dir,"main.png")).convert()
    title = pygame.transform.scale(title,(WIDTH, HEIGHT),screen)
    screen.blit(title,(0,0))
    pygame.display.update()
    #檢測玩家操作事件
    while True:
        ev = pygame.event.poll()
        if ev.type == pygame.KEYDOWN:
            if ev.key == pygame.K_RETURN:
                break
        elif ev.type == pygame.QUIT:
                pygame.quit()
                quit()
        else:
            draw_text(screen, "Press [ENTER] To Begin", 30, WIDTH/2, HEIGHT/2)
            draw_text(screen, "[A] ←  [S] ↓  [D] →  [W] ↑", 30, WIDTH/2, 2*HEIGHT/3) 
            draw_text(screen, "[Space] fire", 30, WIDTH/2, 3*HEIGHT/4)           
            pygame.display.update()
    #加載準備聲音
    ready = pygame.mixer.Sound(path.join(sound_folder,'getready.ogg'))
    ready.play()
    #加載準備開始界面背景顏色和文本
    screen.fill(BLACK)
    draw_text(screen, "GET READY!", 40, WIDTH/2, HEIGHT/3)
    pygame.display.update()

#設置文本屬性函數
def draw_text(surf,text,size,x,y):
    #定義文本參數
    font = pygame.font.Font(font_name,size)
    text_surface = font.render(text,True,WHITE)
    text_rect = text_surface.get_rect()
    text_rect.midtop = (x,y)
    surf.blit(text_surface,text_rect)

#設置玩家血量條屬性函數
def draw_shield_bar(surf,x,y,pct):
    pct = max(pct,0)
    fill = (pct/100)*BAR_LENGTH
    outline_rect = pygame.Rect(x,y,BAR_LENGTH,BAR_HEIGHT)
    fill_rect = pygame.Rect(x,y,fill,BAR_HEIGHT)
    pygame.draw.rect(surf,GREEN,fill_rect)
    pygame.draw.rect(surf,WHITE,outline_rect,2)

#設置玩家生命數量屬性函數
def draw_lives(surf,x,y,lives,img):
    for i in range(lives):
        img_rect = img.get_rect()
        img_rect.x = x+30*i
        img_rect.y = y
        surf.blit(img,img_rect)

#添加敵機函數
def newmob():
    mob_element = Mob()
    all_sprites.add(mob_element)
    mobs.add(mob_element)

#添加火山石函數
def newlava():
    lava_element = Lava()
    all_sprites.add(lava_element)
    lavas.add(lava_element)

#######################################類區#######################################

class Explosion(pygame.sprite.Sprite):
    '''創建爆炸類'''
    def __init__(self,center,size):
        pygame.sprite.Sprite.__init__(self)
        self.size = size
        self.image = explosion_anim[self.size][0]
        self.rect = self.image.get_rect()
        self.rect.center = center
        self.frame = 0 
        self.last_update = pygame.time.get_ticks()
        self.frame_rate = 75

    def update(self):        
        now = pygame.time.get_ticks()
        if now - self.last_update > self.frame_rate:
            self.last_update = now
            self.frame += 1
            if self.frame == len(explosion_anim[self.size]):
                self.kill()
            else:
                center = self.rect.center
                self.image = explosion_anim[self.size][self.frame]
                self.rect = self.image.get_rect()
                self.rect.center = center

class Player(pygame.sprite.Sprite):
    '''創建玩家類'''
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.transform.scale(player_img,(50, 38))
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        self.radius = 20
        self.rect.centerx = WIDTH/2
        self.rect.bottom = HEIGHT-10
        self.speedx = 0
        self.speedy = 0
        self.shield = 100
        self.shoot_delay = 250
        self.last_shot = pygame.time.get_ticks()
        self.lives = 3
        self.hidden = False
        self.hide_timer = pygame.time.get_ticks()
        self.power = 1
        self.power_timer = pygame.time.get_ticks()

    def update(self):
        if self.power >= 2 and pygame.time.get_ticks() - self.power_time > POWERUP_TIME: 
            self.power -= 1
            self.power_time = pygame.time.get_ticks()
        if self.hidden and pygame.time.get_ticks() - self.hide_timer > 1000:
            self.hidden = False
            self.rect.centerx = WIDTH/2
            self.rect.bottom = HEIGHT-30
        self.speedx = 0
        self.speedy = 0
        #方向控制:A控制左、D控制右、W控制上、S控制下、A+W控制左上、A+S控制左下、D+W控制右上、D+S控制右下
        keystate = pygame.key.get_pressed()
        if keystate[K_a]:
            self.speedx = -5
        if keystate[K_d]:
            self.speedx = 5
        if keystate[K_w]:
            self.speedy = -5
        if keystate[K_s]:
            self.speedy = 5
        #發射控制:空格
        if keystate[pygame.K_SPACE]:
            self.shoot()
        #設置玩家移動邊界
        if self.rect.right > WIDTH:
            self.rect.right = WIDTH
        if self.rect.left < 0:
            self.rect.left = 0
        if self.rect.top < 10:
            self.rect.top = 10
        if self.rect.bottom > HEIGHT-10:
            self.rect.bottom = HEIGHT-10
        self.rect.x += self.speedx
        self.rect.y += self.speedy

    def shoot(self):
        now = pygame.time.get_ticks()
        if now-self.last_shot > self.shoot_delay:
            self.last_shot = now
            #單火力
            if self.power == 1:
                bullet = Bullet(self.rect.centerx, self.rect.top)
                all_sprites.add(bullet)
                bullets.add(bullet)
                shooting_sound.play()
            #雙火力
            if self.power == 2:
                bullet1 = Bullet(self.rect.left, self.rect.centery)
                bullet2 = Bullet(self.rect.right, self.rect.centery)
                all_sprites.add(bullet1)
                all_sprites.add(bullet2)
                bullets.add(bullet1)
                bullets.add(bullet2)
                shooting_sound.play()
            #三火力
            if self.power >= 3:
                bullet1 = Bullet(self.rect.left, self.rect.centery)
                bullet2 = Bullet(self.rect.right, self.rect.centery)
                missile1 = Missile(self.rect.centerx, self.rect.top) # 導彈
                all_sprites.add(bullet1)
                all_sprites.add(bullet2)
                all_sprites.add(missile1)
                bullets.add(bullet1)
                bullets.add(bullet2)
                bullets.add(missile1)
                shooting_sound.play()
                missile_sound.play()

    def powerup(self):
        self.power += 1
        self.power_time = pygame.time.get_ticks()

    def hide(self):
        self.hidden = True
        self.hide_timer = pygame.time.get_ticks()
        self.rect.center = (WIDTH/2, HEIGHT+200)

class Mob(pygame.sprite.Sprite):
    '''創建敵機類'''
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image_orig = random.choice(enemies_images)
        self.image_orig.set_colorkey(BLACK)
        self.image = self.image_orig.copy()
        self.rect = self.image.get_rect()
        self.radius = int(self.rect.width*.90/2)
        self.rect.x = random.randrange(0, WIDTH-self.rect.width)
        self.rect.y = random.randrange(-150,-100)       
        self.speedy = random.randrange(5,10)
        self.speedx = random.randrange(-3,3)
        self.shoot_delay = 1000
        self.last_shot = pygame.time.get_ticks()        

    def update(self):
        self.rect.x += self.speedx
        self.rect.y += self.speedy
        if random.randrange(10) >= 6:
            self.enemies_shoot()        
        if (self.rect.top > HEIGHT+10) or (self.rect.left < -25) or (self.rect.right > WIDTH+20):
            self.rect.x = random.randrange(0,WIDTH-self.rect.width)
            self.rect.y = random.randrange(-100,-40)
            self.speedy = random.randrange(1,8)
    
    def enemies_shoot(self):
        now = pygame.time.get_ticks()
        if now-self.last_shot > self.shoot_delay:
            self.last_shot = now
            enemies_bullet = EnemiesBullet(self.rect.centerx, self.rect.bottom)
            all_sprites.add(enemies_bullet)
            enemies_bullets.add(enemies_bullet)
            shooting_sound.play()

class Lava(pygame.sprite.Sprite):
    '''創建火山石類'''
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image_orig = random.choice(lava_images)
        self.image_orig.set_colorkey(BLACK)
        self.image = self.image_orig.copy()
        self.rect = self.image.get_rect()
        self.radius = int(self.rect.width*.90/2)
        self.rect.x = random.randrange(0, WIDTH-self.rect.width)
        self.rect.y = random.randrange(-150,-100)       
        self.speedy = random.randrange(5,10)
        self.speedx = random.randrange(-3,3)
        self.rotation = 0
        self.rotation_speed = random.randrange(-8, 8)
        self.last_update = pygame.time.get_ticks()

    #添加火山石旋轉效果
    def rotate(self):
        time_now = pygame.time.get_ticks()
        if time_now-self.last_update > 50:
            self.last_update = time_now
            self.rotation = (self.rotation+self.rotation_speed)%360 
            new_image = pygame.transform.rotate(self.image_orig,self.rotation)
            old_center = self.rect.center
            self.image = new_image
            self.rect = self.image.get_rect()
            self.rect.center = old_center                

    def update(self):
        self.rotate()
        self.rect.x += self.speedx
        self.rect.y += self.speedy               
        if (self.rect.top > HEIGHT+10) or (self.rect.left < -25) or (self.rect.right > WIDTH+20):
            self.rect.x = random.randrange(0,WIDTH-self.rect.width)
            self.rect.y = random.randrange(-100,-40)
            self.speedy = random.randrange(1,8)       

class Bullet(pygame.sprite.Sprite):
    '''創建玩家炮彈類'''
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = bullet_img
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        self.rect.bottom = y 
        self.rect.centerx = x
        self.speedy = -10

    def update(self):
        self.rect.y += self.speedy
        if self.rect.bottom < 0:
            self.kill()

class EnemiesBullet(pygame.sprite.Sprite):
    '''創建敵機炮彈類'''
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = enemies_bullet_img
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        self.rect.top = y 
        self.rect.centerx = x
        self.speedy = 10

    def update(self):
        self.rect.y += self.speedy
        if self.rect.top > 600:
            self.kill()

class Missile(pygame.sprite.Sprite):
    '''創建導彈類'''
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = missile_img
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        self.rect.bottom = y
        self.rect.centerx = x
        self.speedy = -10

    def update(self):
        self.rect.y += self.speedy
        if self.rect.bottom < 0:
            self.kill()

class Pow(pygame.sprite.Sprite):
    '''創建補給類'''
    def __init__(self, center):
        pygame.sprite.Sprite.__init__(self)
        self.type = random.choice(['shield', 'gun'])
        self.image = powerup_images[self.type]
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        self.rect.center = center
        self.speedy = 4

    def update(self):
        self.rect.y += self.speedy
        if self.rect.top > HEIGHT:
            self.kill()

#######################################主循環#######################################

#定義遊戲開始界面標識
running = True
menu_display = True

while running:
    if menu_display:
        main_menu()
        pygame.time.wait(3000)
        pygame.mixer.music.stop()
        pygame.mixer.music.load(path.join(sound_folder,'tgfcoder-FrozenJam-SeamlessLoop.ogg'))
        pygame.mixer.music.play(-1)
        menu_display = False

        all_sprites = pygame.sprite.Group()
        player = Player()
        all_sprites.add(player)        
        mobs = pygame.sprite.Group()
        for i in range(4):
            newmob()
        lavas = pygame.sprite.Group()
        for i in range(4): 
            newlava()
        bullets = pygame.sprite.Group()
        enemies_bullets = pygame.sprite.Group()
        powerups = pygame.sprite.Group()

        score = 0   

    clock.tick(FPS)
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False

    all_sprites.update()
    
    #敵機與玩家炮彈碰撞檢測
    hits = pygame.sprite.groupcollide(mobs,bullets,True,True)    
    for hit in hits:
        score += 50-hit.radius
        random.choice(expl_sounds).play()       
        expl = Explosion(hit.rect.center,'lg')
        all_sprites.add(expl)
        if random.random() > 0.9:
            pow = Pow(hit.rect.center)
            all_sprites.add(pow)
            powerups.add(pow)
        newmob()
    
    #火山石與玩家炮彈碰撞檢測
    hits = pygame.sprite.groupcollide(lavas,bullets,True,True)    
    for hit in hits:
        score += 50-hit.radius
        random.choice(expl_sounds).play()      
        expl = Explosion(hit.rect.center,'lg')
        all_sprites.add(expl)
        if random.random() > 0.9:
            pow = Pow(hit.rect.center)
            all_sprites.add(pow)
            powerups.add(pow)
        newlava()

    #玩家與敵機碰撞檢測
    hits = pygame.sprite.spritecollide(player,mobs,True,pygame.sprite.collide_circle)
    for hit in hits:
        player.shield -= hit.radius*2
        expl = Explosion(hit.rect.center,'sm')
        all_sprites.add(expl)
        newmob()
        if player.shield <= 0: 
            player_die_sound.play()
            death_explosion = Explosion(player.rect.center,'player')
            all_sprites.add(death_explosion)
            player.hide()
            player.lives -= 1
            player.shield = 100

    #玩家與火山石碰撞檢測
    hits = pygame.sprite.spritecollide(player,lavas,True,pygame.sprite.collide_circle)
    for hit in hits:
        player.shield -= hit.radius*2
        expl = Explosion(hit.rect.center,'sm')
        all_sprites.add(expl)
        newlava()
        if player.shield <= 0: 
            player_die_sound.play()
            death_explosion = Explosion(player.rect.center,'player')
            all_sprites.add(death_explosion)
            player.hide()
            player.lives -= 1
            player.shield = 100
    
    #玩家與敵機炮彈碰撞檢測
    hits = pygame.sprite.spritecollide(player,enemies_bullets,True,pygame.sprite.collide_circle)
    for hit in hits:
        player.shield -= hit.radius*2
        expl = Explosion(hit.rect.center,'sm')
        all_sprites.add(expl)        
        if player.shield <= 0: 
            player_die_sound.play()
            death_explosion = Explosion(player.rect.center,'player')
            all_sprites.add(death_explosion)
            player.hide()
            player.lives -= 1
            player.shield = 100
    
    #玩家與補給碰撞檢測
    hits = pygame.sprite.spritecollide(player,powerups,True)
    for hit in hits:
        if hit.type == 'shield':
            player.shield += random.randrange(10,30)
            if player.shield >= 100:
                player.shield = 100
        if hit.type == 'gun':
            player.powerup()

    if player.lives == 0 and not death_explosion.alive():
        pygame.time.wait(1000)
        screen.fill(BLACK)
        draw_text(screen, "Game Over", 40, WIDTH/2, HEIGHT/3)
        pygame.display.update()
        pygame.time.wait(3000)
        menu_display = True

    #背景畫卷向下滾動
    screen.fill(BLACK)    
    screen.blit(background,(0,height))
    height += 2
    if height >= -168:
        height = -936

    all_sprites.draw(screen)
    draw_text(screen,str(score),18,WIDTH/2,10)
    draw_shield_bar(screen,5,5,player.shield)    
    draw_lives(screen,WIDTH-100,5,player.lives,player_mini_img)

    pygame.display.flip()    

pygame.quit()

感興趣的朋友可以到我的Gitee碼雲下載源代碼、圖片庫和聲音庫,鏈接如下:
添加鏈接描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章