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解决,程序完美运行,那种从无到有、化想法为现实的创造快感,是任何言语都难以形容的。
王老师的教学生动有趣,课堂氛围总是轻松而高效。回望这学期,我收获的不仅是一门编程语言的技能,更重要的是一种解决问题的思维方式和一种对创造的持久热情。这段学习经历无疑为我未来的道路点亮了一盏明灯,我对此深怀感激。
总而言之,非常感谢老师在本学期的辛勤教学!这次大作业让我受益匪浅,它不仅是对我课程学习成果的一次检阅,更是对我未来继续在编程道路上探索的一次巨大激励。

posted @ 2025-06-10 18:06  望舒、  阅读(82)  评论(0)    收藏  举报