20244107 实验四《Python程序设计》 实验报告
20244107 2024-2025-2 《Python程序设计》实验四报告
课程:《Python程序设计》
班级: 2441
姓名: 唐近仁
学号:20244107
实验教师:王志强
实验日期:2025年5月31日
必修/选修: 专选课
1.实验要求
(1)Python综合应用:爬虫、数据处理、可视化、机器学习、神经网络、游戏、网络安全等。
例如:编写从社交网络爬取数据,实现可视化舆情监控或者情感分析。
例如:利用公开数据集,开展图像分类、恶意软件检测等
例如:利用Python库,基于OCR技术实现自动化提取图片中数据,并填入excel中。
例如:爬取天气数据,实现自动化微信提醒
例如:利用爬虫,实现自动化下载网站视频、文件等。
例如:编写小游戏:坦克大战、贪吃蛇、扫雷等等
(2)综合考虑我的兴趣爱好等,选择编写小游戏坦克大战。
2.分析设计
(1)编写准备:通过对《零基础学python》中13.1初识pygame章节的学习,以及在网络上广泛的收集相关资料,我掌握了初步的相关知识以及设计思路。
(2)总体思路:基于Python的Pygame库开发坦克大战小游戏,实现多关卡地图、双人模式、道具系统、碰撞检测等功能。通过面向对象编程设计游戏精灵(坦克、子弹、障碍物等),结合事件驱动机制处理用户输入与游戏逻辑,最终实现完整的游戏流程。
(3)核心功能:
支持单人 / 双人模式选择,玩家操控坦克移动、射击。
设计多关卡地图,包含砖墙、铁墙、河流、树木等障碍物。
实现敌方坦克生成、路径寻优及碰撞检测机制。
添加道具系统(如子弹强化、无敌护盾、增加生命等)。
设计开始界面、关卡切换界面及游戏结束界面。
(4)具体实现想法:
双人模式:通过is_dual_mode标志区分单双人,玩家 2 使用 WSAD 键控制。
敌方坦克生成:
每关初始生成固定数量敌方坦克,超出MAXENEMYNUM时暂停生成。
利用random模块随机选择出生点,避免与障碍物重叠。
碰撞检测:
子弹与障碍物 / 坦克碰撞时触发销毁与爆炸效果。
使用pygame.sprite.spritecollide检测坦克与道具、敌方与大本营的碰撞。
道具系统:随机生成道具(如clock= 加速,protect= 无敌),玩家拾取后触发对应效果(通过定时器实现限时增益)。
(5)设计模块:
cfg.py:存储游戏配置(路径、屏幕尺寸、素材路径等),即配置文件。
modules/sprites:定义坦克、子弹、道具等精灵类。
modules/levels:存储各关卡地图数据(文本格式)。
GameLevel.py:处理关卡逻辑(敌方生成、碰撞检测、胜利条件)。
3.实验过程
(1)安装pygame插件:在命令控制中,先下达命令:
如图,然后再输入pip install pycharm完成安装
接下来,检测一下pygame是否安装成功。打开IDLE,输入如下命令:import pygame,运行结果如图所示,说明安装成功
(2)素材准备
在项目根目录创建resources文件夹,按类型分层存储素材,结构如下:
resources/
├─ audios/ # 音效文件(.wav格式)
│ ├─ add.wav # 拾取道具音效
│ ├─ bang.wav # 爆炸音效
│ └─ fire.wav # 射击音效
├─ images/ # 图片素材(.png格式,部分支持透明通道)
│ ├─ background.png # 游戏背景图
│ ├─ bullet/ # 子弹图片
│ │ ├─ bullet_up.png
│ │ └─ bullet_left.png
│ ├─ enemyTank/ # 敌方坦克图片(不同等级/方向)
│ │ ├─ enemy_1_0.png # 敌方坦克1-上
│ │ └─ enemy_2_3.png # 敌方坦克2-右
│ ├─ playerTank/ # 玩家坦克图片
│ ├─ food/ # 道具图片
│ └─ scene/ # 场景障碍物图片(砖墙、铁墙等)
└─ font/ # 字体文件(.ttf格式)
└─ font.ttf # 用于显示游戏面板文字
(3)游戏原理
主要分为图形界面渲染、事件驱动处理和游戏逻辑控制三部分,综合运用面向对象编程与精灵系统实现复杂交互。
游戏初始化(实现以下三个功能)
①设置游戏窗口screen = pygame.display.set_mode((cfg.WIDTH, cfg.HEIGHT)) # 创建630x630像素窗口 pygame.display.set_caption(cfg.TITLE) # 设置标题
②创建游戏时钟
clock = pygame.time.Clock() # 控制帧率
③创建精灵与精灵组
player_tanks_group = pygame.sprite.Group() # 玩家坦克组
enemy_tanks_group = pygame.sprite.Group() # 敌方坦克组
游戏循环(对应 5 大功能)
while is_running:
clock.tick(60) # 设置刷新频率为60FPS
self.__event_handler() # 事件监听
self.__check_collide() # 碰撞检测
self.__update_sprites() # 更新绘制精灵组
pygame.display.update() # 更新屏幕显示
关键功能实现
① 背景地图渲染
原理:通过两张背景图循环滚动模拟场景移动,利用精灵组管理位置更新。
# 背景精灵(简化版)
class Background(pygame.sprite.Sprite):
def __init__(self, image_path):
self.image = pygame.image.load(image_path)
self.rect = self.image.get_rect()
def update(self):
self.rect.y += 2 # 向下滚动速度
if self.rect.top >= cfg.HEIGHT: # 循环重置
self.rect.y = -self.rect.height
# 主代码调用
background_img = pygame.image.load(cfg.OTHER_IMAGE_PATHS['background'])
screen.blit(background_img, (0, 0)) # 绘制背景
② 敌方坦克生成与移动
原理:通过定时器周期性生成敌方坦克,随机位置与方向实现动态效果。
# 定时器设置(每20秒生成新敌人)
generate_enemies_event = pygame.USEREVENT
pygame.time.set_timer(generate_enemies_event, 20000)
# 敌方坦克初始化(随机位置)
for position in self.enemy_tank_positions:
enemy_tanks_group.add(EnemyTank(position=position)) # 从关卡文件读取出生点
③ 碰撞检测系统
子弹与障碍物碰撞:
# 砖墙可被摧毁(双方销毁)
pygame.sprite.groupcollide(player_bullets_group, self.scene_elems['brick_group'], True, True)
敌方坦克与玩家碰撞:
enemies = pygame.sprite.spritecollide(player_tank, enemy_tanks_group, True)
if len(enemies) > 0:
player_tank.kill() # 玩家坦克销毁
④ 玩家操作控制
键盘事件监听:
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_w]: # 玩家1上移
player_tank.move('up')
if self.is_dual_mode and key_pressed[pygame.K_UP]: # 玩家2上移(双人模式)
player2_tank.move('up')
子弹发射逻辑:
bullet = player_tank.shoot() # 发射子弹
player_bullets_group.add(bullet)
⑤ 关卡与资源管理
关卡文件解析:
def __parseLevelFile(self):
with open(self.levelfilepath) as f:
for line in f:
if line.startswith('%TOTALENEMYNUM'):
self.total_enemy_num = int(line.split(':')[-1]) # 解析总敌人数
素材路径配置:
# cfg.py中定义素材路径
BULLET_IMAGE_PATHS = {
'up': 'resources/images/bullet/bullet_up.png',
# 其他方向子弹路径...
}
(4)代码展现
以下是完整的主代码:
'''
Function:
坦克大战小游戏
Author:
god,it's brutal out here!!
'''
import os
import cfg
import pygame
from modules import *
'''主函数'''
def main(cfg):
# 游戏初始化
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((cfg.WIDTH, cfg.HEIGHT))
pygame.display.set_caption(cfg.TITLE)
# 加载游戏素材
sounds = {}
for key, value in cfg.AUDIO_PATHS.items():
sounds[key] = pygame.mixer.Sound(value)
sounds[key].set_volume(1)
# 开始界面
is_dual_mode = gameStartInterface(screen, cfg)
# 关卡数
levelfilepaths = [os.path.join(cfg.LEVELFILEDIR, filename) for filename in sorted(os.listdir(cfg.LEVELFILEDIR))]
# 主循环
for idx, levelfilepath in enumerate(levelfilepaths):
switchLevelIterface(screen, cfg, idx+1)
game_level = GameLevel(idx+1, levelfilepath, sounds, is_dual_mode, cfg)
is_win = game_level.start(screen)
if not is_win: break
is_quit_game = gameEndIterface(screen, cfg, is_win)
return is_quit_game
'''run'''
if __name__ == '__main__':
while True:
is_quit_game = main(cfg)
if is_quit_game:
break
以下是完整的配置文件代码:
'''配置文件'''
import os
'''字体'''
FONTPATH = os.path.join(os.getcwd(), 'resources/font/font.ttf')
BULLET_IMAGE_PATHS = {
'up': os.path.join(os.getcwd(), 'resources/images/bullet/bullet_up.png'),
'down': os.path.join(os.getcwd(), 'resources/images/bullet/bullet_down.png'),
'left': os.path.join(os.getcwd(), 'resources/images/bullet/bullet_left.png'),
'right': os.path.join(os.getcwd(), 'resources/images/bullet/bullet_right.png')
}
ENEMY_TANK_IMAGE_PATHS = {
'1': [
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_1_0.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_1_1.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_1_2.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_1_3.png')
],
'2': [
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_2_0.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_2_1.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_2_2.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_2_3.png')
],
'3': [
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_3_0.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_3_1.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_3_2.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_3_3.png')
],
'4': [
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_4_0.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_4_1.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_4_2.png'),
os.path.join(os.getcwd(), 'resources/images/enemyTank/enemy_4_3.png')
]
}
PLAYER_TANK_IMAGE_PATHS = {
'player1': [
os.path.join(os.getcwd(), 'resources/images/playerTank/tank_T1_0.png'),
os.path.join(os.getcwd(), 'resources/images/playerTank/tank_T1_1.png'),
os.path.join(os.getcwd(), 'resources/images/playerTank/tank_T1_2.png')
],
'player2': [
os.path.join(os.getcwd(), 'resources/images/playerTank/tank_T2_0.png'),
os.path.join(os.getcwd(), 'resources/images/playerTank/tank_T2_1.png'),
os.path.join(os.getcwd(), 'resources/images/playerTank/tank_T2_2.png')
]
}
FOOD_IMAGE_PATHS = {
'boom': os.path.join(os.getcwd(), 'resources/images/food/food_boom.png'),
'clock': os.path.join(os.getcwd(), 'resources/images/food/food_clock.png'),
'gun': os.path.join(os.getcwd(), 'resources/images/food/food_gun.png'),
'iron': os.path.join(os.getcwd(), 'resources/images/food/food_iron.png'),
'protect': os.path.join(os.getcwd(), 'resources/images/food/food_protect.png'),
'star': os.path.join(os.getcwd(), 'resources/images/food/food_star.png'),
'tank': os.path.join(os.getcwd(), 'resources/images/food/food_tank.png')
}
HOME_IMAGE_PATHS = [
os.path.join(os.getcwd(), 'resources/images/home/home1.png'),
os.path.join(os.getcwd(), 'resources/images/home/home_destroyed.png')
]
SCENE_IMAGE_PATHS = {
'brick': os.path.join(os.getcwd(), 'resources/images/scene/brick.png'),
'ice': os.path.join(os.getcwd(), 'resources/images/scene/ice.png'),
'iron': os.path.join(os.getcwd(), 'resources/images/scene/iron.png'),
'river1': os.path.join(os.getcwd(), 'resources/images/scene/river1.png'),
'river2': os.path.join(os.getcwd(), 'resources/images/scene/river2.png'),
'tree': os.path.join(os.getcwd(), 'resources/images/scene/tree.png')
}
OTHER_IMAGE_PATHS = {
'appear': os.path.join(os.getcwd(), 'resources/images/others/appear.png'),
'background': os.path.join(os.getcwd(), 'resources/images/others/background.png'),
'boom_dynamic': os.path.join(os.getcwd(), 'resources/images/others/boom_dynamic.png'),
'boom_static': os.path.join(os.getcwd(), 'resources/images/others/boom_static.png'),
'gameover': os.path.join(os.getcwd(), 'resources/images/others/gameover.png'),
'logo': os.path.join(os.getcwd(), 'resources/images/others/logo.png'),
'mask': os.path.join(os.getcwd(), 'resources/images/others/mask.png'),
'protect': os.path.join(os.getcwd(), 'resources/images/others/protect.png'),
'tip': os.path.join(os.getcwd(), 'resources/images/others/tip.png'),
'gamebar': os.path.join(os.getcwd(), 'resources/images/others/gamebar.png')
}
'''声音'''
AUDIO_PATHS = {
'add': os.path.join(os.getcwd(), 'resources/audios/add.wav'),
'bang': os.path.join(os.getcwd(), 'resources/audios/bang.wav'),
'blast': os.path.join(os.getcwd(), 'resources/audios/blast.wav'),
'fire': os.path.join(os.getcwd(), 'resources/audios/fire.wav'),
'Gunfire': os.path.join(os.getcwd(), 'resources/audios/Gunfire.wav'),
'hit': os.path.join(os.getcwd(), 'resources/audios/hit.wav'),
'start': os.path.join(os.getcwd(), 'resources/audios/start.wav')
}
'''屏幕'''
WIDTH = 630
HEIGHT = 630
BORDER_LEN = 3
GRID_SIZE = 24
PANEL_WIDTH = 150
TITLE = '坦克大战 —— 杏仁大王'
'''关卡'''
LEVELFILEDIR = os.path.join(os.getcwd(), 'modules/levels')
以下是关卡逻辑核心代码:
def start(self, screen):
# 精灵组初始化
player_tanks_group = pygame.sprite.Group()
enemy_tanks_group = pygame.sprite.Group()
# 碰撞检测示例
pygame.sprite.groupcollide(player_bullets_group, self.scene_elems['brick_group'], True, True)
# 胜利/失败条件
if len(enemy_tanks_group) == 0 and self.total_enemy_num == 0: return True
以下是界面交互模块:
def gameStartInterface(screen, cfg):
while True:
if event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN:
return is_dual_mode # 返回单双人模式选择结果
4.实验结果
可以正常运行,选择玩家数
可以正常进行游戏
以下是运行视频的链接:https://b23.tv/h1AwcRN
5. 实验过程中遇到的问题和解决过程
-问题1:安装pycharm板块时,输入“pip install pygame”指令后显示
-问题1解决方案:上网查询资料,发现是因为没有配置好环境变量,添加环境变量即可。
-问题2:双人模式按键冲突,玩家 2 按键时,玩家 1 坦克同时移动。
-问题2解决方案:查询资料并询问理科同学之后,了解到是使用pygame.key.get_pressed()获取按键状态时,未区分玩家输入。为玩家 2 单独设置按键映射(如玩家 1 用方向键,玩家 2 用 WSAD),在事件处理中分别判断即可。
-问题3:无法找到图片素材文件。
-问题3解决方案:图片文件夹路径错误,不小心在文件夹中又多嵌套了一个文件夹,修改过后成功运行。
6. 收获与感悟
这学期的 Python 课程,像一把精巧的钥匙,悄然为我开启了编程世界的奇妙大门。还记得初次接触时,连环境搭建都让我手忙脚乱,在命令行窗口反复输入指令,手忙脚乱。随着课程推进,从基础语法、数据类型,到控制结构、函数定义,再到面向对象编程、socket 编程、网络爬虫开发这些进阶内容,我在理论知识的海洋里不断遨游探索。老师授课时擅长化繁为简,讲解列表、字典等序列类型时,用一些贴近生活的案例,瞬间就让抽象的概念变得鲜活起来,我也很快掌握了不同数据结构的特点与适用场景。
在实践环节中,从简单的猜数字、猜拳游戏,到计算器制作,每一个项目都像是一场充满挑战的冒险。在编写猜数字游戏代码时,我反复琢磨如何设计游戏流程,怎样处理用户输入的各种情况,又该如何设置循环和结束条件。调试过程中,程序报错、逻辑混乱的状况时常出现,但正是在一次次修改代码、排除故障的过程中,我不仅巩固了知识,更学会了用编程思维拆解问题、解决问题。
Python 课程还极大地激发了我的探索精神。记得在 socket 编程实验中,程序始终无法建立连接,望着控制台的报错信息,我内心满是焦虑。但这次我没有轻易放弃,而是一头扎进技术论坛和官方文档,逐行分析代码逻辑,最终发现是端口号设置错误。当程序成功运行的那一刻,那份喜悦至今难忘。这个经历让我明白,每一个编程难题都是成长的阶梯,只要敢于探索尝试,就一定能找到解决办法。
编程对细节的严苛要求,也悄然改变着我的习惯。在 Python 编程中,一个标点符号的遗漏、缩进格式的错误,都可能让程序陷入 “瘫痪”。正是这种对精准度的极致追求,让我在日常学习和生活中也变得更加细致严谨,养成了反复检查、精益求精的做事风格。
如今回看,从最初连环境都搭建不好的编程小白,到能独立完成坦克大战这类小项目,尽管代码还有诸多不完善之处,但每一行代码的背后,都是成长的见证,都让我充满成就感。展望未来,我渴望在网络爬虫领域深入钻研,用 Python 实现数据的高效整理和文件的批量处理,让编程真正成为提升学习、工作效率的得力助手。感谢这门意义非凡的 Python 课程,它不仅教会我用代码解决实际问题,更赋予我紧跟时代技术浪潮的勇气与信心!

浙公网安备 33010602011771号