Python 龙与魔法回合制2D游戏

Python 龙与魔法回合制游戏开发实战

📜 项目介绍

这是一款基于 Pygame 开发的回合制 RPG 游戏,玩家将扮演勇敢的冒险者,与强大的 BOSS 展开激烈对战。游戏融合了经典的回合制战斗机制,支持多种技能选择,包括物理攻击、魔法攻击、生命恢复、攻击增益和防御增益。

🎮 游戏特色

特性 描述
回合制战斗 策略性回合对战,体验经典 RPG 魅力
丰富技能系统 5种技能可选:攻击、魔法、治疗、增伤、增防
精美动画 完整的角色动画系统,包括待机、攻击、施法等状态
音效反馈 战斗音效、背景音乐,带来沉浸式体验
多元素 BOSS BOSS 拥有火、冰、土、风四种元素攻击

🛠 技术栈

python 复制代码
import pygame      # 游戏引擎
import sys         # 系统交互
import os          # 文件路径处理
import random      # 随机数生成

🎯 核心系统解析

1. 角色属性系统

python 复制代码
# 玩家角色属性字典
playerData = {
    'def': 50,        # 防御力
    'atk': 100,       # 攻击力
    'magic': 1000,    # 魔法伤害
    'addHP': 200,     # 生命恢复量
    'addAtk': 200,    # 攻击增益
    'addDef': 100,    # 防御增益
    'HP': 5000,       # 当前生命值
    'maxHP': 5000,    # 最大生命值
}

# BOSS 属性字典
bossData1 = {
    'def': 50,        # 防御力
    'ice': 100,       # 冰系伤害
    'earth': 200,     # 土系伤害
    'fire': 300,      # 火系伤害
    'wind': 400,      # 风系伤害
    'HP': 10000,      # BOSS 生命值
}

2. 伤害计算机制

python 复制代码
# 玩家技能计算
def playerSkill():
    if playerState == 'attack':
        # 物理攻击:攻击力 - 目标防御力
        damage = (playerData['atk'] - bossData1['def'])
        bossData1['HP'] -= damage
        
    elif playerState == 'magic':
        # 魔法攻击:魔法伤害 - 目标防御力
        damage = (playerData['magic'] - bossData1['def'])
        bossData1['HP'] -= damage
        
    elif playerState == 'addHP':
        # 生命恢复(不超过最大值)
        playerData['HP'] = playerData['HP'] + playerData['addHP']
        if playerData['HP'] > playerData['maxHP']:
            playerData['HP'] = playerData['maxHP']
            
    elif playerState == 'addDef':
        # 永久增加防御力
        playerData['def'] += playerData['addDef']
        
    elif playerState == 'addAtk':
        # 永久增加攻击力
        playerData['atk'] += playerData['addAtk']

3. 动画系统

游戏使用插入排序算法来确保动画帧按正确顺序播放:

python 复制代码
# 插入排序,保证动画帧按数字顺序排列
def insertSort(mylist):
    for i in range(1, len(mylist)):
        for j in range(i, 0, -1):
            if mylist[j] < mylist[j - 1]:
                mylist[j], mylist[j - 1] = mylist[j - 1], mylist[j]
            else:
                break

# 构建动画字典
def createDict(path):
    keys = os.listdir(path)
    gameDict = dict.fromkeys(keys)
    
    for key in keys:
        pictures = []
        names = os.listdir(path + key + '/')
        
        # 将文件名转换为整数以便排序
        for i in range(len(names)):
            names[i] = int(names[i].replace('.png', ''))
        insertSort(names)
        
        # 加载所有动画帧
        for name in names:
            img = pygame.image.load(path + key + '/' + str(name) + '.png')
            pictures.append(img)
        gameDict[key] = pictures
    return gameDict

4. 动画播放控制

python 复制代码
# 动画状态帧
player = 0
boss = 0

