文章目录
游戏规则
玩家1和玩家2各控制一架飞机,通过发射子弹或导弹给对方减血量,拿空投加血量,血量为空时获胜,飞机相撞则两败俱伤。
分析
- 玩家1:
移动: WASD
子弹: 空格
导弹: F1 - 玩家2:
移动: 方向键
子弹: 回车
导弹: F12
资源下载
https://pan.baidu.com/s/1hLmZj8_hD5BADywNyfCx2Q
提取码: xjci
界面分析
- 创建血量条,以矩形作为血量条
- 显示玩家技能冷却时间
- 绘制双方飞机
- 随机掉空投
- 绘制子弹和导弹
- 绘制中央分隔线
- 显示背景
- 游戏结束后显示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