diff --git a/Rogue_Tetris_功能设计文档.md b/Rogue_Tetris_功能设计文档.md new file mode 100644 index 0000000..28d18e7 --- /dev/null +++ b/Rogue_Tetris_功能设计文档.md @@ -0,0 +1,624 @@ +# 《Rogue Tetris》功能设计文档 + +## 1. 项目概述 + +本项目基于经典俄罗斯方块玩法,加入类肉鸽成长系统。玩家通过消除方块获得积分与经验,达到一定条件后触发升级,从随机出现的强化选项中选择一项,形成不同流派与策略组合。 + +游戏核心循环为: + +```text +下落方块 → 摆放 → 消行 → 获得积分/经验 → 升级 → 选择强化 → 提升能力 → 继续挑战 +``` + +项目目标是在保留俄罗斯方块基础规则的同时,增强随机性、成长性和可重复游玩价值。 + +--- + +## 2. 基础玩法设计 + +### 2.1 棋盘设计 + +游戏区域采用标准俄罗斯方块棋盘: + +```cpp +const int BOARD_WIDTH = 10; +const int BOARD_HEIGHT = 20; +``` + +棋盘使用二维数组或 `vector>` 存储。 + +```cpp +int board[BOARD_HEIGHT][BOARD_WIDTH]; +``` + +其中: + +| 数值 | 含义 | +|---|---| +| 0 | 空格 | +| 1-7 | 不同类型的普通方块 | +| 8 | 特殊方块,例如爆破方块 | + +### 2.2 方块类型 + +游戏包含七种经典方块: + +| 类型 | 说明 | +|---|---| +| I | 长条方块 | +| O | 方形方块 | +| T | T 型方块 | +| S | S 型方块 | +| Z | Z 型方块 | +| J | J 型方块 | +| L | L 型方块 | + +### 2.3 玩家操作 + +| 按键 | 功能 | +|---|---| +| A / ← | 左移 | +| D / → | 右移 | +| W / ↑ | 旋转 | +| S / ↓ | 软降 | +| 空格 | 硬降 | +| C | 暂存方块 | +| P | 暂停 | + +--- + +## 3. 肉鸽成长系统设计 + +### 3.1 经验获取 + +玩家通过消行获得经验。 + +| 消除行数 | 获得经验 | +|---|---| +| 1 行 | 10 EXP | +| 2 行 | 25 EXP | +| 3 行 | 45 EXP | +| 4 行 | 80 EXP | + +### 3.2 升级规则 + +玩家经验达到当前等级需求后升级。 + +```cpp +requiredExp = 100 + level * 50; +``` + +升级时: + +1. 游戏暂停; +2. 随机生成若干强化选项; +3. 玩家选择其中一个; +4. 应用强化效果; +5. 游戏继续。 + +### 3.3 升级流程示例 + +```cpp +void levelUp() { + player.level++; + player.exp -= player.requiredExp; + player.requiredExp = 100 + player.level * 50; + + vector choices = upgradeSystem.getRandomUpgrades(player.upgradeChoiceCount); + int selected = showUpgradeChoices(choices); + upgradeSystem.applyUpgrade(choices[selected], player, board); +} +``` + +--- + +## 4. 强化系统设计 + +### 4.1 强化分类 + +| 类型 | 说明 | +|---|---| +| 得分强化 | 提高积分收益 | +| 操作强化 | 改善玩家操作体验 | +| 消行强化 | 增强消行效果 | +| 生存强化 | 降低游戏失败风险 | +| 特殊强化 | 改变游戏规则 | + +--- + +## 5. 强化内容设计 + +### 5.1 得分类强化 + +#### 1. 积分倍率 + +效果:所有得分提高 20%。 + +```cpp +player.scoreMultiplier += 0.2f; +``` + +可重复获得。 + +#### 2. 连击奖励 + +效果:连续消行时获得额外积分。 + +```cpp +player.comboBonus += 50; +``` + +当玩家连续多个方块都产生消行时,触发连击奖励。 + +#### 3. 四消强化 + +效果:一次消除 4 行时额外获得 300 分。 + +```cpp +if (linesCleared == 4) { + score += 300; +} +``` + +--- + +### 5.2 操作类强化 + +#### 4. 慢速下落 + +效果:方块自然下落速度降低 10%。 + +```cpp +player.fallSpeed *= 0.9f; +``` + +#### 5. 额外预览 + +效果:增加下一个方块预览数量。 + +```cpp +player.previewCount += 1; +``` + +#### 6. 暂存槽 + +效果:解锁 Hold 功能,允许玩家暂存当前方块。 + +```cpp +player.canHold = true; +``` + +--- + +### 5.3 消行类强化 + +#### 7. 清扫者 + +效果:每累计消除 10 行,自动清除最底部一行。 + +```cpp +if (player.totalLinesCleared % 10 == 0) { + board.clearBottomLine(); +} +``` + +#### 8. 爆破方块 + +效果:每隔一定数量的方块,生成一个特殊爆破方块。爆破方块落地后清除周围 3x3 区域。 + +```cpp +void explode(int x, int y) { + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + board[y + dy][x + dx] = 0; + } + } +} +``` + +#### 9. 经验强化 + +效果:消行获得经验提高 25%。 + +```cpp +player.expMultiplier += 0.25f; +``` + +--- + +### 5.4 生存类强化 + +#### 10. 最后一搏 + +效果:当玩家即将失败时,自动清除底部 3 行。每局只能触发一次。 + +```cpp +player.hasRevive = true; +``` + +#### 11. 减压 + +效果:每次升级后,自动清除当前最高的一行障碍。 + +```cpp +board.clearHighestOccupiedLine(); +``` + +#### 12. 稳定结构 + +效果:方块锁定后,有小概率自动填补附近空洞。 + +```cpp +if (rand() % 100 < 10) { + board.fillNearbyHole(); +} +``` + +--- + +### 5.5 特殊类强化 + +#### 13. 双倍成长 + +效果:之后每次升级出现 4 个选项,而不是 3 个。 + +```cpp +player.upgradeChoiceCount = 4; +``` + +#### 14. 赌徒 + +效果:选择强化时有概率获得双倍效果,也有概率无效果。 + +```cpp +int chance = rand() % 100; +if (chance < 30) { + applyUpgradeTwice(); +} else if (chance < 50) { + noEffect(); +} +``` + +#### 15. 方块改造 + +效果:降低某一种方块的出现概率,例如减少 Z 方块出现概率。 + +```cpp +blockWeight[BlockType::Z] -= 1; +``` + +--- + +## 6. 数据结构设计 + +### 6.1 方块结构 + +```cpp +struct Tetromino { + int type; + int rotation; + int x; + int y; + vector> shape; +}; +``` + +### 6.2 玩家状态 + +```cpp +struct PlayerStats { + int score = 0; + int level = 1; + int exp = 0; + int requiredExp = 100; + + int totalLinesCleared = 0; + int combo = 0; + int comboBonus = 0; + + float scoreMultiplier = 1.0f; + float expMultiplier = 1.0f; + float fallSpeed = 1.0f; + + bool canHold = false; + bool hasRevive = false; + + int previewCount = 1; + int upgradeChoiceCount = 3; +}; +``` + +### 6.3 强化结构 + +```cpp +enum class UpgradeType { + Score, + Control, + Clear, + Survival, + Special +}; + +struct Upgrade { + string name; + string description; + UpgradeType type; + int level; +}; +``` + +--- + +## 7. 计分系统设计 + +### 7.1 基础得分 + +| 消除行数 | 基础分 | +|---|---| +| 1 行 | 100 | +| 2 行 | 300 | +| 3 行 | 500 | +| 4 行 | 800 | + +### 7.2 得分计算 + +```cpp +int calculateScore(int linesCleared) { + int baseScore = 0; + + switch (linesCleared) { + case 1: baseScore = 100; break; + case 2: baseScore = 300; break; + case 3: baseScore = 500; break; + case 4: baseScore = 800; break; + default: return 0; + } + + int finalScore = static_cast(baseScore * player.scoreMultiplier); + + if (player.combo > 1) { + finalScore += player.combo * player.comboBonus; + } + + return finalScore; +} +``` + +--- + +## 8. 强化随机池设计 + +### 8.1 强化池初始化 + +```cpp +vector upgradePool; + +void initUpgradePool() { + upgradePool.push_back({ + "积分倍率", + "所有得分提高20%", + UpgradeType::Score, + 1 + }); + + upgradePool.push_back({ + "慢速下落", + "方块下落速度降低10%", + UpgradeType::Control, + 1 + }); + + upgradePool.push_back({ + "最后一搏", + "失败时自动清除底部3行,每局一次", + UpgradeType::Survival, + 1 + }); +} +``` + +### 8.2 随机抽取强化 + +```cpp +vector getRandomUpgrades(int count) { + vector result; + vector pool = upgradePool; + + random_shuffle(pool.begin(), pool.end()); + + for (int i = 0; i < count && i < pool.size(); i++) { + result.push_back(pool[i]); + } + + return result; +} +``` + +--- + +## 9. 主要类设计 + +### 9.1 Game 类 + +```cpp +class Game { +private: + Board board; + Tetromino currentBlock; + PlayerStats player; + UpgradeSystem upgradeSystem; + +public: + void init(); + void update(); + void render(); + void handleInput(); + void checkGameOver(); +}; +``` + +### 9.2 Board 类 + +```cpp +class Board { +private: + int grid[20][10]; + +public: + bool checkCollision(Tetromino block); + void lockBlock(Tetromino block); + int clearLines(); + void clearBottomLine(); + void clearHighestOccupiedLine(); + bool isGameOver(); +}; +``` + +### 9.3 UpgradeSystem 类 + +```cpp +class UpgradeSystem { +private: + vector upgradePool; + +public: + void init(); + vector getRandomUpgrades(int count); + void applyUpgrade(Upgrade upgrade, PlayerStats& player, Board& board); +}; +``` + +--- + +## 10. 游戏主流程 + +```text +初始化游戏数据 +↓ +生成当前方块和预览方块 +↓ +进入游戏主循环 +↓ +读取玩家输入 +↓ +更新方块位置 +↓ +检测碰撞 +↓ +无法下落时锁定方块 +↓ +检测并消除完整行 +↓ +计算得分和经验 +↓ +判断是否升级 +↓ +若升级,进入强化选择界面 +↓ +应用强化效果 +↓ +继续游戏 +↓ +判断是否失败 +↓ +游戏结束,显示最终得分 +``` + +--- + +## 11. 界面设计 + +### 11.1 主界面示意 + +```text ++--------------------+----------------+ +| | Score: 1200 | +| | Level: 3 | +| | EXP: 40/250 | +| | | +| 游戏棋盘 | Next: | +| | [T] [L] [I] | +| | | +| | Upgrades: | +| | 积分倍率 Lv2 | ++--------------------+----------------+ +``` + +### 11.2 升级界面示意 + +```text +请选择一个强化: + +[1] 积分倍率 +所有得分提高20% + +[2] 慢速下落 +方块下落速度降低10% + +[3] 最后一搏 +失败时自动清除底部3行,每局一次 +``` + +--- + +## 12. 模块划分 + +| 模块 | 功能 | +|---|---| +| Game | 控制游戏主循环 | +| Board | 管理棋盘数据 | +| Tetromino | 管理方块形状、旋转、移动 | +| Renderer | 负责界面绘制 | +| InputManager | 处理键盘输入 | +| ScoreSystem | 计算分数与经验 | +| UpgradeSystem | 管理强化池、升级、强化应用 | +| PlayerStats | 保存玩家成长数据 | + +--- + +## 13. 开发优先级 + +| 优先级 | 内容 | +|---|---| +| P0 | 实现基础俄罗斯方块逻辑 | +| P0 | 实现消行和计分 | +| P1 | 实现经验和升级系统 | +| P1 | 实现随机强化三选一 | +| P1 | 实现 5 到 8 个基础强化 | +| P2 | 实现特殊方块,例如爆破方块 | +| P2 | 实现 Hold、预览数量等操作强化 | +| P3 | 增加稀有度、成就、存档等扩展内容 | + +--- + +## 14. 技术实现建议 + +| 技术 | 用途 | +|---|---| +| C++ | 主体开发语言 | +| STL vector | 存储棋盘、方块、强化池 | +| enum class | 表示方块类型和强化类型 | +| class | 进行模块化封装 | +| random | 实现随机强化和随机方块 | +| SFML / SDL / Qt | 可选,用于图形界面 | + +如果课程要求较基础,可以先完成控制台版本,再根据时间增加图形界面。 + +--- + +## 15. 创新点总结 + +本项目的主要创新点包括: + +1. 在传统俄罗斯方块中加入经验和等级系统; +2. 将消行行为与角色成长绑定; +3. 每次升级随机提供强化选项; +4. 强化之间可以叠加,形成不同构筑路线; +5. 加入生存类和特殊类强化,提高策略空间; +6. 每局强化组合不同,提升重复游玩价值。 + +--- + +## 16. 结论 + +《Rogue Tetris》在经典俄罗斯方块的基础上引入类肉鸽升级机制,使游戏从单纯的消行挑战扩展为具有成长路线、随机选择和策略构筑的玩法。玩家每局都能通过不同强化组合形成不同体验,既保留了俄罗斯方块的核心规则,也体现了创新性,适合作为 C++ 课程大作业项目。 diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..1d52172 --- /dev/null +++ b/TODO.md @@ -0,0 +1,185 @@ +# Rogue Tetris TODO List + +说明:本清单基于 [Rogue_Tetris_功能设计文档](./Rogue_Tetris_功能设计文档.md) 整理,后续实现严格遵守以下约束: + +- [ ] 不使用 `class`、继承、多态等面向对象特性 +- [ ] 以 `struct + 全局状态 + 纯函数/过程函数 + 模块化源文件` 组织代码 +- [ ] 强化选择界面固定为“三选一”三个选项框,视觉参考《吸血鬼幸存者》 +- [ ] 美术、图标、特效、音频先全部使用占位符资源 + +## 阶段 0:重构准备与技术基线 + +目标:在不破坏现有可运行版本的前提下,为肉鸽系统预留结构。 + +- [ ] 盘点当前全局变量、输入流程、计时器流程、渲染流程 +- [ ] 明确哪些旧逻辑保留,哪些逻辑需要拆分成独立过程函数 +- [ ] 设计新的状态拆分方案:游戏主状态、棋盘状态、当前方块状态、玩家成长状态、强化状态、UI 状态 +- [ ] 统一命名风格,避免后续继续扩散 `type/state/nType` 这类语义偏弱命名 +- [ ] 明确“升级中暂停”和“普通暂停”是两个不同状态 +- [ ] 确定占位符资源策略:纯色块、边框、占位图标、占位按钮 + +完成标准: + +- [ ] 写出核心数据结构草案 +- [ ] 写出模块拆分草案 +- [ ] 当前版本仍可正常编译运行 + +## 阶段 1:核心数据结构改造 + +目标:把当前普通俄罗斯方块状态扩展成可承载 Rogue 系统的数据。 + +- [ ] 新增 `GameState` 相关结构,记录运行态、暂停态、升级选择态、结算态 +- [ ] 新增 `PieceState` 结构,管理当前方块、下一方块、目标落点、是否可 Hold +- [ ] 新增 `PlayerStats` 结构,包含分数、等级、经验、所需经验、连击、总消行数 +- [ ] 新增 `UpgradeEntry` 结构,包含 `id`、名称、描述、分类、当前层数、是否可重复 +- [ ] 新增 `UpgradeRuntime` 结构,保存已获得强化和运行时效果值 +- [ ] 新增 `UiState` 结构,保存当前三个升级选项、当前高亮项、弹窗可见性 +- [ ] 将现有散落的全局变量逐步收敛到几个结构体中 +- [ ] 保留纯过程式调用,不引入成员函数 + +完成标准: + +- [ ] 代码中已能通过结构体访问大部分核心状态 +- [ ] 不再继续新增零散全局变量 + +## 阶段 2:分数、经验、等级主循环 + +目标:把“消行 -> 得分 -> 获得经验 -> 触发升级”接入当前主循环。 + +- [ ] 重写消行结算函数,返回本次消除行数 +- [ ] 按文档接入基础得分:1/2/3/4 行分别对应不同分值 +- [ ] 按文档接入经验收益:1/2/3/4 行分别给予不同 EXP +- [ ] 接入倍率计算:分数倍率、经验倍率、四消奖励、连击奖励 +- [ ] 新增升级判定函数,支持一次结算后连续升多级 +- [ ] 升级触发时停止下落计时与普通输入 +- [ ] 在右侧信息面板显示等级、EXP、所需 EXP、当前已获得强化摘要 + +完成标准: + +- [ ] 消行后可以稳定增加分数和经验 +- [ ] 达到阈值后必定进入升级选择状态 + +## 阶段 3:强化池与三选一系统 + +目标:先完成最关键的 Rogue 核心体验。 + +- [ ] 建立强化池初始化函数,不使用类,采用静态数组或 `std::vector` +- [ ] 先实现一批 P1 基础强化,优先选 6 个左右最稳定的: +- [ ] `score_multiplier` +- [ ] `combo_bonus` +- [ ] `slow_fall` +- [ ] `preview_plus_one` +- [ ] `exp_multiplier` +- [ ] `last_chance` +- [ ] 随机抽取 3 个不重复选项,避免当前局内明显无效选项 +- [ ] 支持重复强化的层数叠加 +- [ ] 选中后立即应用效果并返回游戏 +- [ ] 为后续强化扩展预留 `applyUpgradeById()` 分发函数 + +完成标准: + +- [ ] 每次升级都稳定弹出三个选项框 +- [ ] 选择任一选项后效果立即生效 + +## 阶段 4:升级界面 UI + +目标:实现参考《吸血鬼幸存者》的三选一视觉结构,先用占位表现。 + +- [ ] 增加全屏或半透明遮罩,压暗游戏场景 +- [ ] 中央显示三个横向或纵向排列的选项框 +- [ ] 每个选项框至少包含:占位图标、强化名、强化说明、强化分类、当前层数 +- [ ] 支持键盘选择:`A/D` 或方向键切换,`Enter/Space` 确认 +- [ ] 高亮态、选中态、禁用态视觉区分明确 +- [ ] 保证三个框尺寸一致、布局稳定,不因文本长度错位 +- [ ] 右侧现有信息面板在升级状态下保留或弱化显示,但不能抢焦点 +- [ ] 占位资源统一放入 `assets`,命名先按 `placeholder_*` + +完成标准: + +- [ ] 升级界面可以独立显示 +- [ ] 三个选项框可操作、可确认、可关闭 +- [ ] 视觉结构已经接近目标形式,后续只需替换资源 + +## 阶段 5:玩法强化第一轮 + +目标:把最影响体验的几个强化真正接入玩法。 + +- [ ] 分数倍率:影响所有结算得分 +- [ ] 连击奖励:连续多次成功消行追加奖励 +- [ ] 慢速下落:降低自然下落速度 +- [ ] 额外预览:从 1 个 Next 扩展到 2~3 个 +- [ ] EXP 强化:提升经验获取倍率 +- [ ] 最后一搏:失败前自动清除底部 3 行,仅触发一次 + +完成标准: + +- [ ] 每个强化都能在实际对局中观察到效果 +- [ ] 强化效果叠加后不会引发明显逻辑冲突 + +## 阶段 6:特殊机制第二轮 + +目标:在主体验稳定后再上特殊规则,避免一开始把复杂度拉满。 + +- [ ] Hold 功能改造成可解锁能力,而不是默认启用 +- [ ] 扩展更多预览块显示 +- [ ] 清扫者:累计消行触发底部清除 +- [ ] 爆破方块:生成特殊方块并实现 3x3 清除 +- [ ] 减压:升级后清除最高占用行 +- [ ] 稳定结构:小概率自动填补邻近空洞 +- [ ] 双倍成长 / 赌徒 / 方块改运等特殊强化 + +完成标准: + +- [ ] 至少 2 个特殊机制能稳定跑通 +- [ ] 不会明显破坏基础俄罗斯方块节奏 + +## 阶段 7:界面补全与反馈 + +目标:让系统信息足够清楚,不靠猜测理解机制。 + +- [ ] 右侧面板补充等级、EXP 条、强化列表、当前倍率 +- [ ] 增加本次结算浮动提示:`+Score`、`+EXP`、`Level Up` +- [ ] 为特殊强化增加简短提示文案 +- [ ] 增加暂停、升级、失败三种不同遮罩样式 +- [ ] 增加占位图标、占位按钮、占位边框资源 +- [ ] 检查中文排版、字号、留白、长文本换行 + +完成标准: + +- [ ] 玩家能看懂自己为什么升级、为什么得分变化 + +## 阶段 8:平衡与稳定性 + +目标:把版本从“能跑”拉到“能玩”。 + +- [ ] 调整等级需求曲线 +- [ ] 调整强化出现权重,避免过强或无效强化频繁出现 +- [ ] 调整下落速度、经验速度、升级频率 +- [ ] 修复升级状态下的非法输入问题 +- [ ] 修复连续升级、失败复活、爆破消行等边界问题 +- [ ] 增加最基础回归测试清单 + +回归检查: + +- [ ] 开局正常生成方块 +- [ ] 消行得分与经验正确 +- [ ] 升级必出 3 个选项框 +- [ ] 选择后恢复游戏且不丢状态 +- [ ] 复活、暂停、失败、重开互不冲突 + +## 建议实现顺序 + +1. 先做阶段 1 和阶段 2,把“经验升级”主循环打通。 +2. 再做阶段 3 和阶段 4,优先完成“三选一选项框”体验。 +3. 然后做阶段 5,把 6 个基础强化真正接上。 +4. 最后做阶段 6 到阶段 8,补特殊机制、表现和调平衡。 + +## 当前最近一轮执行清单 + +- [ ] 建立新的状态结构体与头文件声明 +- [ ] 重写消行结算返回值 +- [ ] 加入 `PlayerStats` +- [ ] 加入经验与升级判定 +- [ ] 加入升级状态切换 +- [ ] 做出三个升级选项框的占位 UI +- [ ] 接入第一批 6 个基础强化 diff --git a/src/source/Tetris.cpp b/src/source/Tetris.cpp index e69c96d..ff1c731 100644 --- a/src/source/Tetris.cpp +++ b/src/source/Tetris.cpp @@ -91,7 +91,7 @@ ATOM MyRegisterClass(HINSTANCE hInstance) wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TETRIS)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wcex.lpszMenuName = MAKEINTRESOURCE(IDC_TETRIS); + wcex.lpszMenuName = nullptr; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SMALL)); @@ -111,7 +111,7 @@ ATOM MyRegisterClass(HINSTANCE hInstance) BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { RECT rect = { 0, 0, WINDOW_CLIENT_WIDTH, WINDOW_CLIENT_HEIGHT }; - AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, TRUE); + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); hInst = hInstance; diff --git a/src/source/TetrisRender.cpp b/src/source/TetrisRender.cpp index 23b97e0..803daff 100644 --- a/src/source/TetrisRender.cpp +++ b/src/source/TetrisRender.cpp @@ -30,7 +30,7 @@ void TDrawScreen(HDC hdc, HWND hWnd) int layoutWidth = MulDiv(WINDOW_CLIENT_WIDTH, scale, 1000); int layoutHeight = MulDiv(WINDOW_CLIENT_HEIGHT, scale, 1000); int offsetX = (clientWidth - layoutWidth) / 2; - int offsetY = (clientHeight - layoutHeight) / 2; + int offsetY = 0; auto SX = [offsetX, scale](int value) -> int {