def showActor(dict, file, pos=(0, 0)):
    global player, boss
    
    if dict == playerDict:
        if player >= len(dict[file]):
            player = 0
        screen.blit(dict[file][player], pos)
        player += 1
    else:
        if boss >= len(dict[file]):
            boss = 0
        screen.blit(dict[file][boss], pos)
        boss += 1

5. 血条绘制系统

python 复制代码
def showHP(x, y, hp, name):
    if name == 'player':
        length = int(0.02 * hp)  # 玩家血条比例
    elif name == 'boss':
        length = int(0.01 * hp)  # BOSS 血条比例
    
    # 防止负值
    if length < 0:
        length = 0
        
    # 缩放血条图片
    bar = pygame.transform.scale(uiDict['HPbar'][0], (length, 13))
    screen.blit(bar, (x, y))

🎮 游戏流程控制

主循环架构

python 复制代码
while True:
    # 事件处理
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            sys.exit()
            
        # 键盘事件
        if e.type == pygame.KEYDOWN:
            if e.key == pygame.K_KP_ENTER or e.key == pygame.K_RETURN:
                state = 'running'  # 开始游戏
                
        # 鼠标点击技能选择
        if e.type == pygame.MOUSEBUTTONDOWN:
            x, y = e.pos[0], e.pos[1]
            if 450 < y < 490:  # 技能按钮区域
                if 270 < x < 340:
                    playerState = 'attack'; skill = 1
                elif 370 < x < 440:
                    playerState = 'magic'; skill = 2
                # ... 其他技能

游戏状态机

python 复制代码
state = 'loading'  # 初始状态:资源加载

# 游戏状态流转
# loading -> start -> running -> end

elif state == 'running':
    # 显示地图、血条、属性
    screen.blit(uiDict['map'][skill], (0, 0))
    showHP(86, 642, playerData['HP'], 'player')
    showHP(818, 642, bossData1['HP'], 'boss')
    
    # 玩家回合 / BOSS 回合并行处理
    if turn == 'player':
        showActor(playerDict, 'idle')  # 待机动画
        # ...

🎨 音效系统

python 复制代码
# 初始化音频系统
pygame.mixer.music.load("music/bg.mp3")
pygame.mixer.music.set_volume(0.3)
pygame.mixer.music.play(-1)  # 无限循环

# 攻击音效
attack1_fx = pygame.mixer.Sound("music/attack1.mp3")
attack1_fx.set_volume(0.9)

# 魔法音效
magic1_fx = pygame.mixer.Sound("music/magic1.mp3")
magic1_fx.set_volume(0.9)

# 游戏胜利/失败音效
gameover1_fx = pygame.mixer.Sound("music/gameover.wav")
gamewin1_fx = pygame.mixer.Sound("music/gamewin.wav")

📁 项目结构

复制代码
Python龙与魔法回合游戏/
├── mainText.py          # 🎯 主程序入口
├── UI/                  # 🖼️ 界面资源
│   ├── HPbar/           # 血条图片
│   ├── end/             # 结束画面
│   ├── map/             # 地图背景
│   └── start/           # 开始界面
├── animation/           # 🎬 角色动画
│   ├── boss/            # BOSS 动画(idle + 四元素攻击)
│   │   ├── idle/        # 待机
│   │   ├── fire/        # 火系
│   │   ├── ice/         # 冰系
│   │   ├── earth/       # 土系
│   │   └── wind/        # 风系
│   ├── player/          # 玩家动画
│   │   ├── idle/        # 待机
│   │   ├── attack/      # 物理攻击
│   │   ├── magic/       # 魔法攻击
│   │   ├── addHP/       # 治疗
│   │   ├── addAtk/      # 增伤
│   │   └── addDef/      # 增防
│   └── other/           # 其他素材(裁判)
└── music/               # 🔊 音效资源

🚀 运行方式

bash 复制代码
# 确保已安装 Pygame
pip install pygame

# 运行游戏
python mainText.py

🎮 操作说明

操作 功能
Enter 开始游戏
鼠标点击 选择技能
Space 确认技能

💡 开发心得

1. 模块化设计

