python+pygame空战游戏 详细注释

游戏规则

玩家1和玩家2各控制一架飞机,通过发射子弹或导弹给对方减血量,拿空投加血量,血量为空时获胜,飞机相撞则两败俱伤。

分析

  • 玩家1:
    移动: WASD
    子弹: 空格
    导弹: F1
  • 玩家2:
    移动: 方向键
    子弹: 回车
    导弹: F12

资源下载

https://pan.baidu.com/s/1hLmZj8_hD5BADywNyfCx2Q
提取码: xjci

界面分析

  1. 创建血量条,以矩形作为血量条
  2. 显示玩家技能冷却时间
  3. 绘制双方飞机
  4. 随机掉空投
  5. 绘制子弹和导弹
  6. 绘制中央分隔线
  7. 显示背景
  8. 游戏结束后显示BANG特效

项目结构

项目结构

  • resources/background/background.png为背景
  • resources/font/font.ttf为游戏字体
  • resources/icon.png为窗口图标
  • resources/sounds/boom.wav为BOOM音效
  • resources/sounds/spiral.wav为空战音效(BGM)
  • resources/sprites/airdrop.png为空投图片
  • resources/sprites/bang.png为BANG特效
  • resources/sprites/bullet.png为子弹图片
  • resources/missile.png为导弹图片
  • resources/plane1.png为玩家1的飞机
  • resources/plane2.png为玩家2的飞机
  • airdrop.py定义空投类
  • bang.py定义BANG特效类
  • bloodbar.py定义血量条类
  • configs.py定义游戏常量
  • game.py为主程序
  • missile.py定义导弹类
  • plane.py定义飞机类
  • text.py定义游戏文字

依赖项

pip install playsound
pip install pygame==2.0.1

代码

configs.py

