20222227 2024-2025-2 《Python程序设计》综合实践报告
Python课程综合实践报告
课程名称:《Python程序设计》
专业:计算机科学与技术
姓名:赫连紫阳
学号:20222227
指导教师:王志强
日期:2024年5月14日
1 项目目标
利用Python语言及Pygame库,从零开始构建一款具备核心Roguelike元素的策略回合制游戏。具体目标如下:
1 建立一个包含多种职业、可升级的角色系统。
2 设计并实现一个基于节点探索、包含随机事件的地图系统。
3 开发一个功能完善的、支持多角色对战的回合制战斗系统。
4 为游戏设计并实现一个直观、美观的图形用户界面。
5 最终将所有模块整合,形成一个可玩的、完整的游戏产品。
2 系统分析与设计
2.1 需求分析
经过初步构思和对经典Roguelike游戏的分析,本项目确定了以下核心需求:
1 游戏类型:2D、策略回合制、角色扮演游戏。
2 角色系统:玩家和敌人两大阵营。角色拥有职业(如单体攻击、群体攻击、治疗、辅助)、等级、生命值、攻击力等属性,并可以升级。
3 地图系统:游戏按层级推进,每层由多个节点构成。节点类型包括普通战斗、商店、随机奇遇和BOSS战。路径在节点间随机生成,玩家需规划路线前进。
4 战斗系统:我方和敌方回合交替进行。每回合拥有固定行动次数。玩家可指挥角色进行攻击、治疗等操作,也可使用特殊效果卡牌。
5 经济与奖励系统:战斗胜利可获得金币奖励,用于在商店中招募新角色或购买卡牌。战斗后角色可获得升级机会。
6 交互系统:需要一个战前角色部署界面,允许玩家从阵容中选择参战角色。需要支持鼠标拖拽来浏览超出一屏的地图或角色列表。
7 “濒死”机制:我方角色生命值降为0后不会消失,而是进入无法出战的“濒死”状态,可通过奇遇复活。
2.2 技术选型
1 开发语言:Python 3。Python语法简洁,开发效率高,拥有强大的标准库和第三方库生态,非常适合进行快速原型开发和项目迭代。
2 图形库:Pygame。Pygame是一个成熟、稳定且功能丰富的Python游戏开发库。它封装了对SDL的调用,能方便地处理图形绘制、事件响应、图像加载等任务,是开发2D游戏的理想选择。
2.3 系统总体设计
为实现上述需求并保证代码的可维护性和扩展性,系统采用面向对象的思想进行设计,主要划分为以下几个核心类:
1 Game类:作为游戏的主引擎和状态管理器,负责游戏的主循环、状态切换(如主菜单、地图、战斗、部署)、管理玩家数据、生成关卡等。
2 GUI类:负责所有图形界面的绘制工作,包括开始菜单、地图、战斗场景、部署界面以及各种信息弹窗。它将游戏逻辑与视觉表现解耦。
3 Character类:所有角色的基类,封装了名字、等级、血量、攻击力等通用属性和方法(如受伤、治疗)。其他具体职业通过继承该类来实现各自独特的行为和AI。
4 Battle类:负责处理单场战斗的全部逻辑,包括回合管理、行动顺序、胜负判断、角色技能处理、卡牌效果结算等。
5 Player类:封装了玩家所拥有的全部数据,如角色阵容、持有的卡牌、金币数量等。
6 GameNode类:地图上每个节点的对象表示,存储了节点类型、位置、连接关系以及相关的事件数据。
3 系统实现过程
本项目的实现过程是一个典型的迭代开发模型,大致可分为四个阶段。
3.1 第一阶段:命令行原型搭建
在项目初期,我首先专注于核心逻辑的实现,搭建了一个纯命令行的游戏原型。在这个阶段,我完成了Character、Card等核心类的定义,并实现了一个简易的回合制战斗逻辑。这种方式使我能快速验证玩法的可行性,而不必过早陷入图形界面的细节中。
3.2 第二阶段:图形化界面迁移
在核心玩法验证后。我引入了Pygame库,并创建了GUI类。这项工作是整个项目中工作量最大的一次重构。我为所有需要显示的游戏实体(角色、节点、卡牌)都添加了draw()方法,并通过ai进行游戏贴图的生成,重写了游戏主循环,将其改造为基于Pygame事件驱动的模式。经过这一阶段,游戏从一个抽象的命令行程序,变成了一个有图像、可交互的真实游戏。
3.3 第三阶段:核心功能迭代与BUG修复
在游戏具备基本框架后,我开始逐一实现详细的需求,并修复测试中发现的各种问题。
1 功能实现:
我逐步添加了分层地图的生成逻辑、商店系统、金币经济、战后角色升级、以及BOSS战等核心玩法。
2 交互优化:
根据设想,我开发了战前角色部署系统,并为地图和角色选择界面加入了鼠标拖拽滚动功能,极大地提升了游戏的操作便利性。
3 BUG修复:
开发过程并非一帆风順。我遇到了各种问题,如资源文件加载失败、中文字体显示乱码、角色AI逻辑错误(BOSS不攻击)、战斗伤害结算目标错误等。通过利用print语句调试、分析Pygame报错信息、以及对代码逻辑进行反复推演,我逐一解决了这些问题,保证了游戏的稳定性和正确性。
3.4 第四阶段:策略深度与体验优化
在游戏功能完备后,我将重心转向了提升策略深度和玩家体验。
1 AI与机制优化:
我为巨魔设计了概率性AI,使其行为更难预测;为商店加入了智能招募机制,能根据玩家阵容动态调整新角色出现的概率。
2 “濒死”状态:
引入了“濒死”机制,使角色培养的沉没成本更高,也让恢复类奇遇的价值得以凸显。
3 UI:
我对UI进行了细节打磨,统一了所有弹窗的视觉风格,并重写了战斗布局算法,解决了角色信息重叠的问题。
4 运行结果与总结
4.1 成果展示
经过上述开发过程,最终完成的“闯入城堡的冒险”游戏达到了预期的所有目标。游戏运行流畅,核心功能完备。
开始界面:

地图界面:

奇遇界面:

商店界面:

战斗界面:

战斗胜利:

升级界面:

战斗失败:

boss战1:

boss战2:

1 完整的游戏流程:
玩家可以从主菜单开始新游戏,在程序生成的、包含不同事件节点的地图上进行探索,通过战前部署进入战斗,最终挑战强大的BOSS,并迎来胜利或失败的结局。
2 丰富的策略维度:
玩家需要在地图上规划路线,在战斗中合理运用角色的技能和有限的卡牌,并在战斗后谨慎选择要升级的角色。商店的智能招募和“濒死”机制也为资源管理带来了更多挑战。
3 良好的用户体验:
游戏拥有统一的图形界面,清晰的信息展示,为玩家提供了良好的沉浸式体验。
4.2 问题与展望
尽管当前版本已经非常完善,但仍有一些可以继续改进的地方:
1 缺少音效:
目前游戏是完全静音的,未来可以为背景、按钮点击、攻击、施法等加入音效和背景音乐,能极大地提升表现力。
2 内容可以扩展:
可以设计更多种类的角色、敌人、BOSS和卡牌,增加更多有趣的奇遇事件,进一步丰富游戏的内容和重玩价值。
3 无法存档:
游戏目前不支持进度的保存和读取。
4 可以加入特殊道具机制:
收获后可以获得各种效果如战斗后可以升级两名角色等。
5 代码总体架构分析
5.1 全局设置与初始化
在代码的开头,定义了游戏运行所需的基础环境和常量。
# 导入库
import pygame
import sys
import random
import math
from enum import Enum
#初始化 Pygame
pygame.init()
pygame.font.init()
# 屏幕尺寸、颜色和字体等常量
SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 800
# 其他颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# 字体路径
FONT_PATH = "方正小标宋简.TTF"
# 使用枚举类定义游戏状态,使状态管理更清晰
class GameState(Enum):
START_MENU = 1
MAP = 2
BATTLE = 3
SHOP = 4
...
5.2 核心数据类
游戏中的所有实体,如角色、卡牌、地图节点,都被设计成了类。这种设计利用了继承来共享通用属性和行为,同时又保持了每个类的独特性。
角色继承结构: PlayerCharacter 和 EnemyCharacter都继承自Character基类。具体的职业则进一步继承自阵营角色类。
```python
class Character:
def __init__(self, name, hp, attack_power, ...):
... 属性 ...
class PlayerCharacter(Character):
def __init__(self, name, hp, ...):
super().__init__(name, hp, ..., faction="player")
class Warrior(PlayerCharacter):
def __init__(self, level=1):
super().__init__(name="战士", hp=100 + (level-1)*15, ...)
self.image = pygame.image.load('assets/player/warrior.jpg').convert()
...
```
地图节点继承结构: 不同类型的地图节点都继承自一个通用的 Node 基类。
```python
class Node:
def __init__(self, x, y, node_type, level):
# ... 通用节点属性 ...
class BattleNode(Node):
def __init__(self, x, y, level, is_boss=False):
super().__init__(x, y, "battle", level)
self.is_boss = is_boss
# ...
```
5.3 游戏逻辑与状态管理 Game类
整个游戏的中枢,负责驱动游戏流程、管理所有游戏数据和状态。
class Game:
def __init__(self):
# 初始化玩家数据(角色、卡牌、金币)
self.player_characters = []
self.cards = []
self.gold = 100
#初始化游戏状态和关卡信息
self.game_state = GameState.START_MENU
self.current_level = 0
self.map_nodes = []
self.active_nodes = []
#战斗相关状态
self.player_battle_group = []
self.enemy_battle_group = []
self.player_turn = True
self.current_actor_index = 0
self.acted_in_turn = set() # 记录本回合已行动角色
#UI状态,如弹窗信息
self.message_box_info = None
5.4 图形用户界面 GUI类
负责所有的渲染任务。和Game类是分离的,本身不存储游戏的核心数据,它只在每次需要绘制时,从 Game对象中获取数据并将其可视化。
class GUI:
def __init__(self, screen):
self.screen = screen
# 加载字体和图像资源
def draw(self, game):
# 根据 game 对象中的 game_state,决定调用哪个绘制函数
if game.game_state == GameState.BATTLE:
self.draw_battle(game)
elif game.game_state == GameState.MAP:
self.draw_map(game)
#其他状态的绘制
# 如果有消息框,则绘制在最上层
if game.message_box_info:
self.draw_message_box(game.message_box_info)
5.5 主游戏循环
创建Game和 GUI的实例,然后进入一个无限循环。在循环中,它按顺序执行三项主要任务:处理事件、更新逻辑、绘制屏幕。
if __name__ == '__main__':
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Roguelike Game")
clock = pygame.time.Clock()
game = Game()
gui = GUI(screen)
running = True
while running:
# 1. 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
game.handle_event(event) # 将事件交给 Game 类处理
# 2. 更新游戏逻辑
game.update()
# 3. 绘制屏幕
gui.draw(game)
pygame.display.flip() # 更新整个屏幕
clock.tick(60) # 控制帧率
pygame.quit()
sys.exit()
6 核心类详细设计分析
6.1 Character类 - 战斗的基础
act方法是角色行为的封装。战斗逻辑不需要关心一个技能是单体还是群体,只需调用act方法,根据角色的target_type属性自动处理。
class Character:
# ... __init__ ...
def act(self, action_type, target, game):
# 如果是群体目标
if self.ability_target_type == 'multi' and action_type == 'special':
targets = game.enemy_battle_group if self.faction == 'player' else game.player_battle_group
self.special_ability(targets, game.enemy_battle_group, game.player_battle_group)
# 如果是单体目标
elif self.target_type == 'single' or (self.ability_target_type == 'single' and action_type == 'special'):
if action_type == 'attack':
self.attack(target)
elif action_type == 'heal':
self.heal(target)
elif action_type == 'special':
self.special_ability([target], game.enemy_battle_group, game.player_battle_group)
6.2 Game类
Game类通过状态机来控制游戏流程。update和handle_event方法会根据当前的game_state将任务分派给具体的处理函数。
逻辑更新分派:
```python
# in class Game:
def update(self):
if self.game_state == GameState.BATTLE:
self.update_battle()
# ... elif 其他状态 ...
```
事件处理分派:
```python
# in class Game:
def handle_event(self, event):
# 弹窗激活时,只处理弹窗事件
if self.message_box_info:
self.handle_message_box_event(event)
return
# 根据状态分派事件
if self.game_state == GameState.BATTLE:
self.handle_battle_event(event)
elif self.game_state == GameState.MAP:
self.handle_map_event(event)
# ... elif 其他状态 ...
```
地图生成逻辑: generate_map 方法体现了程序化内容生成的思想。它根据当前关卡self.current_level从配置中获取参数,动态创建节点布局。
```python
# in class Game:
def generate_map(self):
# ...
level_conf = self.level_config[self.current_level]
num_nodes = level_conf['nodes']
# ...
for i in range(num_nodes):
# ... 计算节点位置 ...
# ... 根据规则创建 BattleNode, ShopNode, EncounterNode ...
new_node = BattleNode(x, y, self.current_level)
self.map_nodes.append(new_node)
# ...
```
6.3 GUI类
动态布局和可复用组件。
动态战斗布局: draw_battle方法会根据当前战场上的人数,动态计算每个角色的坐标。
```python
# in class GUI:
def draw_battle(self, game):
# ...
# 动态计算玩家角色位置
player_count = len(game.player_battle_group)
player_spacing = 20
player_total_width = player_count * (char_width + player_spacing) - player_spacing
player_start_x = (SCREEN_WIDTH // 2 - player_total_width) // 2
for i, char in enumerate(game.player_battle_group):
x = player_start_x + i * (char_width + player_spacing)
y = 400
char.rect.topleft = (x, y)
# ... 绘制角色、血条等 ...
```
可重用UI组件: draw_message_box方法是一个完美的例子。游戏任何地方需要弹出消息,都可以调用这个函数,只需要提供消息内容和按钮信息,就能生成一个标准样式的弹窗,极大地提高了开发效率和UI统一性。
```python
# in class GUI:
def draw_message_box(self, message_info):
# 获取消息文本、按钮等信息
text = message_info['text']
buttons = message_info['buttons']
# 绘制弹窗背景
self.screen.blit(self.message_box_bg, (box_rect.x, box_rect.y))
# 绘制文本
self.draw_text(self.screen, text, self.font_medium, BLACK, box_rect.centerx, box_rect.centery - 30)
#遍历并绘制所有按钮
for i, btn_text in enumerate(buttons):
# 计算按钮位置
self.draw_button(btn_text, btn_rect, self.button_color, self.font_small, WHITE)
```
7 主要游戏流程分析
7.1 从地图到战斗:
玩家在地图上点击一个战斗节点。
```python
# in class Game:
def handle_map_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
for node in self.active_nodes:
if node.rect.collidepoint(event.pos):
# ...
if isinstance(node, BattleNode):
self.start_battle(node) # 触发战斗
# ...
break
```
7.2 战斗中的玩家回合:
玩家选择角色,然后选择敌人进行攻击。
```python
# in class Game:
def handle_battle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
# ...
# 如果已选择我方角色,再点击敌人
elif self.selected_character_index is not None and self.player_turn:
actor = self.player_battle_group[self.selected_character_index]
if actor in self.acted_in_turn: # 防止重复行动
return
for i, target in enumerate(self.enemy_battle_group):
if target.rect.collidepoint(event.pos) and target.hp > 0:
self.player_action(self.selected_character_index, 'attack', i) # 执行攻击动作
# ...
return
```
7.3 敌人回合:
玩家回合结束后,update_battle会自动触发敌人AI。
```python
# in class Game:
def update_battle(self):
if self.message_box_info:
return
# 检查战斗是否结束
if self.check_battle_end():
return
# 如果轮到敌人行动
if not self.player_turn:
# 使用定时器或简单的延迟来模拟敌人思考
if self.enemy_action_timer is None:
self.enemy_action_timer = pygame.time.get_ticks()
if pygame.time.get_ticks() - self.enemy_action_timer > 1000: # 延迟1秒行动
self.enemy_turn()
self.enemy_action_timer = None
def enemy_turn(self):
# 遍历敌人并执行AI
for actor in self.enemy_battle_group:
if actor.hp > 0:
actor.ai_action(self) # 调用敌人的AI方法
# 切换回玩家回合
```
7.4 战斗胜利:
check_battle_end检测到敌人全部倒下后,调用handle_battle_won。
```python
# in class Game:
def check_battle_end(self):
# ...
if all(enemy.hp <= 0 for enemy in self.enemy_battle_group):
self.handle_battle_won()
return True
# ...
return False
def handle_battle_won(self):
# 计算金币和经验值奖励
self.game_state = GameState.POST_BATTLE_REWARD # 进入战后奖励状态
# 设置弹窗,让玩家选择奖励
```
当然可以。在开发过程中,遇到问题是常态,而解决问题的过程正是学习和进步最快的方式。以下是在本次Roguelike游戏开发过程中遇到的几个典型问题及其解决方案的简述,可以作为你报告的一部分。
8 开发中遇到的主要问题及解决方案
开发过程中,我遇到了从环境配置到核心逻辑、再到用户体验等多个层面的挑战。解决这些问题的过程,极大地锻炼了我的调试能力和系统设计思维。以下是我遇到的主要问题及其解决方案的总结。
8.1 环境配置与库安装问题
问题描述:
项目启动初期,由于我本地的Python环境与标准的Windows环境存在差异,导致在命令行使用 pip install pygame 命令时,频繁遭遇SSL证书验证失败和平台架构不兼容的错误
解决方案:
在尝试了多种pip参数如更换镜像源、添加信任主机均未成功后,我意识到问题在于开发环境。最终的解决方案是下载了最新的开发环境。
8.2 中文显示问题
问题描述:
中文字符乱码:游戏中的角色名称、按钮文本、提示信息等所有中文内容,在屏幕上都显示为无法辨认的方块(乱码)。
解决方案:
加载指定中文字体:我分析出乱码的原因是pygame使用的默认字体不包含中文字符集。解决方法是:将支持中文的TrueType字体文件放置在项目根目录下。通过 self.font = pygame.font.Font("字体文件路径.TTF", size) 的方式加载。
8.3 逻辑问题
问题描述:
角色无限行动Bug:在战斗中,可以反复点击同一个我方角色对敌人进行攻击,使其在一回合内可以无限次行动。
解决方案:
增加回合内行动记录:在Game类中增加了一个set类型的变量 acted_in_turn。
在每个新回合开始时,将这个集合清空。
当一个角色完成行动后,就将其对象添加到这个集合中。
先检查被选中的角色是否已存在于acted_in_turn集合内。如果是,则忽略本次操作。
8.4 UI布局与交互体验问题
问题描述:
UI元素动态重叠:战斗界面最初使用固定的坐标来放置角色。当战场上的角色数量变化时角色们的图像、名字和血条会发生重叠。
内容溢出屏幕:随着游戏进程,地图变得越来越大,可选角色也越来越多,导致地图节点和角色选择列表的内容超出了屏幕边界,玩家无法看到或点击所有的选项。
解决方案:
实现动态布局算法:编写了一个动态布局算法。获取当前战场上所有角色的数量。根据角色图片的宽度和预设的间距,计算出容纳所有角色所需的总宽度。用屏幕宽度减去这个总宽度,再除以二,得到第一个角色的起始x坐标。通过循环,依次计算并放置每个角色。
实现鼠标拖动滚动:为地图和角色选择等界面实现了鼠标拖动滚动的功能。
Game类中增加一个scroll_x变量来记录水平滚动偏移量。
在事件处理循环中,监听MOUSEMOTION事件。如果事件发生时鼠标左键是按下的状态,就将鼠标在x轴上的相对位移event.rel[0]累加到scroll_x上。
在绘制这些界面的UI元素时,将它们原本的x坐标都减去当前的scroll_x值,这样就实现了视口的平滑移动,让所有内容都变得可以访问。
9 课程感想与体会
9.1 知识总结
这次大作业是一次对整个Python课程所学知识的全面回顾与深度实践。
整个项目基于OOP思想构建。我深刻体会到封装、继承和多态在构建复杂系统时的巨大优势。例如,所有角色都继承自一个基类,但通过多态实现了各自独特的AI和技能。熟练运用了列表来管理角色阵容,字典来存储配置和资源,以及计数器来高效统计卡牌数量。设计并实现了状态机算法来管理游戏流程,以及动态布局算法来解决UI重叠问题。这让我明白,编程不仅是写代码,更是设计解决方案。通过使用Pygame、random、math等模块,我体会到了Python强大生态带来的便利,学会了如何阅读文档并利用现成的工具来解决问题。
9.2 感想与体会
通过这次从零到一的开发经历,我收获良多。最大的感受是,实践是检验和巩固理论知识的最佳途径。很多在课堂上感觉已经理解的概念,只有在实际应用中遇到问题并解决后,才能真正地掌握。例如,对于类的继承,直到我需要设计不同行为模式的敌人AI时,才真正领会其设计的精妙之处。
其次,我学会了以迭代的方式推进项目。面对一个复杂的项目,试图一次性完美实现所有功能是不现实的。将大目标分解为小模块,先搭建骨架再逐步填充细节,这个过程让我始终保持着清晰的思路和前进的动力。
最后,我也深刻体会到了调试的重要性。一个微小的逻辑错误或拼写失误都可能导致程序崩溃。耐心和细致地分析错误、定位问题,并最终解决它,这个过程虽然痛苦,但完成后带来的成就感是无与伦比的。
课程最吸引我的地方在它并非枯燥地罗列知识点,而是通过一个个精心设计的项目实验,引导我们把理论付诸实践。点燃了我主动探索的热情。每次作业,我都不再满足于仅仅实现基础功能,而是开始审视设计的全局性,这种从程序员到软件设计者的视角转变,是我最大的收获。
Python语言本身的高效,让我可以将精力更多放在逻辑构建和创新实现。正因如此,我才有机会挑战并完成了这个凝聚了我诸多想法的Roguelike游戏。当最后一个bug解决,程序完美运行,那种从无到有、化想法为现实的创造快感,是任何言语都难以形容的。
王老师的教学生动有趣,课堂氛围总是轻松而高效。回望这学期,我收获的不仅是一门编程语言的技能,更重要的是一种解决问题的思维方式和一种对创造的持久热情。这段学习经历无疑为我未来的道路点亮了一盏明灯,我对此深怀感激。
总而言之,非常感谢老师在本学期的辛勤教学!这次大作业让我受益匪浅,它不仅是对我课程学习成果的一次检阅,更是对我未来继续在编程道路上探索的一次巨大激励。
浙公网安备 33010602011771号