将动画系统、技能系统、UI系统分离,使代码结构清晰易维护。

2. 资源预加载

使用加载画面提前加载所有资源,避免游戏过程中卡顿。

3. 状态机模式

使用 state 变量控制游戏流程,逻辑清晰易于扩展。

4. 帧率控制

使用 pygame.time.delay(100) 控制动画播放速度,避免画面过快。


🔮 未来扩展方向

  • 添加更多 BOSS 怪物
  • 实现玩家升级系统
  • 添加道具商店
  • 支持存档/读档功能
  • 增加更多技能树

🔠 核心代码

python 复制代码
import pygame
import sys
import os
import random
# 初始化及背景显示
pygame.init()
judge_new_list = []
gameobj = 'animation'
# 设置窗口尺寸大小
screen = pygame.display.set_mode((1000, 700))
# 加载设置游戏的裁判判官的图片
judge = pygame.image.load("./animation/other/0.png")
judge = pygame.transform.scale(judge, (200,150))
# 字体设置
# windows字体    fangsong        simhei       kaiti
# macOS字体      arialunicode    pingfang     songti
# 设置游戏中敌我属性文本的字体
font = pygame.font.SysFont('simhei', 20)
# 游戏的背景音乐 音量大小0.3 无限循环播放
pygame.mixer.music.load("music/bg.mp3")
pygame.mixer.music.set_volume(0.3)
pygame.mixer.music.play(-1)
# 设置物理攻击音效
attack1_fx = pygame.mixer.Sound("music/attack1.mp3")
attack1_fx.set_volume(0.9)
# 设置魔法攻击音效
magic1_fx = pygame.mixer.Sound("music/magic1.mp3")
magic1_fx.set_volume(0.9)
# 设置生命值回复音效
addhp1_fx = pygame.mixer.Sound("music/addhp1.mp3")
addhp1_fx.set_volume(0.9)
# 设置增伤音效
addatk1_fx = pygame.mixer.Sound("music/addatk1.mp3")
addatk1_fx.set_volume(0.9)
# 设置增伤音效
adddef1_fx = pygame.mixer.Sound("music/adddef1.mp3")
adddef1_fx.set_volume(0.9)
# 设置游戏失败结束音效
gameover1_fx = pygame.mixer.Sound("music/gameover.wav")
gameover1_fx.set_volume(0.9)
# 设置游戏胜利音效
gamewin1_fx = pygame.mixer.Sound("music/gamewin.wav")
gamewin1_fx.set_volume(0.9)
# 玩家角色属性字典,
playerData = {
    'def': 50,
    'atk': 100,
    'magic': 1000,
    'addHP': 200,
    'addAtk': 200,
    'addDef': 100,
    'HP': 5000,
    'maxHP': 5000,
}
# boss属性字典
bossData1 = {
    'def': 50,
    'ice': 100,
    'earth': 200,
    'fire': 300,
    'wind': 400,
    'HP': 10000,
}
def showJudge():
    # 显示旁观
    for i in judge_new_list:
        screen.blit(i, (290, 10))
        pygame.display.flip()
# 插入排序,使用os库的listdir获取其对应文件下的图片名称是,是无顺序的
def insertSort(mylist):
    for i in range(1, len(mylist)):
        for j in range(i, 0, -1):
            if mylist[j] < mylist[j - 1]:
                mylist[j], mylist[j - 1] = mylist[j - 1], mylist[j]
            else:
                break
# 构建动画字典,返回值是动画字典
def createDict(path):
    # 获取文件夹名字
    keys = os.listdir(path)  # print()列表
    # 以文件夹名字列表构建字典
    gameDict = dict.fromkeys(keys)  # print()空字典
    # 字典赋值
    for key in keys:
        pictures = []
        # 获取图片的名字(全名包括扩展名)
        names = os.listdir(path + key + '/')
        # 把图片变量存在picture列表中
        for i in range(len(names)):
            names[i] = int(names[i].replace('.png', ''))
        insertSort(names)
        for name in names:
            # 把.png拼上
            img = pygame.image.load(path + key + '/' + str(name) + '.png')  # convert可以去掉
            pictures.append(img)
        gameDict[key] = pictures
    return gameDict