TITLE = '极端空战'  # 窗口标题
WIDTH = 1020  # 窗口长
HEIGHT = 650  # 窗口宽
CENTER_LINE_WIDTH = 4  # 分隔线宽度
ICON_PATH = 'resources/icon/icon.png'  # 窗口图标
BACKGROUND_PATH = 'resources/background/background.png'  # 背景图片
PLANE1_PATH = 'resources/sprites/plane1.png'  # 战机1图片
PLANE2_PATH = 'resources/sprites/plane2.png'  # 战机2图片
BULLET_PATH = 'resources/sprites/bullet.png'  # 子弹图片
BANG_PATH = 'resources/sprites/bang.png'  # BANG特效图片
MISSILE_PATH = 'resources/sprites/missile.png'  # 导弹图片
AIRDROP_PATH = 'resources/sprites/airdrop.png'  # 空投图片
BULLET_SIZE = (30, 45)  # 子弹大小
MISSILE_SIZE = (50, 18)  # 导弹大小
BANG_SIZE = (235, 138)  # BANG特效大小
AIRDROP_SIZE = (70, 70)  # 空投大小
BGM_PATH = 'resources/sounds/spiral.wav'  # BGM
BOOM_PATH = 'resources/sounds/boom.wav'  # BOOM音效
GAME_FONT_PATH = 'resources/font/font.ttf'  # 游戏字体
GAME_FONT_SIZE = 26  # 字体大小
GAME_FONT_COLOR = (0, 0, 0)  # 字体颜色:黑色
PLANE_SIZE = (WIDTH // 10, HEIGHT // 10)  # 飞机大小
SPEED = 6  # 移速
FPS = 40  # 屏幕刷新帧率
BLOOD_BAR_WIDTH = 15  # 血量条宽度
BLOOD_BAR_COLOR = (255, 0, 0)  # 红色
BLOOD_BAR_SIDE_COLOR = (0, 0, 0)  # 黑色
BLOOD_VALUE = 200  # 血量
BLOOD_BAR_SIDE_WIDTH = 2  # 血量条边宽
BULLET_HURT = 10  # 子弹伤害
MISSILE_HURT = 50  # 导弹伤害
AIRDROP_HELP = 20  # 空投回血
BULLET_COOLING_TIME = 100  # 子弹冷却时间
MISSILE_COOLING_TIME = 450  # 导弹冷却时间
AIRDROP_COOLING_TIME = 120  # 空投冷却时间

airdrop.py

import pygame
import random
from configs import *
from pygame.sprite import Sprite

class Airdrop(Sprite):  # 空投类
    def __init__(self):
        super(Airdrop, self).__init__()
        self.image = pygame.image.load(AIRDROP_PATH)
        self.image = pygame.transform.scale(self.image, AIRDROP_SIZE)
        self.rect = self.image.get_rect()
        self.rect.top = 0
        self.rect.left = random.randint(SPEED, WIDTH - self.rect.height)
        
    def update(self, *args, **kwargs):
        self.rect.bottom += round(SPEED * 1.5)  # 空投速度1.5x
        if self.rect.bottom >= HEIGHT:  # 移出边界
            self.kill()

bang.py

import pygame
from configs import *

class Bang(object):
    def __init__(self):
        self.image = pygame.image.load(BANG_PATH)
        self.image = pygame.transform.scale(self.image, BANG_SIZE)
        
    def update(self, screen: pygame.Surface, x: float, y: float):
        screen.blit(self.image, (x, y))

bloodbar.py

import pygame
from configs import *
from text import GameText

class BloodBar(object):  # 血量条类
    def __init__(self, left: float, top: float, text_angel='left'):
        self.value = BLOOD_VALUE
        self.rect = pygame.Rect(left, top, self.value, BLOOD_BAR_WIDTH)
        self.left = left
        self.top = top
        self.text = GameText()
        self.text_angel = text_angel
        
    def set(self, value):
        self.value = value
        self.rect = pygame.Rect(self.left, self.top, self.value, BLOOD_BAR_WIDTH)
        
    def update(self, screen: pygame.Surface):
        pygame.draw.rect(screen, BLOOD_BAR_COLOR, self.rect)  # 绘制矩形当做血量条
        pygame.draw.rect(screen, BLOOD_BAR_SIDE_COLOR,
                         pygame.Rect(self.left, self.top, BLOOD_VALUE, BLOOD_BAR_WIDTH + 1),
                         width=BLOOD_BAR_SIDE_WIDTH)  # 绘制血量条边框
        self.text.set(str(self.value))  # 绘制血量信息
        if self.text_angel == 'left':
            self.text.update(self.left + self.value + 20, 0, screen)
        else:
            self.text.update(self.left - 50, 0, screen)
              
class Player1BloodBar(BloodBar):
    def __init__(self):
        super(Player1BloodBar, self).__init__(0, 0)
        
class Player2BloodBar(BloodBar):
    def __init__(self):
        super(Player2BloodBar, self).__init__(WIDTH - BLOOD_VALUE - 15, 0, 'right')

bullet.py

import pygame
from pygame.sprite import Sprite
from configs import *

class Bullet(Sprite):  # 子弹类
    def __init__(self, x, y, angel='left'):
        super(Bullet, self).__init__()
        self.image = pygame.image.load(BULLET_PATH)
        self.image = pygame.transform.scale(self.image, BULLET_SIZE)
        self.rect = self.image.get_rect()
        self.rect.x, self.rect.y = x, y
        self.angel = angel
        
    def update(self, *args, **kwargs):
        if self.angel == 'left':
            self.rect.left += SPEED
        else:
            self.rect.left -= SPEED
        if self.rect.right > WIDTH or self.rect.left < 0:
            self.kill()

missile.py

import pygame
from configs import *
from pygame.sprite import Sprite

class Missile(Sprite):  # 导弹类
    def __init__(self, x, y, angel='left'):
        super(Missile, self).__init__()
        self.image = pygame.image.load(MISSILE_PATH)
        self.image = pygame.transform.scale(self.image, MISSILE_SIZE)
        if angel == 'left':
            self.image = pygame.transform.rotate(self.image, 180)  # 旋转导弹方向
        self.rect = self.image.get_rect()
        self.rect.x, self.rect.y = x, y
        self.angel = angel
    
    def update(self, *args, **kwargs):
        if self.angel == 'left':
            self.rect.left += SPEED
        else:
            self.rect.left -= SPEED
        if self.rect.right > WIDTH or self.rect.left < 0:
            self.kill()  # 移出边界就删除

plane.py

import pygame
import random
from configs import *

class Plane(object):  # 飞机类
    def __init__(self, screen: pygame.Surface, image_path: str):
        self.screen = screen
        self.screen_rect = screen.get_rect()
        
        self.image = pygame.image.load(image_path)
        self.image = pygame.transform.scale(self.image, PLANE_SIZE)
        self.rect = self.image.get_rect()
        
        self.move_left = self.move_right = \
            self.move_up = self.move_down = False  # 为了实现持续按键盘可移动
    
    def update(self):
        self.screen.blit(self.image, self.rect)
    
    def move(self):
        if self.move_left:
            if self.rect.left - SPEED > 0:  # 防止移出去
                self.rect.left -= SPEED
            else:
                self.move_left = False      
        elif self.move_right:
            if self.rect.left + SPEED < WIDTH:  # 防止移出去
                self.rect.left += SPEED
            else:
                self.move_right = False
        elif self.move_up:
            if self.rect.top - SPEED > 0:  # 防止移出去
                self.rect.top -= SPEED
            else:
                self.move_up = False        
        elif self.move_down:
            if self.rect.top + SPEED < HEIGHT:  # 防止移出去
                self.rect.top += SPEED
            else:
                self.move_down = False

class Player1(Plane):
    def __init__(self, screen: pygame.Surface):
        super(Player1, self).__init__(screen, PLANE1_PATH)
        self.rect.left = SPEED
        self.rect.top = random.randint(SPEED,
                                       self.screen_rect.height - self.rect.width)  # 随机出生地

class Player2(Plane):
    def __init__(self, screen: pygame.Surface):
        super(Player2, self).__init__(screen, PLANE2_PATH)
        self.rect.right = self.screen_rect.width - SPEED
        self.rect.bottom = random.randint(self.rect.width,
                                          self.screen_rect.height - SPEED)

text.py

import pygame
from configs import *

class GameText(object):  # 文字类
    def __init__(self):
        self.font = pygame.font.Font(GAME_FONT_PATH, GAME_FONT_SIZE)
        self.text = ''
        
    def set(self, text: str):
        self.text = text
        
    def update(self, x: int, y: int, screen: pygame.Surface):
        surface = self.font.render(self.text, True, GAME_FONT_COLOR)
        screen.blit(surface, (x, y))

game.py

import sys
import pygame
from pygame.locals import *  # 导入pygame事件
from playsound import playsound  # 使用play sound同步放音
# 导入游戏常量及角色
from configs import *
from plane import Player1, Player2
from text import GameText
from bloodbar import Player1BloodBar, Player2BloodBar
from bullet import Bullet
from bang import Bang
from missile import Missile
from airdrop import Airdrop

def init():
    pygame.init()
    pygame.mixer.init()
    pygame.font.init()
    
def destroy():  # 关闭游戏
    pygame.quit()
    sys.exit()

init()  # 初始化pygame
screen = pygame.display.set_mode((WIDTH, HEIGHT))
# 用下面的代码全屏玩游戏
# screen = pygame.display.set_mode((WIDTH, HEIGHT), flags=FULLSCREEN)
icon = pygame.image.load(ICON_PATH)
pygame.display.set_icon(icon)  # 设图标
pygame.display.set_caption(TITLE)  # 设标题
background = pygame.image.load(BACKGROUND_PATH)
background = pygame.transform.scale(background, (WIDTH, HEIGHT))  # 缩放自适应窗口大小

bang = Bang()
player1 = Player1(screen)
player2 = Player2(screen)
text_render = GameText()
tips = ''

over_text = ''
lose_player = player1

player1_blood_bar = Player1BloodBar()
player2_blood_bar = Player2BloodBar()

sound = pygame.mixer.Sound(BGM_PATH)

# 创建精灵组
player1_bullets = pygame.sprite.Group()
player2_bullets = pygame.sprite.Group()
player1_missiles = pygame.sprite.Group()
player2_missiles = pygame.sprite.Group()
airdrops = pygame.sprite.Group()

player1_bullet_clock = 0
player2_bullet_clock = 0
player1_missile_clock = 0
player2_missile_clock = 0

rates = 0  # 频率计数器
clock = pygame.time.Clock()

while True:
    clock.tick(FPS)
    rates += 1

    if player1_blood_bar.value <= 0:
        playsound(BOOM_PATH)
        if player2_blood_bar.value <= 0:  # 都没了
            lose_player = player1
            over_text = '两败俱伤!'
            break
        lose_player = player1
        over_text = '玩家2获胜!'
        break
    elif player2_blood_bar.value <= 0:
        lose_player = player2
        playsound(BOOM_PATH)
        over_text = '玩家1获胜!'
        break

    screen.blit(background, (0, 0))
    pygame.draw.line(screen,
                     (255, 0, 0),  # 红色
                     (WIDTH // 2, 0),  # 上
                     (WIDTH // 2, HEIGHT),   # 下
                     width=CENTER_LINE_WIDTH)  # 绘制分隔线
    sound.play()  # 播放BGM
    player1.move()
    player2.move()
    player1_blood_bar.update(screen)
    player2_blood_bar.update(screen)
    
    player1.update()
    player2.update()
    
    text_render.update(0, BLOOD_BAR_WIDTH + 25, screen)  # 显示文字
    
    player1_bullets.draw(screen)
    player2_bullets.draw(screen)
    airdrops.draw(screen)
    player1_bullets.update()
    player2_bullets.update()
    airdrops.draw(screen)
    
    player1_missiles.draw(screen)
    player2_missiles.draw(screen)
    player1_missiles.update()
    player2_missiles.update()
    airdrops.update()
    
    text_render.set(tips)
    
    if rates % AIRDROP_COOLING_TIME == 0:
        airdrop = Airdrop()  # 空投
        airdrops.add(airdrop)
    
    if pygame.sprite.collide_rect(player1, player2):  # 飞机相撞
        playsound(BOOM_PATH)  # BOOM音效
        over_text = '两败俱伤!'
        break
    if pygame.sprite.spritecollide(player1, player2_bullets, True):
        player1_blood_bar.set(player1_blood_bar.value - BULLET_HURT)
    if pygame.sprite.spritecollide(player2, player1_bullets, True):
        player2_blood_bar.set(player2_blood_bar.value - BULLET_HURT)
    if pygame.sprite.spritecollide(player1, player2_missiles, True):
        player1_blood_bar.set(player1_blood_bar.value - MISSILE_HURT)
    if pygame.sprite.spritecollide(player2, player1_missiles, True):
        player2_blood_bar.set(player2_blood_bar.value - MISSILE_HURT)
    if pygame.sprite.spritecollide(player1, airdrops, True):
        if player1_blood_bar.value + AIRDROP_HELP <= BLOOD_VALUE:
            player1_blood_bar.set(player1_blood_bar.value + AIRDROP_HELP)
    if pygame.sprite.spritecollide(player2, airdrops, True):
        if player2_blood_bar.value + AIRDROP_HELP <= BLOOD_VALUE:
            player2_blood_bar.set(player2_blood_bar.value + AIRDROP_HELP)
    
    for event in pygame.event.get():
        if event.type == QUIT:
            destroy()
        elif event.type == KEYDOWN:
            if event.key == K_ESCAPE:  # ESC结束
                destroy()
            elif event.key == K_a:
                player1.move_left = True
            elif event.key == K_d:
                player1.move_right = True
            elif event.key == K_w:
                player1.move_up = True
            elif event.key == K_s:
                player1.move_down = True
            elif event.key == K_LEFT:
                player2.move_left = True
            elif event.key == K_RIGHT:
                player2.move_right = True
            elif event.key == K_UP:
                player2.move_up = True
            elif event.key == K_DOWN:
                player2.move_down = True
        # 持续按键可移动
        elif event.type == KEYUP:
            if event.key == K_a:
                player1.move_left = False
            elif event.key == K_d:
                player1.move_right = False
            elif event.key == K_w:
                player1.move_up = False
            elif event.key == K_s:
                player1.move_down = False
            elif event.key == K_LEFT:
                player2.move_left = False
            elif event.key == K_RIGHT:
                player2.move_right = False
            elif event.key == K_UP:
                player2.move_up = False
            elif event.key == K_DOWN:
                player2.move_down = False
                
            elif event.key == K_SPACE:  # 发射子弹: 空格键
                if rates - player1_bullet_clock >= BULLET_COOLING_TIME:
                    player1_bullet_clock = rates
                    bullet = Bullet(player1.rect.right, player1.rect.y)
                    player1_bullets.add(bullet)
            elif event.key == K_RETURN:  # 发射子弹: 回车键
                if rates - player2_bullet_clock >= BULLET_COOLING_TIME:
                    player2_bullet_clock = rates
                    bullet = Bullet(player2.rect.x, player2.rect.y, angel='right')
                    player2_bullets.add(bullet)
                    
            elif event.key == K_F1:  # 发射导弹: F1
                if rates - player1_missile_clock >= MISSILE_COOLING_TIME:
                    player1_missile_clock = rates
                    missile = Missile(player1.rect.right, player1.rect.y)
                    player1_missiles.add(missile)
            elif event.key == K_F12:  # 发射导弹: F12
                if rates - player2_missile_clock >= MISSILE_COOLING_TIME:
                    player2_missile_clock = rates
                    missile = Missile(player2.rect.x, player2.rect.y, 'right')
                    player2_missiles.add(missile)

    # 显示玩家冷却时间信息
    player1_bullet_cooling_time = BULLET_COOLING_TIME - (rates - player1_bullet_clock)
    player2_bullet_cooling_time = BULLET_COOLING_TIME - (rates - player2_bullet_clock)
    player1_missile_cooling_time = MISSILE_COOLING_TIME - (rates - player1_missile_clock)
    player2_missile_cooling_time = MISSILE_COOLING_TIME - (rates - player2_missile_clock)
    # 防止冷却时间为负数
    player1_bullet_cooling_time = 0 \
        if player1_bullet_cooling_time < 0 else player1_bullet_cooling_time
    player2_bullet_cooling_time = 0 \
        if player2_bullet_cooling_time < 0 else player2_bullet_cooling_time
    player1_missile_cooling_time = 0 \
        if player1_missile_cooling_time < 0 else player1_missile_cooling_time
    player2_missile_cooling_time = 0 \
        if player2_missile_cooling_time < 0 else player2_missile_cooling_time
    tips = (
        f'玩家1子弹冷却时间: {player1_bullet_cooling_time} '
        f'导弹冷却时间: {player1_missile_cooling_time}   '
        f'玩家2子弹冷却时间: {player2_bullet_cooling_time} '
        f'导弹冷却时间: {player2_missile_cooling_time}'
    )
            
    pygame.display.update()  # 刷新

    
while True:
    clock.tick(FPS)
    screen.blit(background, (0, 0))
    if over_text:
        text_render.set(over_text)
        text_render.update(WIDTH // 2, HEIGHT // 2, screen)
    player1.update()
    player2.update()
    bang.update(screen, lose_player.rect.x, lose_player.rect.y)
    
    for event in pygame.event.get():
        if event.type == QUIT:
            destroy()
        elif event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                destroy()
   
    pygame.display.update()

运行

python game.py

效果

游戏效果

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值