《一起消灭怪兽吧》——在深夜的屏幕前,你是指引光明的勇者。键盘化作利剑,用方向键在像素战场游走,发射吧,每次击杀都有代码绽放的烟火。这款由Python与Pygame铸就的小游戏,让0与1的世界生长出童真的浪漫。
文章目录
一、概要
本游戏是基于Pygame框架开发的2D射击类游戏,采用经典街机游戏设计范式。游戏实现了玩家控制、怪物生成、碰撞检测、音效系统、UI界面等核心功能,并包含难度升级机制和游戏状态管理系统。项目代码量约400行,采用面向对象编程范式,通过精灵类(Sprite)实现游戏元素的模块化管理,具有较好的可扩展性。
二、游戏界面
三、游戏介绍
1)游戏特性:
三种不同类型的怪兽(普通怪,快速怪,boss)
随分数提升自动升级,每级提升飞机子弹攻速,并增加怪物数量
显示生命值、分数和等级
爆炸效果和音效
游戏结束后可重新开始
2)游戏控制:
上下左右方向键控制飞机移动
自动发射子弹
鼠标点击也可以发射子弹
四、整体架构流程
1)游戏采用典型的三层架构:
-
表现层:Pygame的Surface对象渲染系统
-
逻辑层:
-
精灵管理系统(Sprites)
-
碰撞检测系统
-
游戏状态机(运行/暂停/结束)
-
-
数据层:
-
图像/音频资源加载
-
游戏状态持久化(分数/生命值)
-
2)核心类结构:
classDiagram
class Plane
class Bullet
class Enemy
class FastEnemy
class BossEnemy
class Explosion
Plane --> Bullet : 生成
Enemy <|-- FastEnemy
Enemy <|-- BossEnemy
Bullet --> Enemy : 碰撞
Plane --> Enemy : 碰撞
Enemy --> Explosion : 触发
五、技术细节
1)资源管理
-
图像加载:
-
采用PIL库进行动态尺寸调整(
Image.resize()
),通过pygame.image.fromstring()
实现跨库图像转换,支持alpha通道处理(convert_alpha()
) -
音频系统:
-
背景音乐循环播放(
pygame.mixer.music.play(-1)
) -
音效异常处理机制(try-except块)
-
-
资源回退:
-
当资源加载失败时自动生成红色占位Surface,保证游戏可运行性
2)游戏逻辑
-
怪物生成算法:
enemy_type = random.random()
if enemy_type < 0.7: # 70%普通敌机
enemy = Enemy()
elif enemy_type < 0.9: # 20%快速敌机
enemy = FastEnemy()
else: # 10%BOSS敌机
enemy = BossEnemy()
-
难度升级机制:
每累计1000分提升玩家等级,增强子弹速度(+0.2px/frame)和移动速度(+1px/frame),曾加生命值10(生命值满100则不增加),增加敌人数量0.3倍。
3)碰撞检测
采用Pygame内置碰撞检测方法:
-
子弹-怪物碰撞:
groupcollide()
-
玩家-怪物碰撞:
spritecollide()
-
分层碰撞处理(BOSS需被击中5次)
4)怪物特性
为不同类型的怪物设置了不同的移动速度:
普通怪物:2-3
快速怪物:4-6
BOSS:1-2
你可以通过调整以下参数来改变游戏难度:enemy_spawn_rate:值越小,怪物生成越频繁
各种敌机的speed范围:值越大,怪物下落越快
bullet_interval:值越小,子弹发射越频繁
5)状态管理
-
游戏状态机:
if not paused and not game_over:
# 游戏逻辑
elif game_over:
# 结束菜单处理
-
生命值系统:
使用全局变量life
管理,碰撞扣除10点,BOSS逃脱扣除20点
6)渲染优化
-
使用
clock.tick(60)
保持60FPS帧率 -
分层绘制策略:
-
背景清除
-
精灵组批量绘制(
all_sprites.draw()
) -
UI叠加渲染
-
7)输入处理
-
键盘事件:方向键控制移动,P键暂停
-
鼠标事件:左键射击
-
菜单导航:上下方向键选择,回车确认
六、需要的文件
1. 需要的资源文件:
player.png(玩家飞机)
enemy.png(普通怪物)
fast_enemy.png(快速怪物)
boss.png(BOSS)
explosion1.png, explosion2.png, explosion3.png(爆炸效果)
background.mp3(背景音乐)
shoot.wav(射击音效)
explosion.wav(爆炸音效)
game_over.wav(游戏结束音效)2. 图片要求:
建议使用PNG格式的图片,支持透明背景
参考尺寸:
普通怪物:50x50像素
快速怪物:40x40像素
BOSS:100x100像素
* 如果缺少某些资源文件,游戏会使用替代的简单图形,不会影响游戏的正常运行。
* 有需要的话提供百度网盘地址可以下载图片和音频:
链接: https://pan.baidu.com/s/1CSCgSLswcwXSvocHzZ031A?pwd=ce7t提取码: ce7t
七、完整代码
import pygame
import random
import os
from PIL import Image
# 初始化Pygame
pygame.init()
# 设置屏幕尺寸
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# 设置标题
pygame.display.set_caption("打怪兽")
# 定义颜色
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
GRAY = (128, 128, 128)
# 修改后的加载图片函数
def load_image(name, size=None):
try:
fullname = os.path.join(os.path.dirname(__file__), name)
# 使用PIL打开和处理图片
pil_image = Image.open(fullname)
if size:
pil_image = pil_image.resize(size, Image.Resampling.LANCZOS)
# 转换为pygame surface
mode = pil_image.mode
size = pil_image.size
data = pil_image.tobytes()
py_image = pygame.image.fromstring(data, size, mode)
py_image = py_image.convert_alpha()
print(f"图片加载成功: {name}")
return py_image
except Exception as e:
print(f"图片 {name} 加载失败: {e}")
print(f"当前工作目录: {os.getcwd()}")
surface = pygame.Surface(size if size else (50, 50), pygame.SRCALPHA)
surface.fill((255, 0, 0, 128))
return surface
# 加载音乐和音效
pygame.mixer.init()
try:
bg_music = pygame.mixer.music.load("background.mp3")
pygame.mixer.music.play(-1)
except:
print("背景音乐加载失败,继续使用无音乐模式。")
try:
shoot_sound = pygame.mixer.Sound("shoot.wav")
except:
shoot_sound = None
try:
explosion_sound = pygame.mixer.Sound("explosion.wav")
except:
explosion_sound = None
try:
game_over_sound = pygame.mixer.Sound("game_over.wav")
except:
game_over_sound = None
# 定义飞机类
class Plane(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = load_image("player.png", (50, 50))
self.rect = self.image.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT - 100))
self.speed = 8
self.upgrade_level = 1
self.bullet_speed = 10
self.bullet_frequency = 10 # 初始子弹发射间隔
def update(self):
keys = pygame.key.get_pressed()
# 左右移动
if keys[pygame.K_LEFT] and self.rect.left > 0:
self.rect.x -= self.speed
if keys[pygame.K_RIGHT] and self.rect.right < SCREEN_WIDTH:
self.rect.x += self.speed
# 上下移动
if keys[pygame.K_UP] and self.rect.top > 0:
self.rect.y -= self.speed
if keys[pygame.K_DOWN] and self.rect.bottom < SCREEN_HEIGHT:
self.rect.y += self.speed
# 定义子弹类
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y, speed):
super().__init__()
self.image = pygame.Surface((5, 10))
self.image.fill(RED)
self.rect = self.image.get_rect(center=(x, y))
self.speed = speed
if shoot_sound:
shoot_sound.play()
def update(self):
self.rect.y -= self.speed
if self.rect.bottom < 0:
self.kill()
# 定义敌机基类
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = load_image("enemy.png", (50, 50))
self.rect = self.image.get_rect()
self.rect.x = random.randint(0, SCREEN_WIDTH - self.rect.width)
self.rect.y = -self.rect.height
self.speed = random.randint(2, 3)
self.score = 10
def update(self):
global life
self.rect.y += self.speed
if self.rect.top > SCREEN_HEIGHT:
if life > 0:
life -= 10
self.kill()
# 定义快速敌机类
class FastEnemy(Enemy):
def __init__(self):
super().__init__()
self.image = load_image("fast_enemy.png", (40, 40))
self.rect = self.image.get_rect()
self.rect.x = random.randint(0, SCREEN_WIDTH - self.rect.width)
self.rect.y = -self.rect.height
self.speed = random.randint(4, 5)
self.score = 20
# 定义BOSS敌机类
class BossEnemy(Enemy):
def __init__(self):
super().__init__()
self.image = load_image("boss.png", (100, 100))
self.rect = self.image.get_rect()
self.rect.x = random.randint(0, SCREEN_WIDTH - self.rect.width)
self.rect.y = -self.rect.height
self.speed = random.randint(1, 2)
self.score = 50
self.hp = 5
def update(self):
global life
self.rect.y += self.speed
if self.rect.top > SCREEN_HEIGHT:
if life > 0:
life -= 20
self.kill()
# 定义爆炸效果类
class Explosion(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.images = []
for i in range(1, 4):
try:
self.images.append(load_image(f"explosion{i}.png", (50, 50)))
except:
surface = pygame.Surface((50, 50))
surface.fill(RED)
self.images.append(surface)
self.image = self.images[0]
self.rect = self.image.get_rect(center=(x, y))
self.frame = 0
self.frame_rate = 0
if explosion_sound:
explosion_sound.play()
def update(self):
self.frame_rate += 1
if self.frame_rate >= 10:
self.frame += 1
if self.frame < len(self.images):
self.image = self.images[self.frame]
else:
self.kill()
self.frame_rate = 0
# 添加游戏重置函数
def reset_game():
global life, score, plane, bullets, enemies, explosions, all_sprites, enemy_spawn_multiplier
life = 100
score = 0
enemy_spawn_multiplier = 1.0
bullets.empty()
enemies.empty()
explosions.empty()
all_sprites.empty()
plane = Plane()
all_sprites.add(plane)
# 开始界面绘制函数
def draw_start_screen():
title_text = font.render("Fight Monsters", True, WHITE)
start_text = font.render("Start Game", True, WHITE if menu_selection == 0 else GRAY)
quit_text = font.render("Quit Game", True, WHITE if menu_selection == 1 else GRAY)
if menu_selection == 0:
start_text = font.render("> Start Game", True, WHITE)
quit_text = font.render(" Quit Game", True, GRAY)
else:
start_text = font.render(" Start Game", True, GRAY)
quit_text = font.render("> Quit Game", True, WHITE)
title_rect = title_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 3))
start_rect = start_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
quit_rect = quit_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 50))
screen.blit(title_text, title_rect)
screen.blit(start_text, start_rect)
screen.blit(quit_text, quit_rect)
# 创建精灵组
plane = Plane()
bullets = pygame.sprite.Group()
enemies = pygame.sprite.Group()
explosions = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
all_sprites.add(plane)
# 设置时钟
clock = pygame.time.Clock()
# 游戏变量
bullet_interval = 0
enemy_interval = 0
enemy_spawn_rate = 60
enemy_spawn_multiplier = 1.0 # 敌人生成倍率
life = 100
score = 0
paused = False
menu_selection = 0
game_state = "START"
# 设置字体
font = pygame.font.Font(None, 36)
# 主游戏循环
running = True
while running:
# 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if game_state == "START":
if event.key == pygame.K_UP:
menu_selection = 0
elif event.key == pygame.K_DOWN:
menu_selection = 1
elif event.key == pygame.K_RETURN:
if menu_selection == 0:
game_state = "PLAYING"
else:
running = False
elif game_state == "PLAYING":
if event.key == pygame.K_p:
paused = not paused
elif game_state == "OVER":
if event.key == pygame.K_UP:
menu_selection = 0
elif event.key == pygame.K_DOWN:
menu_selection = 1
elif event.key == pygame.K_RETURN:
if menu_selection == 0:
reset_game()
game_state = "PLAYING"
else:
running = False
screen.fill(BLACK)
if game_state == "START":
draw_start_screen()
elif game_state == "PLAYING":
if not paused:
# 发射子弹
bullet_interval += 1
if bullet_interval >= plane.bullet_frequency:
bullets.add(Bullet(plane.rect.centerx, plane.rect.top, plane.bullet_speed))
bullet_interval = 0
# 生成敌机
enemy_interval += 1
if enemy_interval >= enemy_spawn_rate:
# 根据倍率生成多个敌人
enemies_to_spawn = max(1, int(enemy_spawn_multiplier))
for _ in range(enemies_to_spawn):
enemy_type = random.random()
if enemy_type < 0.7:
enemy = Enemy()
elif enemy_type < 0.9:
enemy = FastEnemy()
else:
enemy = BossEnemy()
enemies.add(enemy)
all_sprites.add(enemy)
enemy_interval = 0
# 更新精灵
all_sprites.update()
bullets.update()
enemies.update()
explosions.update()
# 碰撞检测
hits = pygame.sprite.groupcollide(bullets, enemies, True, False)
for bullet, enemies_hit in hits.items():
for enemy in enemies_hit:
if isinstance(enemy, BossEnemy):
enemy.hp -= 1
if enemy.hp <= 0:
enemy.kill()
score += enemy.score
explosions.add(Explosion(enemy.rect.centerx, enemy.rect.centery))
else:
enemy.kill()
score += enemy.score
explosions.add(Explosion(enemy.rect.centerx, enemy.rect.centery))
# 敌机击中飞机
hits = pygame.sprite.spritecollide(plane, enemies, True)
if hits:
life = max(0, life - 10)
explosions.add(Explosion(plane.rect.centerx, plane.rect.centery))
# 检查升级
if score >= plane.upgrade_level * 1000:
plane.upgrade_level += 1
plane.bullet_speed += 2
plane.speed += 1
# 子弹频率随等级提升而减少(发射更密集)
plane.bullet_frequency = max(1.2, 10 // plane.upgrade_level)
# 增加生命值
life = min(100, life + 10) # 限制最大生命值为100
# 增加敌人生成倍率
enemy_spawn_multiplier += 0.3
# 绘制游戏画面
all_sprites.draw(screen)
bullets.draw(screen)
enemies.draw(screen)
explosions.draw(screen)
# 显示游戏信息
life_text = font.render(f"Life: {life}", True, WHITE)
score_text = font.render(f"Score: {score}", True, WHITE)
level_text = font.render(f"Level: {plane.upgrade_level}", True, WHITE)
screen.blit(life_text, (10, 10))
screen.blit(score_text, (10, 50))
screen.blit(level_text, (10, 90))
if paused:
pause_text = font.render("Paused", True, WHITE)
screen.blit(pause_text, (SCREEN_WIDTH // 2 - 50, SCREEN_HEIGHT // 2))
elif game_state == "OVER":
game_over_text = font.render("Game Over", True, RED)
restart_text = font.render("Restart", True, WHITE if menu_selection == 0 else GRAY)
quit_text = font.render("Quit", True, WHITE if menu_selection == 1 else GRAY)
final_score_text = font.render(f"Final Score: {score}", True, WHITE)
if menu_selection == 0:
restart_text = font.render("> Restart", True, WHITE)
quit_text = font.render(" Quit", True, GRAY)
else:
restart_text = font.render(" Restart", True, GRAY)
quit_text = font.render("> Quit", True, WHITE)
screen.blit(game_over_text, (SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 - 75))
screen.blit(restart_text, (SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 - 25))
screen.blit(quit_text, (SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 25))
screen.blit(final_score_text, (SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 75))
# 检查游戏结束
if life <= 0 and game_state == "PLAYING":
if game_over_sound:
game_over_sound.play()
game_state = "OVER"
pygame.display.flip()
clock.tick(60)
# 退出游戏
pygame.quit()
八、打包成exe
1)准备工作:
确保已安装Python环境(建议3.6+版本)
安装打包工具(推荐使用PyInstaller):pip install pyinstaller
2)打包步骤:
1. 定位到项目目录
打开命令行(CMD/PowerShell/终端)
使用cd命令进入游戏代码所在目录:
cd C:\path\to\your\game
2. 基础打包命令
pyinstaller --onefile --windowed game.py
--onefile:生成单个exe文件
--windowed:隐藏控制台窗口(适用于图形界面游戏)
将game.py替换为你的主程序文件名
3. 处理资源文件(如图片/音效等)
如果游戏包含外部资源文件,需要:修改代码中的资源路径为临时路径
import sys
import os
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
# 使用示例
image = pygame.image.load(resource_path("images/monster.png"))
4. 获取exe文件
打包完成后,在项目目录下会生成:
dist/文件夹 ➔ 包含最终exe文件
build/文件夹 ➔ 临时文件(可删除)
5. 进阶选项
参数 | 说明 |
--icon=game.ico | 添加exe图标 |
--noconsole | 完全隐藏控制台 |
--add-data "src;dest" | 添加额外文件 |
--upx-dir UPX_DIR | 使用UPX压缩exe体积 |
九、总结
1)技术亮点
-
采用多态继承实现敌机类型扩展
-
异常处理机制保障程序健壮性
-
动态难度调整提升游戏可玩性
-
使用PIL实现跨分辨率适配
2)扩展性分析
当前架构支持以下扩展:
-
新怪物类型:继承Enemy类并重写属性
-
武器系统:在Plane类中添加武器类型属性
-
成就系统:通过装饰器模式跟踪游戏事件
游戏实现经典射击游戏的核心机制,通过Pygame框架在2D游戏开发中的高效性、模块化设计和面向对象方法,使代码具备良好的可维护性和扩展性。
祝你和孩子玩的开心!如果喜欢这款小游戏的话记得点赞收藏加关注哦!