# 绘制单独角色UI函数,角色字典名,坐标x,y
def showHP(x, y, hp, name):  # x,y固定值
    if name == 'player':
        length = int(0.02 * hp)
    elif name == 'boss':
        length = int(0.01 * hp)
    # 保证血条,不能为负值。
    if length < 0:
        length = 0
    # 缩放图片,参数:图片变量;缩放的像素大小
    bar = pygame.transform.scale(uiDict['HPbar'][0], (length, 13))
    screen.blit(bar, (x, y))

# 显示人物属性
offset = 0
def showData(d):
    global offset
    if d == playerData:
        x = 260
        gap = 21
    else:
        x = 700
        gap = 25
    for k, v in d.items():
        if k != 'maxHP':
            words = k + ':' + str(v)
            if d == playerData:
                text = font.render(words, True, (255,8,8))
            else:
                text = font.render(words, True, (5, 255,255))
            screen.blit(text, (x, 516 + offset * gap))
            offset += 1
    offset = 0

# 动画状态帧
player = 0
boss = 0
judge_index=0
def showActor(dict, file, pos=(0, 0)):
    global player, boss
    if dict == playerDict:
        if player >= len(dict[file]):
            player = 0
        screen.blit(dict[file][player], pos)
        player += 1
    else:
        if boss >= len(dict[file]):
            boss = 0
        screen.blit(dict[file][boss], pos)
        boss += 1

state = 'loading'
turn = 'player'
choose = 'doing'
skill = 0

# 玩家计算
def playerSkill():
    if playerState == 'attack':
        # 剩余生命计算
        damage = (playerData['atk'] - bossData1['def'])
        bossData1['HP'] -= damage
    elif playerState == 'magic':
        damage = (playerData['magic'] - bossData1['def'])
        bossData1['HP'] -= damage
    # 生命恢复
    elif playerState == 'addHP':
        playerData['HP'] = playerData['HP'] + playerData['addHP']
        if playerData['HP'] > playerData['maxHP']:
            playerData['HP'] = playerData['maxHP']
    elif playerState == 'addDef':
        playerData['def'] += playerData['addDef']
    elif playerState == 'addAtk':
        playerData['atk'] += playerData['addAtk']



# boss伤害
def bossSkill():
    damage = (bossData1[randomSkill] - playerData['def'])
    if damage > 0:
        playerData['HP'] -= damage


while True:
    # 事件获取
    for e in pygame.event.get():
        if e.type == pygame.QUIT:
            sys.exit()
        # 键盘按下事件
        if e.type == pygame.KEYDOWN:
            # 回车键进入游戏
            if e.key == pygame.K_KP_ENTER or e.key == pygame.K_RETURN:
                state = 'running'
            # 空格键选择完毕 触发动作及其音效
            if e.key == pygame.K_SPACE and skill == 1:
                choose = 'done';attack1_fx.play()
            elif e.key == pygame.K_SPACE and skill == 2:
                choose = 'done';magic1_fx.play()
            elif e.key == pygame.K_SPACE and skill == 3:
                choose = 'done';addhp1_fx.play()
            elif e.key == pygame.K_SPACE and skill == 4:
                choose = 'done';addatk1_fx.play()
            elif e.key == pygame.K_SPACE and skill == 5:
                choose = 'done';adddef1_fx.play()
        if choose == 'doing':
            if e.type == pygame.MOUSEBUTTONDOWN:
                x = e.pos[0]
                y = e.pos[1]
                if 450 < y < 490:  # 根据UI改
                    # y坐标判断
                    if 270 < x < 340:
                        playerState = 'attack'
                        skill = 1
                    elif 370 < x < 440:
                        playerState = 'magic'
                        skill = 2
                    elif 470 < x < 540:
                        playerState = 'addHP'
                        skill = 3
                    elif 570 < x < 640:
                        playerState = 'addAtk'
                        skill = 4
                    elif 670 < x < 740:
                        playerState = 'addDef'
                        skill = 5
    # 资源加载
    if state == 'loading':
        # 资源加载(更换图片)
        uiDict = createDict('UI/')
        screen.blit(uiDict['start'][0], (0, 0))
        pygame.display.update()

        playerDict = createDict(gameobj+'/player/')
        screen.blit(uiDict['start'][1], (0, 0))
        pygame.display.update()

        bossDict = createDict(gameobj+'/boss/')
        screen.blit(uiDict['start'][3], (0, 0))
        pygame.display.update()
        pygame.time.delay(500)
        state = 'start'
    # 游戏开始画面
    elif state == 'start':
        # 显示开始界面,按enter
        screen.blit(uiDict['start'][4], (0, 0))
    # 游戏进行状态
    elif state == 'running':
        # 绘制背景
        screen.blit(uiDict['map'][skill], (0, 0))
        # 显示面板UI
        showHP(86, 642, playerData['HP'], 'player')
        showHP(818, 642, bossData1['HP'], 'boss')
        # 显示属性
        showData(playerData)
        showData(bossData1)
        # 显示判官
        screen.blit(judge,(350,10))
        # 玩家回合
        if turn == 'player':
            # 技能选择
            if choose == 'doing':
                # 双方闲置动画
                showActor(playerDict, 'idle')
                showActor(bossDict, 'idle')
                showJudge()
            else:
                # BOSS为闲置动画
                showActor(bossDict, 'idle')
                showActor(playerDict, playerState)
                num = len(playerDict[playerState])
                if player >= num:
                    playerSkill()
                    randomSkill = random.choice(['ice', 'earth', 'fire', 'wind'])
                    turn = 'boss'
        # boss回合
        elif turn == 'boss':
            # player为闲置动画
            showActor(playerDict, 'idle')
            showActor(bossDict, randomSkill)
            num = len(bossDict[randomSkill])
            if boss >= num:
                bossSkill()
                choose = 'doing'
                turn = 'player'
            # 游戏结束状态,显示胜利或者失败
        if playerData['HP'] <= 0 or bossData1['HP'] <= 0:
            # 双方任意一方没血,结束游戏
            state = 'end'
    elif state == 'end':
        if playerData['HP'] <= 0:
            screen.blit(uiDict['end'][0], (0, 0))
            gameover1_fx.play()
        elif bossData1['HP'] <= 0:
            screen.blit(uiDict['end'][1], (0, 0))
            gamewin1_fx.play()
    # 全局更新
    pygame.display.update()
    pygame.time.delay(100)

☑ 最后附上项目截图












💬 欢迎在评论区留言交流!

📧 如需转载,请注明出处

相关推荐
t_hj11 分钟前
大模型微调
人工智能·python·深度学习
范范@43 分钟前
python基础-函数
开发语言·python
2301_803934611 小时前
MySQL 字段类型选择规范指南
jvm·数据库·python
yaoxin5211232 小时前
406. Java 文件操作基础 - 字符与二进制流
java·开发语言·python
一勺菠萝丶2 小时前
macOS 安装 Python 包报错:`externally-managed-environment` 怎么解决?
python
醒李3 小时前
盲人出行辅助系统原型
人工智能·python·目标检测
PILIPALAPENG3 小时前
第4周 Day 3:多 Agent 协作——让 Agent 们"组队干活"
前端·人工智能·python
Omics Pro4 小时前
填补蛋白质组深度学习预处理教学空白
人工智能·python·深度学习·plotly·numpy·pandas·scikit-learn
伽蓝_游戏4 小时前
第二章:深入 Unity 资源导入管线 (Asset Import Pipeline)
游戏·unity·c#·游戏引擎·游戏程序
万邦科技Lafite4 小时前
实战演练:利用京东API一键抓取商品详情
数据库·redis·python·缓存·开放api·淘宝开放平台