chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

@@ -0,0 +1,269 @@
# 00 · 设计规范与符号约定
> **所属文档集** [← 返回索引](./README.md)
> **用途**:阅读本文档集前的必读指南,定义所有文档统一使用的符号、术语和格式约定。
---
## 目录
1. [文档定位原则](#1-文档定位原则)
2. [抽象数据类型](#2-抽象数据类型)
3. [数据模型符号](#3-数据模型符号)
4. [接口契约符号](#4-接口契约符号)
5. [状态机符号](#5-状态机符号)
6. [事件符号](#6-事件符号)
7. [系统依赖符号](#7-系统依赖符号)
8. [参数表格式](#8-参数表格式)
9. [设计决策标注](#9-设计决策标注)
---
## 1. 文档定位原则
### 1.1 本文档集的边界
| ✅ 应包含 | ❌ 不应包含 |
|----------|-----------|
| 系统的功能目标(做什么)| 特定引擎的组件名称MonoBehaviour、Node 等)|
| 数据模型与字段语义 | 特定语言的代码语法 |
| 系统间的通信契约 | 特定插件/库的调用方式 |
| 状态机的状态与转换规则 | 文件系统路径、资产命名规范 |
| 行为触发的条件与结果 | 引擎内部执行顺序Update、FixedUpdate|
| 配置参数的含义与推荐范围 | Inspector 字段序列化细节 |
| 设计决策的原因Why| 代码模块的命名空间划分 |
### 1.2 阅读层次
```
本文档集DesignSpec
↓ 解释"做什么、为什么"
Docs/Architecture引擎架构文档
↓ 解释"代码如何组织、各模块如何实现"
实际代码
↓ 最终实现
```
---
## 2. 抽象数据类型
本文档集使用以下引擎无关的抽象类型:
| 符号 | 含义 | 示例 |
|------|------|------|
| `Integer` | 整数 | `hp: Integer` |
| `Number` | 浮点数 | `speed: Number` |
| `Boolean` | 布尔值 | `isGrounded: Boolean` |
| `String` | 字符串 | `id: String` |
| `ID` | 唯一标识符(字符串或枚举)| `formId: ID` |
| `List<T>` | 有序列表 | `skills: List<SkillData>` |
| `Map<K,V>` | 键值映射 | `flagMap: Map<String, Boolean>` |
| `Optional<T>` | 可为空的值 | `target: Optional<Entity>` |
| `Ref<T>` | 对另一数据模型的引用 | `weapon: Ref<WeaponData>` |
| `Enum` | 枚举类型(正文中明确列出所有值)| `DamageType` |
| `Flags` | 可组合的位标记集 | `DamageFlags` |
| `Duration` | 时间长度(秒)| `stunDuration: Duration` |
| `Vector2` | 二维方向或坐标 | `knockbackDir: Vector2` |
---
## 3. 数据模型符号
数据模型描述一组字段的结构,**不含逻辑**,类似数据表或结构体:
```
DataModel ModelName {
fieldName : Type // 字段说明
fieldName : Type = default // 带默认值
fieldName : Optional<Type> // 可选字段
──────────────────────────────── // 分隔线表示分组
groupedField : Type // 同组字段
}
```
### 示例
```
DataModel DamagePacket {
rawDamage : Integer // 基础伤害
damageType : DamageType // 伤害类型枚举
knockbackDir : Vector2 // 击退方向(单位向量)
knockbackForce : Number // 击退力度
stunDuration : Duration // 硬直时长(秒)
flags : Flags<DamageFlags>
──────────────────────────────────
sourceId : ID // 攻击来源的唯一标识(用于特殊规则查询)
sourcePosition : Vector2 // 攻击来源位置(计算击退方向时备用)
}
```
---
## 4. 接口契约符号
接口描述一个系统对外暴露的操作与属性,不规定内部实现:
```
Interface IInterfaceName {
// 只读属性
[readonly] propertyName : Type
// 可写属性
[readwrite] propertyName : Type
// 方法:方法名(参数名: 类型, …) → 返回类型
// 若无返回值写 void
methodName(param: Type) → ReturnType
// 可失败的方法(返回 Result
methodName(param: Type) → Result<ReturnType>
// 事件(外部可订阅)
[event] onSomething : EventType
}
```
### 示例
```
Interface IHealthOwner {
[readonly] currentHP : Integer
[readonly] maxHP : Integer
[readonly] isAlive : Boolean
takeDamage(packet: DamagePacket) → void
heal(amount: Integer) → void
[event] onHPChanged : Event<HPChangedPayload>
[event] onDied : Event<void>
}
```
---
## 5. 状态机符号
### 5.1 状态定义
```
State StateName {
priority : Integer // 数字越大、越难被低优先级状态打断
interruptBy: List<State> // 可被哪些状态强制打断
}
```
### 5.2 状态转换图
```
[StateA]
──条件──► [StateB] // 正常转换
══强制══► [StateC] // 强制(忽略优先级)
~~超时~~► [StateD] // 超时自动转换
[StateB]
──落地────────────► [IdleState]
──攻击输入─────────► [AttackState]
```
### 5.3 转换条件格式
| 符号 | 含义 |
|------|------|
| `──条件──►` | 满足条件后转换 |
| `══强制══►` | 外部强制调用,跳过优先级检查 |
| `~~超时~~►` | 经过 N 秒后自动转换 |
| `[condition AND condition]` | 复合条件 |
| `[NOT condition]` | 条件取反 |
---
## 6. 事件符号
事件用于系统间的解耦通知,只传递最小必要数据:
```
Event EventName {
payload: PayloadType // 携带的数据void 表示无数据
emitter: SystemName // 哪个系统发出
listeners: [SystemA, SystemB, ...] // 典型订阅方(非强制)
}
```
### 事件目录格式
| 事件名 | 载荷类型 | 发出方 | 用途 |
|--------|---------|--------|------|
| `OnPlayerDied` | `void` | 玩家系统 | 触发复活流程 |
| `OnHPChanged` | `{current, max}` | 玩家系统 | 更新 HUD |
---
## 7. 系统依赖符号
系统之间的依赖关系通过以下格式表达:
```
SystemA ──读取──► DataModel // 读取数据
SystemA ──写入──► DataModel // 写入数据
SystemA ──发出──► EventX // 发出事件
SystemA ◄─订阅── EventY // 订阅事件
SystemA ──调用──► IInterface // 调用接口方法
系统间不应出现:
SystemA ──直接引用──► SystemB // ❌ 禁止直接持有另一系统引用
```
### 依赖等级
| 等级 | 说明 |
|------|------|
| **强依赖**(编译时)| A 的存在必须依赖 B 的接口定义 |
| **弱依赖**(运行时事件)| A 通过事件通知 BB 不存在时 A 仍可正常运行 |
| **数据依赖**(只读配置)| A 读取共享配置数据,无运行时交互 |
---
## 8. 参数表格式
配置参数表统一格式:
| 参数名 | 类型 | 推荐值 | 说明 | 调整影响 |
|--------|------|--------|------|---------|
| `walkSpeed` | Number | 6.0 | 地面行走速度(单位/秒)| 越高越灵活,越低越沉重 |
| `jumpForce` | Number | 16.0 | 跳跃初速度 | 越高跳跃越高,需同步调整重力 |
> **调整原则**:每次只修改一个参数,实测手感后确认。参数间存在联动关系时,文档中注明。
---
## 9. 设计决策标注
文档中的设计决策用以下格式标注原因,方便日后回溯:
```
> **设计决策**[决策内容]
> **原因**[为什么这样设计]
> **替代方案**[考虑过但未选用的方案及原因]
```
### 示例
> **设计决策**:弹反成功后增加 +33 灵力,而非固定值
> **原因**:鼓励玩家主动弹反,弹反本身是高风险操作,大幅奖励使弹反策略具有可行的资源通路
> **替代方案**:弹反+0 灵力(纯战斗技巧),但测试发现玩家没有足够动机学习弹反
---
## 附:文档写作检查清单
在向本文档集新增或修改文档时,请确认:
- [ ] 所有数据类型使用抽象类型(不出现 `int`, `float`, `string`, `MonoBehaviour` 等)
- [ ] 所有引擎功能通过行为描述替代("物理检测地面"而非"OverlapBox"
- [ ] 系统接口以契约格式定义,不包含实现细节
- [ ] 参数表包含"调整影响"列
- [ ] 重要设计决策附有原因说明
- [ ] 文档末尾无悬空的"TODO"或临时注记

View File

@@ -0,0 +1,278 @@
# 01 · 系统架构总览
> **所属文档集** [← 返回索引](./README.md)
> **摘要**:定义游戏的核心设计哲学、系统全景图、模块边界与系统间通信规范。
---
## 目录
1. [设计哲学](#1-设计哲学)
2. [系统全景图](#2-系统全景图)
3. [系统分层模型](#3-系统分层模型)
4. [通信模式规范](#4-通信模式规范)
5. [数据层与逻辑层分离](#5-数据层与逻辑层分离)
6. [模块边界原则](#6-模块边界原则)
7. [系统优先级与生命周期](#7-系统优先级与生命周期)
---
## 1. 设计哲学
### 1.1 四大核心原则
| 原则 | 含义 | 实践方式 |
|------|------|---------|
| **零耦合** | 系统间不持有彼此的直接引用 | 通过事件频道或统一接口通信 |
| **数据驱动** | 行为由配置数据决定,而非硬编码 | 参数外置为可配置数据模型 |
| **行为契约** | 系统对外只暴露接口,不暴露实现 | 所有跨系统调用通过接口进行 |
| **单一职责** | 每个系统只负责一件事 | 系统边界清晰,不兼管相邻职责 |
### 1.2 设计动词
理解游戏整体架构的关键词汇:
- **探索**:玩家自由穿越相互连接的房间,逐渐解锁新区域
- **弹反**:高风险高回报的格挡反击,是核心战斗循环的差异化机制
- **形态切换**:三种形态提供不同战斗风格,玩家选择适合当前情况的形态
- **资源循环**:灵力(攻击积累)→ 技能消耗 → 再攻击积累,形成持续循环
---
## 2. 系统全景图
```
┌─────────────────────────────────────────────────────────────────────┐
│ 输入层Input Layer
│ 处理原始输入 → 转化为游戏动作事件 → 缓冲管理 │
└──────────────────────────────┬──────────────────────────────────────┘
│ 输入动作事件
┌──────────────────────────────▼──────────────────────────────────────┐
│ 玩家层Player Layer
│ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ 移动系统 │ │ 战斗系统 │ │ 形态系统 │ │ 资源系统 │ │
│ │(Movement) │ │ (Combat) │ │ (Form) │ │(Resource)│ │
│ └──────────────┘ └─────────────┘ └─────────────┘ └──────────┘ │
└──────────────────────────────┬──────────────────────────────────────┘
│ 玩家状态事件
┌──────────────────────────────▼──────────────────────────────────────┐
│ 世界层World Layer
│ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 敌人系统 │ │ 世界系统 │ │ 进程系统 │ │
│ │ (Enemy) │ │ (World) │ │(Progression)│ │
│ └──────────────┘ └─────────────┘ └─────────────┘ │
└──────────────────────────────┬──────────────────────────────────────┘
│ 世界状态事件
┌──────────────────────────────▼──────────────────────────────────────┐
│ 支撑层Support Layer
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ UI 系统 │ │ 音频系统 │ │ 存档系统 │ │ 叙事系统 │ │
│ │ (UI) │ │ (Audio) │ │ (Save) │ │(Narrative│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 经济系统 │ │ 反馈系统 │ │
│ │(Economy) │ │(Feedback)│ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 3. 系统分层模型
### 层级定义
| 层级 | 名称 | 职责 | 依赖方向 |
|------|------|------|---------|
| L0 | **配置层** | 只读的设计数据(参数表、模板)| 被所有层引用,不依赖任何层 |
| L1 | **输入层** | 原始输入 → 标准化动作事件 | 依赖 L0 |
| L2 | **玩家层** | 玩家实体的所有行为 | 依赖 L0, L1 |
| L3 | **世界层** | 敌人、关卡、进程 | 依赖 L0, L2通过接口|
| L4 | **支撑层** | UI、音频、存档、叙事 | 订阅 L2, L3 的事件 |
> **设计决策**高层系统L4只通过事件订阅低层系统L2、L3不直接调用。
> **原因**UI 系统永远不应"告诉"玩家系统去做什么,只需观察状态变化后更新显示。
---
## 4. 通信模式规范
系统间通信允许三种方式,按优先级排序:
### 4.1 事件频道(优先使用)
```
发出方 接收方
──────── ────────
系统 A 系统 B
└─ 发出 Event<Payload> └─ 订阅 Event<Payload>
└─ 执行响应逻辑
```
**规则**
- 发出方不知道谁在监听
- 接收方不知道谁发出了事件
- 事件只传递"发生了什么",不传递"如何处理"的指令
- 单次事件One-shot不轮询
### 4.2 接口调用(功能依赖)
```
调用方 被调用方
──────── ────────
系统 A 实现了 IInterface 的系统 B
└─ 持有 IInterface 引用 └─ 提供 IInterface 实现
└─ 调用 IInterface.Method()
```
**规则**
- 调用方只知道接口,不知道实现
- 接口定义在公共层,不属于任何具体系统
- 允许同步调用并获取返回值
### 4.3 共享数据读取(只读访问)
```
消费方 数据源
──────── ────────
系统 A 配置数据 / 运行时状态数据
└─ 只读访问共享数据 └─ 数据由其所有者系统维护
```
**规则**
- 消费方只读,不写
- 写入权唯一归属于数据的所有系统
- 其他系统通过事件订阅感知数据变化,不轮询
### 4.4 禁止的通信方式
| 禁止模式 | 原因 |
|----------|------|
| 系统 A 直接持有系统 B 的具体引用 | 产生双向耦合,修改 B 影响 A |
| 系统 A 轮询系统 B 的状态 | 性能浪费,职责不清 |
| 事件携带"命令"语义("去做 X"| 事件只通知,不指令 |
| 支撑层系统调用玩家/世界层方法 | 违反依赖方向UI 不应控制游戏逻辑 |
---
## 5. 数据层与逻辑层分离
### 5.1 两类数据
| 类型 | 说明 | 生命周期 | 示例 |
|------|------|---------|------|
| **配置数据Config** | 设计时填写,运行时只读 | 常驻内存 | 移动速度、伤害倍率、关卡布局 |
| **运行时状态State** | 运行时动态变化 | 随场景/游戏生命周期 | 当前 HP、已解锁能力、世界标志位 |
### 5.2 配置数据模型的设计原则
```
ConfigData 特征:
✅ 字段全部只读
✅ 可被多个系统引用(共享)
✅ 可在运行前被工具验证合理性
✅ 变更不需要修改代码,只修改数据
❌ 不包含运行时可变字段
❌ 不包含对具体系统的引用
```
### 5.3 运行时状态的所有权
每份运行时状态只有**一个系统**拥有写权限:
| 状态 | 所有系统 | 其他系统访问方式 |
|------|---------|----------------|
| 玩家 HP | 玩家系统 | 事件订阅 `OnHPChanged` |
| 世界标志位 | 叙事系统 / 世界系统 | 只读接口查询 |
| 背包道具 | 进程系统 | 只读接口查询 |
| 货币余额 | 经济系统 | 事件订阅 `OnGeoChanged` |
---
## 6. 模块边界原则
### 6.1 边界定义规则
系统的边界由以下三要素确定:
1. **输入**:系统响应的事件 + 调用的接口
2. **输出**:系统发出的事件 + 对外提供的接口
3. **状态**:系统独占写权限的运行时数据
### 6.2 系统边界一览
| 系统 | 输入来源 | 输出目标 | 独占状态 |
|------|---------|---------|---------|
| 输入系统 | 设备原始信号 | 动作事件(发出)| — |
| 玩家系统 | 输入动作事件 | 玩家状态事件 | HP、灵力、魄元、灵泉、Geo、能力 |
| 战斗系统 | 碰撞检测结果 | 伤害事件、命中事件 | — |
| 敌人系统 | 玩家位置(只读)| 敌人行动事件 | 敌人 HP、AI 状态 |
| 世界系统 | 玩家位置(只读)| 场景切换事件 | 房间发现状态、存档点激活状态 |
| 进程系统 | 玩家动作事件 | 解锁事件 | 已解锁能力、完成度 |
| 存档系统 | 存档/读档请求 | — | 持久化数据快照 |
| UI 系统 | 全部状态事件(订阅)| 输入事件(菜单操作)| UI 层堆叠状态 |
| 音频系统 | 全部状态事件(订阅)| — | 当前音乐状态 |
| 经济系统 | 购买/掉落事件 | 货币变化事件 | Geo 余额 |
| 叙事系统 | 世界/玩家事件 | 对话事件、结局评估 | 世界标志位 |
---
## 7. 系统优先级与生命周期
### 7.1 启动顺序
系统初始化应遵循依赖顺序,低层先于高层初始化:
```
1. 存档系统(读取持久化数据)
2. 配置数据加载(所有 Config 可用)
3. 输入系统
4. 玩家系统(使用配置和存档数据初始化)
5. 世界系统(加载场景数据)
6. 敌人系统(使用世界数据初始化)
7. 支撑层系统UI、音频、叙事等
```
### 7.2 关卡切换时的系统行为
| 事件 | 各系统行为 |
|------|----------|
| 触发场景切换 | 世界系统发出 `OnSceneTransitionBegin` |
| 过渡动画播放 | UI 系统显示过渡遮罩;输入系统暂停接收 |
| 旧场景卸载 | 敌人系统、VFX 等局部系统销毁 |
| 新场景加载 | 世界系统重建房间;敌人系统重新初始化 |
| 玩家状态恢复 | 玩家系统将 HP/技能应用至新场景(不重置)|
| 切换完成 | 世界系统发出 `OnSceneTransitionEnd`;输入系统恢复 |
### 7.3 死亡与复活流程
```
玩家 HP 降至 0
→ 玩家系统发出 OnPlayerDied
→ 输入系统:禁止所有输入
→ UI 系统:播放死亡遮罩动画
→ 音频系统:切换至死亡音乐
→ 存档系统:记录死亡位置(遗骸数据)
→ 世界系统:在上次死亡位置生成遗骸实体
等待(玩家按键或自动)→ 触发复活
复活流程:
→ 世界系统:加载最近存档点所在场景
→ 玩家系统:恢复存档时的 HP / 灵泉次数(见存档规范)
→ 输入系统:恢复输入
→ UI 系统:淡出遮罩
→ 音频系统:恢复区域音乐
```
---
## 附:常见架构陷阱与规避方法
| 陷阱 | 典型表现 | 规避方法 |
|------|---------|---------|
| **上帝对象** | 一个系统知道所有其他系统的状态 | 拆分为多个专责系统 |
| **循环依赖** | A 依赖 BB 依赖 A | 引入事件频道或接口打断循环 |
| **隐式顺序耦合** | 系统 A 必须在 B 之后执行才正确 | 改用事件触发替代时序依赖 |
| **状态同步噩梦** | 多个系统各自维护同一份数据的副本 | 确立唯一所有者,其他系统订阅变化 |
| **胖配置数据** | 配置数据里混入运行时逻辑 | 严格区分 Config 和 RuntimeState |

View File

@@ -0,0 +1,173 @@
# 02 · 事件与消息系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**:定义游戏全局的事件通信规范、事件目录与发布/订阅契约。
---
## 目录
1. [事件系统设计原则](#1-事件系统设计原则)
2. [事件类型体系](#2-事件类型体系)
3. [发布/订阅契约](#3-发布订阅契约)
4. [全局事件目录](#4-全局事件目录)
5. [事件设计指导](#5-事件设计指导)
---
## 1. 事件系统设计原则
| 原则 | 说明 |
|------|------|
| **最小载荷** | 事件只携带消费方需要的最少信息 |
| **类型安全** | 每个事件有明确的载荷类型,不使用无类型通用事件 |
| **无副作用传递** | 事件不携带可修改的对象引用,防止订阅方意外修改状态 |
| **单向通知** | 事件只通知"发生了什么",不携带"如何处理"的指令 |
| **有限生命周期** | 事件在发出后立即处理,不在系统间长期持有 |
---
## 2. 事件类型体系
### 2.1 按载荷分类
| 类型 | 描述 | 适用场景 |
|------|------|---------|
| `Event<void>` | 无载荷事件,纯通知 | 玩家死亡、游戏暂停 |
| `Event<T>` | 单值载荷 | HP 变化(传入新值)|
| `Event<Payload>` | 结构体载荷 | 伤害事件(多字段)|
### 2.2 按作用范围分类
| 类型 | 作用范围 | 说明 |
|------|---------|------|
| **全局事件** | 全游戏有效,跨场景 | 玩家死亡、游戏暂停、存档完成 |
| **局部事件** | 单场景/房间内有效 | 敌人死亡、机关触发、Boss 阶段变更 |
| **实体事件** | 单个实体范围 | 单个 NPC 对话开始、单个陷阱激活 |
---
## 3. 发布/订阅契约
### 3.1 发布方契约
发布方Emitter必须
- 在系统初始化时注册事件频道
- 仅在状态确实发生变化时发出事件(不重复发出相同状态)
- 在系统销毁时清理事件频道
### 3.2 订阅方契约
订阅方Listener必须
- 在系统初始化时注册订阅
- 在系统销毁时取消订阅(防止悬挂引用)
- 不在事件处理中再发出同类型事件(防止递归循环)
### 3.3 事件处理顺序
当多个系统订阅同一事件时,处理顺序**不保证**。
任何依赖特定顺序的逻辑应通过**新增中间事件**拆分为有序的因果链,而非依赖底层执行顺序。
```
❌ 错误:期望 A 先处理B 后处理同一事件
✅ 正确A 处理事件后发出新事件B 订阅新事件
```
---
## 4. 全局事件目录
### 4.1 玩家相关事件
| 事件名 | 载荷类型 | 发出方 | 典型订阅方 | 触发时机 |
|--------|---------|--------|----------|---------|
| `OnPlayerDied` | `void` | 玩家系统 | UI、音频、存档、世界 | 玩家 HP 归零 |
| `OnPlayerRevived` | `void` | 游戏管理器 | 玩家系统、UI、音频 | 复活流程完成 |
| `OnHPChanged` | `{current: Integer, max: Integer}` | 玩家系统 | UIHUD| HP 任意变化 |
| `OnSoulPowerChanged` | `{current: Integer, max: Integer}` | 玩家系统 | UIHUD| 灵力变化 |
| `OnSpiritPowerChanged` | `{current: Integer, max: Integer}` | 玩家系统 | UIHUD| 魄元变化 |
| `OnSpringChargesChanged` | `{current: Integer, max: Integer}` | 玩家系统 | UIHUD| 灵泉次数变化 |
| `OnGeoChanged` | `{current: Integer, delta: Integer}` | 经济系统 | UIHUD| Geo 增减 |
| `OnAbilityUnlocked` | `{abilityId: ID}` | 进程系统 | 玩家系统、UI | 新能力获得 |
| `OnFormChanged` | `{newFormId: ID}` | 形态系统 | UI、音频、武器系统 | 玩家切换形态 |
| `OnParrySuccess` | `{counterWindowDuration: Duration}` | 战斗系统 | 玩家系统、UI、音频 | 弹反判定成功 |
| `OnHitConfirmed` | `HitPayload` | 战斗系统 | 玩家系统(灵力积累)、反馈系统 | 攻击命中判定 |
### 4.2 敌人相关事件
| 事件名 | 载荷类型 | 发出方 | 典型订阅方 | 触发时机 |
|--------|---------|--------|----------|---------|
| `OnEnemyDied` | `{enemyId: ID, position: Vector2}` | 敌人系统 | 进程系统、经济系统、叙事系统 | 敌人 HP 归零 |
| `OnEnemyHit` | `HitPayload` | 战斗系统 | 反馈系统、音频 | 攻击命中敌人 |
| `OnBossPhaseChanged` | `{bossId: ID, phase: Integer}` | Boss 系统 | UI、音频 | Boss 进入新阶段 |
| `OnBossDefeated` | `{bossId: ID}` | 敌人系统 | 进程系统、叙事系统、存档 | Boss 战胜利 |
### 4.3 世界/进程相关事件
| 事件名 | 载荷类型 | 发出方 | 典型订阅方 | 触发时机 |
|--------|---------|--------|----------|---------|
| `OnSceneTransitionBegin` | `{targetSceneId: ID}` | 世界系统 | UI遮罩、输入系统 | 开始场景切换 |
| `OnSceneTransitionEnd` | `{sceneId: ID}` | 世界系统 | 输入系统、音频 | 场景切换完成 |
| `OnSavePointActivated` | `{savePointId: ID, position: Vector2}` | 世界系统 | 存档系统、玩家系统、UI | 玩家激活存档点 |
| `OnRoomDiscovered` | `{roomId: ID}` | 世界系统 | 地图系统 | 玩家首次进入房间 |
| `OnCollectiblePickedUp` | `{collectibleId: ID, type: CollectibleType}` | 世界系统 | 进程系统、叙事系统 | 玩家拾取收集品 |
| `OnWorldFlagChanged` | `{flagId: ID, value: Boolean}` | 叙事系统 | 叙事系统(自引用)、进程系统 | 世界状态标志变化 |
### 4.4 UI/系统相关事件
| 事件名 | 载荷类型 | 发出方 | 典型订阅方 | 触发时机 |
|--------|---------|--------|----------|---------|
| `OnGamePaused` | `void` | 游戏管理器 | 所有系统 | 暂停键触发 |
| `OnGameResumed` | `void` | 游戏管理器 | 所有系统 | 恢复键触发 |
| `OnSaveCompleted` | `{slotId: Integer}` | 存档系统 | UI提示| 存档写入完成 |
| `OnSettingsChanged` | `SettingsPayload` | 设置系统 | 音频、无障碍、UI 等 | 设置项变更 |
| `OnAchievementUnlocked` | `{achievementId: ID}` | 成就系统 | UIToast| 成就达成 |
---
## 5. 事件设计指导
### 5.1 新增事件时的决策流程
```
需要系统 A 通知系统 B 时:
B 是否需要立即同步返回值?
├─ 是 → 使用接口调用(不是事件)
└─ 否 → 使用事件
事件是否跨多个系统关心?
├─ 是(多个系统订阅)→ 定义为全局事件,加入全局目录
└─ 否(仅 A→B→ 考虑是否用接口更合适
事件是否携带可变对象引用?
├─ 是 → 改为传递值类型载荷ID + 必要字段)
└─ 否 → 可以使用
```
### 5.2 载荷设计规范
**好的载荷(值类型,信息完整)**
```
OnEnemyDied {
enemyId : ID // 唯一标识,订阅方可用于查询更多信息
position : Vector2 // 死亡位置(生成掉落物需要)
lootTableId : ID // 掉落表 ID经济系统直接使用
}
```
**不好的载荷(引用传递,导致耦合)**
```
OnEnemyDied {
enemyRef : EnemyObject // ❌ 引用具体对象,订阅方与敌人系统耦合
}
```
### 5.3 事件命名规范
| 规范 | 说明 | 示例 |
|------|------|------|
| 动词过去式On + 名词 + 动词过去式)| 表示"已发生的事" | `OnPlayerDied``OnAbilityUnlocked` |
| 变化类用 Changed | 状态数值变化 | `OnHPChanged``OnGeoChanged` |
| 开始/结束对 | 持续性事件标注阶段 | `OnSceneTransitionBegin` / `OnSceneTransitionEnd` |
| 避免"Will/Should" | 事件是通知,不是征询许可 | ❌ `OnPlayerWillDie`,✅ `OnPlayerDied` |

View File

@@ -0,0 +1,332 @@
# 03 · 玩家系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**:玩家实体的构成、资源模型、状态机规范与对外行为契约。
> **关联**[04_CombatSystem](./04_CombatSystem.md) · [05_MovementSystem](./05_MovementSystem.md) · [06_FormSystem](./06_FormSystem.md)
---
## 目录
1. [玩家实体构成](#1-玩家实体构成)
2. [资源模型](#2-资源模型)
3. [玩家状态机](#3-玩家状态机)
4. [行为契约](#4-行为契约)
5. [能力解锁体系](#5-能力解锁体系)
6. [玩家配置参数](#6-玩家配置参数)
7. [发出的事件](#7-发出的事件)
---
## 1. 玩家实体构成
玩家实体是多个**职责分离的子系统**的协调器,自身不含具体业务逻辑:
```
PlayerEntity协调器
├── MovementController ← 物理移动(速度、跳跃、冲刺)
├── StatContainer ← 资源数值HP、灵力、魄元、灵泉、Geo
├── CombatHandler ← 攻击判定HitBox 开关、连击链)
├── FormController ← 形态管理(天魂/地魂/命魂切换)
├── WeaponSlot ← 当前武器(形态联动自动切换)
├── SkillExecutor ← 技能执行(魂技能/魄技能)
├── ParryHandler ← 弹反逻辑
├── HurtBox ← 受击判定(路由 DamagePacket
└── StateMachine ← 行为状态机(协调上述子系统)
```
**协调器原则**PlayerEntity 只负责:
1. 初始化子系统并维护引用
2. 维护状态机(决定当前行为)
3. 在受击时强制转换到受击状态
4. 在死亡时触发 `OnPlayerDied` 事件
---
## 2. 资源模型
### 2.1 资源总览
| 资源 | 类型 | 范围 | 特性 | 用途 |
|------|------|------|------|------|
| **HP** | Integer | 0 ~ MaxHP | 受击减少,存档点/灵泉恢复 | 生存指标 |
| **灵力SoulPower** | Integer | 0 ~ 100 | 近战命中积累,死亡清零 | 魂技能燃料 |
| **魄元SpiritPower** | Integer | 0 ~ 100 | 时间自动恢复,死亡恢复至上限 | 魄技能燃料 |
| **灵泉次数SpringCharges** | Integer | 0 ~ MaxCharges | 击杀积分驱动,存档点恢复 | 治疗次数 |
| **Geo灵晶** | Integer | 0 ~ ∞ | 击败敌人获得,死亡遗留遗骸 | 货币 |
### 2.2 灵力积累规则
> **设计目标**:鼓励玩家主动攻击,弹反作为高回报行为
| 来源 | 灵力增量 |
|------|---------|
| 近战命中普通敌人 | +10 |
| 近战命中 Boss | +5 |
| 弹反成功 | +33 |
- 死亡时归零
- 无自动恢复
### 2.3 魄元恢复规则
> **设计目标**:魄元是始终可用的"持续消耗"资源,不依赖风险操作
| 来源 | 魄元增量 |
|------|---------|
| 每秒自动恢复 | +`spiritRegenRate`(见配置参数)|
| 死亡复活 | 恢复至上限 |
| 存档点交互 | 恢复至上限 |
- 受伤不影响魄元
- 使用魄技能后扣除固定量
### 2.4 灵泉次数规则
> **设计目标**:治疗是"击杀驱动"而非"时间驱动",鼓励激进战斗风格
| 来源 | 变化 |
|------|------|
| 击杀普通敌人 | +1 积分 |
| 击杀精英怪 | +3 积分 |
| 击杀 Boss | +5 积分 |
| 积分达到阈值 | +1 次灵泉(积分清零)|
| 使用灵泉(治疗)| -1 次 |
| 与存档点交互 | 恢复至上限 |
| 死亡复活 | **不恢复**(死亡惩罚)|
> **设计决策**:死亡不恢复灵泉
> **原因**:保留一定死亡惩罚,让玩家珍惜治疗机会,但由于复活在存档点旁,玩家仍可快速通过激活存档点补满
### 2.5 Geo货币规则
| 来源 | 变化 |
|------|------|
| 击败敌人 | 掉落固定 + 随机范围 |
| 破坏场景容器 | 少量固定 |
| 任务奖励 | 固定量 |
| 死亡 | Geo 留在遗骸,不直接丢失 |
| 取回遗骸 | 遗骸的 Geo 归还玩家 |
| 再次死亡(未取回遗骸)| 遗骸 Geo **永久丢失** |
---
## 3. 玩家状态机
### 3.1 状态优先级
| 优先级 | 状态 | 说明 |
|--------|------|------|
| 100 | `DeathState` | 死亡,不可被任何状态打断 |
| 90 | `HurtState` | 受击硬直,仅死亡可打断 |
| 80 | `DashState` | 冲刺(含空中冲刺),受伤/死亡可打断 |
| 75 | `SoulSkillState` | 魂技能前摇,冲刺/受伤可打断 |
| 72 | `SpiritSkillState` | 魄技能前摇,冲刺/受伤可打断 |
| 70 | `ParryState` | 弹反等待,受伤可打断 |
| 60 | `AttackState` | 攻击,弹反/受伤可打断 |
| 55 | `SpringState` | 使用灵泉,前摇可被打断 |
| 40 | `InteractState` | 与 NPC/物件交互 |
| 35 | `WallGrabState` | 抓墙悬挂,冲刺/技能可打断 |
| 30 | `AirState` | 空中(含跳跃/下落)|
| 20 | `RunState` | 地面奔跑 |
| 10 | `SwimState` | 游泳(解锁后)|
| 0 | `IdleState` | 待机(最低优先级)|
### 3.2 状态转换规则
```
任意状态
══受击(HP>0)══► HurtState [强制,忽略优先级]
══受击(HP=0)══► DeathState [强制,不可被打断]
IdleState
──移动输入──────────► RunState
──跳跃输入──────────► AirState
──攻击输入──────────► AttackState
──弹反输入──────────► ParryState
──冲刺输入──────────► DashState
──魂技能+灵力充足───► SoulSkillState
──魄技能+魄元充足───► SpiritSkillState
──使用灵泉+次数≥1──► SpringState
──交互输入+目标存在─► InteractState
RunState
──无移动输入─────────► IdleState
──跳跃输入───────────► AirState
──攻击输入───────────► AttackState保持RunState上半身叠加
──弹反/冲刺/技能─────► 对应状态
AirState统一处理 跳跃/双跳/下落)
──落地───────────────► IdleState 或 RunState
──跳跃输入+双跳可用──► AirState内重置双跳不切状态
──贴墙+朝墙方向输入──► WallGrabState
──攻击/弹反/冲刺─────► 对应状态
WallGrabState
──跳跃输入───────────► AirState蹬墙跳
──反向输入或落地─────► AirState / IdleState
──冲刺/技能──────────► 对应状态
AttackState上半身叠加下半身保持原状态
~~动画结束~~──────────► 恢复原状态Idle/Run/Air
──连击窗口内攻击输入─► AttackState切换连击段
──弹反输入───────────► ParryState
ParryState
──弹反成功───────────► ParrySuccessState反击窗口
──攻击输入(窗口内)─► AttackStateParryCounter
~~反击窗口超时~~─────► IdleState/RunState
~~弹反超时~~──────────► IdleState/RunState
DashState
~~冲刺时长结束~~────── ► AirState 或 IdleState/RunState
SpringState
~~前摇完成~~──────────► 后摇 → IdleState
──被打断─────────────► IdleState灵泉次数已扣不返还
HurtState
~~硬直时长结束~~──────► IdleState
DeathState
(等待游戏管理器触发复活流程)
```
### 3.3 攻击连击链
| 连击段 | 触发方式 | 攻击方向 | 伤害倍率 | 备注 |
|--------|---------|---------|---------|------|
| `Attack1` | 第一次攻击输入 | 水平 | ×1.0 | 各形态动画不同 |
| `Attack2` | 连击窗口内再攻击 | 水平 | ×1.0 | |
| `Attack3` | 连击窗口内再攻击 | 水平 | ×2.0 | 重击,连击链结尾 |
| `AirAttack` | 空中攻击 | 水平 | ×1.0 | |
| `UpAttack` | 移动方向向上时攻击 | 向上 | ×1.0 | 角色轻微下移(反作用力)|
| `DownAttack` | 空中+移动方向向下时攻击 | 向下 | ×1.0 | 命中时产生弹起Pogo|
| `ParryCounter` | 弹反成功后攻击 | 水平 | ×3.0 | 不可被弹反,忽略无敌帧 |
> **设计决策**:第三击伤害 ×2弹反反击 ×3
> **原因**:鼓励打满连击,奖励弹反技巧。高倍率使玩家有显著的策略目标。
---
## 4. 行为契约
### 4.1 玩家实体对外接口
```
Interface IPlayerEntity {
// 只读属性
[readonly] position : Vector2
[readonly] facingDirection : Integer // +1 右 / -1 左
[readonly] currentState : StateID
[readonly] isInvincible : Boolean
// 强制状态切换(外部系统使用,如受击)
forceState(stateId: StateID, context: Optional<StateContext>) → void
// 查询
hasAbility(abilityId: ID) → Boolean
// 事件
[event] onDied : Event<void>
[event] onHit : Event<HitPayload>
[event] onStateChanged : Event<StateChangedPayload>
}
```
### 4.2 StatContainer 对外接口
```
Interface IStatContainer {
[readonly] currentHP : Integer
[readonly] maxHP : Integer
[readonly] currentSoulPower : Integer
[readonly] currentSpiritPower : Integer
[readonly] currentSpringCharges: Integer
[readonly] maxSpringCharges : Integer
[readonly] currentGeo : Integer
[readonly] isAlive : Boolean
takeDamage(amount: Integer) → void
heal(amount: Integer) → void
addSoulPower(amount: Integer) → void
consumeSoulPower(amount: Integer) → Result<Boolean>
consumeSpiritPower(amount: Integer) → Result<Boolean>
addKillCredit(creditType: KillType) → void // 驱动灵泉积分
useSpring() → Result<Boolean>
restoreSpringCharges() → void
addGeo(amount: Integer) → void
spendGeo(amount: Integer) → Result<Boolean>
beginInvincibility(duration: Duration)→ void
[event] onHPChanged : Event<{current, max}>
[event] onSoulPowerChanged : Event<{current, max}>
[event] onSpiritPowerChanged : Event<{current, max}>
[event] onSpringChargesChanged : Event<{current, max}>
[event] onGeoChanged : Event<{current, delta}>
[event] onDied : Event<void>
}
```
---
## 5. 能力解锁体系
### 5.1 能力定义
| 能力 ID | 默认状态 | 影响的状态/行为 | 解锁位置 |
|---------|---------|--------------|---------|
| `DoubleJump` | 锁定 | AirState 允许第二次跳跃 | 探索奖励 |
| `WallGrab` | **已解锁** | AirState → WallGrabState 允许 | 初始能力 |
| `AerialDash` | 锁定 | AirState → DashState 允许(消耗充能)| 探索奖励 |
| `Parry` | **已解锁** | ParryState 可进入 | 初始能力 |
| `Swim` | 锁定 | 液体区域不死亡SwimState 可进入 | 主线解锁 |
| `PlungeAttack` | 锁定 | 空中向下冲刺触发下冲 | 探索奖励 |
### 5.2 解锁流程
```
玩家触碰解锁物件AbilityUnlockItem
→ 世界系统发出 OnCollectiblePickedUptype=Ability, abilityId
→ 进程系统处理:将 abilityId 加入已解锁集合
→ 进程系统发出 OnAbilityUnlockedabilityId
→ 玩家系统接收:更新运行时能力标记
→ UI 系统显示能力获得动画
→ 存档系统:在下次存档时持久化
```
---
## 6. 玩家配置参数
以下参数在配置数据中定义,运行时只读:
| 参数 | 类型 | 推荐值 | 说明 |
|------|------|--------|------|
| `maxHP` | Integer | 5 | 初始最大生命值 |
| `maxSoulPower` | Integer | 100 | 灵力上限 |
| `maxSpiritPower` | Integer | 100 | 魄元上限 |
| `spiritRegenRate` | Number | 8.0/s | 魄元每秒自动回复量 |
| `maxSpringCharges` | Integer | 3 | 初始最大灵泉次数 |
| `springKillThreshold` | Integer | 5 | 增加 1 次灵泉所需积分 |
| `iFrameAfterHit` | Duration | 1.2s | 受击后无敌时长 |
| `comboWindowDuration` | Duration | 0.5s | 连击窗口时长 |
| `comboResetDelay` | Duration | 0.3s | 第三击后连击重置延迟 |
| `parryCounterDuration` | Duration | 0.6s | 弹反后反击窗口时长 |
---
## 7. 发出的事件
| 事件 | 触发时机 | 载荷 |
|------|---------|------|
| `OnPlayerDied` | HP 归零 | `void` |
| `OnHPChanged` | HP 任意变化 | `{current, max}` |
| `OnSoulPowerChanged` | 灵力变化 | `{current, max}` |
| `OnSpiritPowerChanged` | 魄元变化 | `{current, max}` |
| `OnSpringChargesChanged` | 灵泉次数变化 | `{current, max}` |
| `OnGeoChanged` | Geo 增减 | `{current, delta}` |
| `OnFormChanged` | 形态切换 | `{newFormId}` |
| `OnParrySuccess` | 弹反成功 | `{counterWindowDuration}` |
| `OnHitConfirmed` | 攻击命中有效目标 | `HitPayload` |
| `OnAbilityUnlocked` | 新能力获得 | `{abilityId}` |

View File

@@ -0,0 +1,271 @@
# 04 · 战斗系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**:伤害数据模型、命中/受击管线、弹反系统、状态效果与连击规范。
> **关联**[03_PlayerSystem](./03_PlayerSystem.md) · [07_EnemySystem](./07_EnemySystem.md)
---
## 目录
1. [战斗设计原则](#1-战斗设计原则)
2. [DamagePacket — 伤害数据模型](#2-damagepacket--伤害数据模型)
3. [命中/受击管线](#3-命中受击管线)
4. [弹反系统](#4-弹反系统)
5. [霸体与打断系统](#5-霸体与打断系统)
6. [状态效果系统](#6-状态效果系统)
7. [战斗配置参数](#7-战斗配置参数)
---
## 1. 战斗设计原则
| 原则 | 说明 |
|------|------|
| **数据驱动** | 伤害、击退、硬直全部封装在 DamagePacket 中传递,不通过直接引用 |
| **物理解耦** | 攻击方和受击方通过碰撞区域交互,不互相持有引用 |
| **事件广播** | 命中确认后广播事件,让灵力积累、反馈系统等独立响应 |
| **可弹反标记** | 每次攻击明确标记是否可被弹反,由 DamagePacket.flags 决定 |
---
## 2. DamagePacket — 伤害数据模型
DamagePacket 是一次攻击行为的完整数据描述,**只读值类型**,在管线中传递不可修改:
```
DataModel DamagePacket {
rawDamage : Integer // 基础伤害(未经减免)
finalDamage : Integer // 实际伤害(由受击方计算后填写)
damageType : DamageType // 伤害类型
flags : Flags<DamageFlags> // 属性标记
──────────────────────────────────────
knockbackDir : Vector2 // 击退方向(单位向量)
knockbackForce : Number // 击退力度
stunDuration : Duration // 受击硬直时长(秒)
breakLevel : BreakLevel // 打断等级(影响霸体穿透)
──────────────────────────────────────
sourceId : ID // 攻击来源标识(用于特殊规则)
sourcePosition : Vector2 // 攻击来源位置
sourceLayer : AttackerType // 来源类型(玩家/敌人/环境)
hitFxType : HitFxType // 命中特效类型
}
```
### DamageType 枚举
| 值 | 说明 |
|----|------|
| `Normal` | 普通物理伤害 |
| `Fire` | 火焰伤害(可点燃)|
| `Poison` | 毒素伤害(可中毒)|
| `Void` | 虚空伤害(忽略所有减免,用于特定 Boss|
| `True` | 真实伤害(固定量,忽略防御)|
| `Environmental` | 环境伤害(棘刺/熔岩)|
### DamageFlags 标记
| 标记 | 说明 |
|------|------|
| `CanBeParried` | 可被弹反(移动弹射物/近身攻击)|
| `Unblockable` | 不可被格挡或弹反(特殊攻击)|
| `IgnoreIFrame` | 忽略受击无敌帧(毒素持续伤害)|
| `PerfectParryOnly` | 只能被完美弹反(高难度攻击)|
| `AoE` | 范围伤害(爆炸/冲击波)|
| `DotTick` | 持续伤害跳数(由状态效果发出)|
---
## 3. 命中/受击管线
### 3.1 管线流程
```
攻击方激活 HitArea攻击判定区域
├─ 与 HurtArea受击判定区域发生碰撞检测
│ └─ 确认碰撞:攻击方的 AttackerLayer 与受击方的 HurtableLayer 匹配
├─ 受击方 HurtArea 接收 DamagePacket
│ └─ 检查:是否处于无敌帧?
│ ├─ 是(且非 IgnoreIFrame→ 丢弃,不处理
│ └─ 否 → 继续
├─ 受击方计算 finalDamage
│ └─ rawDamage × 攻击倍率 × 防御减免
├─ 受击方本地响应
│ ├─ 扣减 HP
│ ├─ 应用击退
│ ├─ 进入硬直状态
│ └─ 启动无敌帧计时
└─ 广播 OnHitConfirmed 事件(全局)
├─ 玩家系统:攻击命中敌人 → 增加灵力
├─ 反馈系统:播放命中特效、震屏、音效
└─ 进程系统:记录伤害统计
```
### 3.2 攻击判定区域HitArea规范
| 属性 | 说明 |
|------|------|
| 形状 | 矩形主要或圆形AoE 攻击)|
| 激活时机 | 由动画事件驱动(某一帧开启,某一帧关闭)|
| 打击次数 | 默认一次攻击只打击一个目标一次 |
| 层级过滤 | 只与己方对立层级的 HurtArea 发生碰撞 |
### 3.3 受击判定区域HurtArea规范
| 属性 | 说明 |
|------|------|
| 形状 | 矩形(贴近实体碰撞箱)|
| 常态 | 始终激活(死亡后关闭)|
| 无敌帧 | 无敌期间 HurtArea 不响应伤害(仍检测碰撞)|
---
## 4. 弹反系统
### 4.1 设计目标
弹反是游戏的**核心差异化机制**
- 高风险(窗口极短)→ 高回报(大量灵力 + 反击机会)
- 成功弹反会产生"子弹时间"效果,强调成功感
- 所有标记 `CanBeParried` 的攻击都可以弹反
### 4.2 弹反流程
```
玩家按弹反键 → 进入 ParryState
ParryState 开始:
├─ 弹反激活期parryActiveWindow开始
│ └─ 激活期内接收到带 CanBeParried 标记的 DamagePacket
│ → 弹反成功
│ ├─ 丢弃该 DamagePacket伤害不生效
│ ├─ 触发子弹时间(游戏速度降至 slowMotionScale持续 hitStopDuration
│ ├─ 播放弹反成功动画/特效/音效
│ ├─ 发出 OnParrySuccess 事件
│ ├─ 为玩家增加 +33 灵力
│ └─ 开启反击窗口counterWindow
~~parryActiveWindow 超时~~ → 弹反失败,回到正常状态
反击窗口内:
──攻击输入──► ParryCounter 攻击×3 伤害,不可弹反)
~~反击窗口超时~~ → IdleState/RunState
```
### 4.3 弹反参数
| 参数 | 推荐值 | 说明 |
|------|--------|------|
| `parryActiveWindow` | 0.15s | 弹反激活期(可弹反的时间窗口)|
| `parryStartupDelay` | 0.05s | 按键到激活期的前摇 |
| `hitStopDuration` | 0.1s | 子弹时间持续 |
| `slowMotionScale` | 0.15 | 子弹时间游戏速度倍率0=完全静止)|
| `counterWindowDuration` | 0.6s | 弹反成功后的反击窗口 |
| `parryCounterMultiplier` | 3.0 | 弹反反击伤害倍率 |
| `soulPowerOnParry` | 33 | 弹反成功获得灵力 |
> **设计决策**:弹反前摇 0.05s
> **原因**:消除"垃圾反弹"(无脑按弹反躲伤害),要求玩家预判攻击时机
### 4.4 弹反优先级规则
| 情况 | 处理 |
|------|------|
| 多个攻击同时到达 | 优先弹反第一个判定的攻击,其余正常受击 |
| 弹反期间受到 `Unblockable` 攻击 | 正常受伤,弹反状态中断 |
| 弹反期间受到 `PerfectParryOnly` | 需在激活期的前 1/3 时间内(完美窗口)|
---
## 5. 霸体与打断系统
### 5.1 概念
**霸体Poise**:实体在受到攻击时抵抗动作打断的能力。
**打断等级BreakLevel**:一次攻击能打断多高等级霸体的能力。
### 5.2 等级体系
| 打断等级 | 含义 | 典型来源 |
|---------|------|---------|
| `0 - None` | 无打断力 | 环境持续伤害 |
| `1 - Light` | 打断低级霸体 | 玩家普通攻击 |
| `2 - Medium` | 打断中级霸体 | 玩家重击Attack3|
| `3 - Heavy` | 打断高级霸体 | 弹反反击、魂技能 |
| `4 - Force` | 无条件打断(无视霸体)| 特定 Boss 技能 |
### 5.3 霸体等级
| 霸体等级 | 含义 | 适用实体 |
|---------|------|---------|
| `0 - Fragile` | 任意攻击均可打断 | 普通小怪 |
| `1 - Standard` | 普通攻击可打断 | 常规敌人 |
| `2 - Armored` | 需重击才能打断 | 精英怪 |
| `3 - Boss` | 仅弹反反击/强力技能可打断 | Boss |
### 5.4 判定规则
```
attackBreakLevel >= targetPoiseLevel → 动作打断(进入受击状态)
attackBreakLevel < targetPoiseLevel → 伤害照常,但不打断动作
ForceBreak 标记 → 无条件打断,忽略霸体等级
```
---
## 6. 状态效果系统
### 6.1 状态效果模型
```
DataModel StatusEffect {
effectId : ID
effectType : StatusEffectType
duration : Duration // 持续时间0 = 永久直到治疗)
tickInterval : Duration // DoT 跳数间隔(仅伤害类)
damagePerTick : Integer // 每跳伤害(仅伤害类)
stackBehavior : StackBehavior // 叠加规则
sourceId : ID // 施加方标识
}
```
### 6.2 效果类型
| 类型 | 效果 | 可叠加 | 治疗方式 |
|------|------|--------|---------|
| `Poison` | 每 0.5s 扣 1 HP持续 6s | 延长时间(不叠加层数)| 解毒药 / 离开毒素区 |
| `Burn` | 每 0.3s 扣 2 HP持续 3s | 刷新时间 | 水体 / 时间 |
| `Stun` | 完全硬直,无法行动 | 刷新时间 | 时间 |
| `Slow` | 移动速度 ×0.5 | 不叠加(取最强)| 时间 |
| `Frozen` | 完全冻结,无敌但也无法行动 | 不叠加 | 火焰攻击 |
### 6.3 叠加规则StackBehavior
| 规则 | 说明 |
|------|------|
| `RefreshDuration` | 已有效果时重置持续时间 |
| `ExtendDuration` | 已有效果时延长持续时间 |
| `AddStack` | 叠加层数,每层独立计时 |
| `IgnoreIfActive` | 已激活时忽略新施加 |
---
## 7. 战斗配置参数
| 参数 | 推荐值 | 说明 |
|------|--------|------|
| `baseAttackDamage` | 10 | 玩家基础攻击伤害 |
| `attack1Multiplier` | 1.0 | 第一击倍率 |
| `attack2Multiplier` | 1.0 | 第二击倍率 |
| `attack3Multiplier` | 2.0 | 第三击(重击)倍率 |
| `parryCounterMultiplier` | 3.0 | 弹反反击倍率 |
| `knockbackForceNormal` | 5.0 | 普通击退力度 |
| `knockbackForceHeavy` | 10.0 | 重击击退力度 |
| `defaultStunDuration` | 0.3s | 默认受击硬直时长 |
| `soulPowerPerHit` | 10 | 命中普通敌人获得灵力 |
| `soulPowerPerBossHit` | 5 | 命中 Boss 获得灵力 |

View File

@@ -0,0 +1,267 @@
# 05 · 移动系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**:玩家移动的物理模型、跳跃机制、冲刺、墙壁力学与手感参数。
---
## 目录
1. [移动设计目标](#1-移动设计目标)
2. [地面移动](#2-地面移动)
3. [跳跃模型](#3-跳跃模型)
4. [冲刺系统](#4-冲刺系统)
5. [墙壁力学](#5-墙壁力学)
6. [空中冲刺Aerial Dash](#6-空中冲刺aerial-dash)
7. [下冲Plunge](#7-下冲plunge)
8. [地面与碰撞检测](#8-地面与碰撞检测)
9. [移动配置参数](#9-移动配置参数)
---
## 1. 移动设计目标
| 目标 | 实现方式 |
|------|---------|
| **精确可控** | 即时响应方向输入,没有"滑动"惯性 |
| **灵活多样** | 多种移动选项(跳跃、冲刺、墙跳、空中冲刺)|
| **手感沉重** | 快速下落fall multiplier跳跃顶点缓降 |
| **容错空间** | 土狼时间、跳跃缓冲降低输入失误惩罚 |
| **进阶可深** | 空中冲刺+墙跳的组合技深度 |
---
## 2. 地面移动
### 2.1 水平移动
| 状态 | 速度 | 说明 |
|------|------|------|
| 行走(轻推摇杆)| `walkSpeed` | 6 units/s |
| 奔跑(全推摇杆)| `runSpeed` | 9 units/s |
- 水平速度**即时响应**输入,无加速/减速惯性
- 松开输入时立即停止(不滑行)
> **设计决策**:不使用加速/减速曲线
> **原因**:类银河恶魔城强调精确跳台,惯性会使平台跳跃难以预判落点
### 2.2 朝向
- 朝向随水平输入即时翻转
- 攻击/技能状态中锁定朝向(不随输入改变)
---
## 3. 跳跃模型
### 3.1 跳跃类型
| 类型 | 触发条件 | 初速度 | 备注 |
|------|---------|--------|------|
| 地面跳跃 | 地面 + 跳跃键 | `jumpForce` | 标准跳跃 |
| 土狼跳 | 离地后 `coyoteTime` 内 + 跳跃键 | `jumpForce` | 视为地面跳跃 |
| 双跳 | 空中 + 跳跃键 + `DoubleJump` 已解锁 + 未使用过 | `doubleJumpForce` | 每次落地重置 |
| 墙跳(背墙)| 贴墙 + 跳跃键 | 水平: `wallJumpAwayHForce`,垂直: `wallJumpVerticalForce` | 背离墙壁方向 |
| 墙跳(对墙)| 贴墙 + 朝墙方向 + 跳跃键 | 水平: `wallJumpTowardHForce`,垂直: `wallJumpVerticalForce` | 朝墙壁方向 |
### 3.2 可变跳跃高度
松开跳跃键时,若玩家垂直速度仍向上,截断为原值的 50%。
→ 长按获得完整跳跃高度;短按获得约一半高度。
### 3.3 重力模型
使用**非对称重力**以获得"果断"手感:
| 阶段 | 重力倍率 |
|------|---------|
| 上升阶段 | ×1.0(基础重力)|
| 下落阶段 | ×`fallMultiplier`(默认 2.5|
| 短跳(松键后上升)| ×`lowJumpMultiplier`(默认 2.0|
| 跳跃顶点(速度接近 0| 短暂降低至 ×0.6(缓降感)|
### 3.4 跳跃缓冲Jump Buffer
在落地前 `jumpBufferDuration`(默认 0.15s)内按下跳跃键,落地后立即触发跳跃。
→ 消除玩家"刚好落地后跳跃失败"的挫败感。
---
## 4. 冲刺系统
### 4.1 地面冲刺
| 参数 | 值 | 说明 |
|------|-----|------|
| 方向 | 当前朝向(水平,不可向上/下)| 地面冲刺只能水平 |
| 速度 | `dashSpeed` (18 units/s) | 远高于跑步速度 |
| 时长 | `dashDuration` (0.18s) | 固定时长 |
| 冷却 | `dashCooldown` (0.5s) | 冷却期间不可再冲刺 |
| 无敌帧 | 冲刺全程 | 不受任何伤害 |
| 穿透 | 可穿过敌人 | 不被敌人阻挡 |
### 4.2 冲刺取消
冲刺中不可被低优先级状态打断。但以下情况可中断:
- 受到 `Unblockable` 攻击(仅此一项,普通攻击在冲刺无敌期间被忽略)
---
## 5. 墙壁力学
### 5.1 墙抓WallGrab
> **默认解锁**,不需要能力门
当玩家在空中贴近可抓取的墙壁并输入朝墙方向时,进入 WallGrabState
| 行为 | 说明 |
|------|------|
| 悬挂 | 玩家停止下落,附着在墙上 |
| 高度记忆 | 记录抓墙时的 Y 坐标(`wallGrabAnchorY`|
| 超出记忆值 | 玩家可向上爬(无上限)|
| 低于记忆值 | 按 `wallGrabSlideSpeed` 缓慢下滑(不快速下落)|
| 持续时间 | 无时间限制(可一直抓)|
### 5.2 墙跳类型
| 类型 | 触发 | 水平冲力 | 垂直冲力 |
|------|------|---------|---------|
| **背墙跳** | 跳跃键(无方向或反向)| `wallJumpAwayHForce` (10) | `wallJumpVerticalForce` (15) |
| **对墙跳** | 朝墙方向 + 跳跃键 | `wallJumpTowardHForce` (3) | `wallJumpVerticalForce` (15) |
背墙跳弹离效果强,用于快速脱离;对墙跳爬升效果强,用于沿墙向上攀爬。
> **设计决策**:两种墙跳共用垂直分量
> **原因**:统一手感基准,区别仅在水平表现
### 5.3 墙跳后的输入锁定
背墙跳后:
- 水平方向输入锁定 0.1s(防止玩家立即贴回同一面墙)
---
## 6. 空中冲刺Aerial Dash
> **需要解锁** `AerialDash` 能力
### 6.1 基本规则
| 参数 | 值 | 说明 |
|------|-----|------|
| 方向 | 8 方向(摇杆方向量化)| 与地面冲刺最大区别 |
| 速度 | `aerialDashSpeed` (15 units/s) | 略低于地面冲刺 |
| 时长 | `aerialDashDuration` (0.20s) | |
| 无敌帧 | 冲刺全程 | 同地面冲刺 |
| 充能次数 | 默认 1 次(护符可升至 2 次)| 每次空中各自独立 |
### 6.2 充能重置条件
| 触发条件 | 说明 |
|---------|------|
| 落地 | 触地瞬间恢复 |
| 墙跳成功 | 墙跳给予新的空中冲刺机会 |
| **近战攻击命中** | 命中敌人即恢复(鼓励进攻性空中移动)|
| **弹反成功** | 弹反视为命中,给予恢复 |
> **设计决策**:命中恢复充能
> **原因**:这是《丝之歌》核心机制,使得玩家可以通过激进攻击保持永续空中移动。
> **体验目标**:顶级玩家应该能通过攻击和弹反的组合在空中无限机动。
### 6.3 方向量化规则
摇杆输入方向量化为 8 个标准方向45° 间隔):
- 上/下/左/右4 个主方向)
- 左上/右上/左下/右下4 个斜方向)
- 若无输入,默认朝当前朝向水平冲刺
---
## 7. 下冲Plunge
> **无需解锁**,基础能力
### 7.1 触发条件
```
空中 + 向下方向输入 + 冲刺键
(优先级高于普通空中冲刺)
```
### 7.2 行为规范
| 阶段 | 说明 |
|------|------|
| 激活 | 取消垂直速度,施加极大向下速度(`plungeSpeed` = 28 units/s|
| 下冲中 | 全程无敌帧;保留少量水平速度(原值 ×0.2|
| 落地 | 触发冲击波 AoE范围内敌人受伤 + 强硬直 |
| 落地后 | 轻微弹起(`verticalBounce` = 4 units/s→ 回 IdleState |
| 充能消耗 | 消耗 1 次空中冲刺充能(与 AerialDash 共享)|
### 7.3 冲击波参数
| 参数 | 值 |
|------|-----|
| 冲击波半径 | 2.0 units |
| 冲击伤害 | 基础攻击 ×2.0 |
| 硬直时长 | 0.8s |
| 是否可弹反 | 否 |
---
## 8. 地面与碰撞检测
### 8.1 地面检测
- 在角色脚部发起**小矩形区域检测**,检测 Ground 层
- 每帧更新,检测结果对外暴露为只读属性 `isGrounded`
- **单向平台**OneWayPlatform
- 从下方跳跃时穿过(检测忽略该层)
- 站在上方时视为地面
- 向下 + 跳跃键:临时忽略单向平台层,允许下穿
### 8.2 土狼时间Coyote Time
离开地面后(如走出平台边缘)的 `coyoteTime`(默认 0.12s)内仍视为"在地面"
- 这段时间内可触发地面跳跃
- 只对自然离地有效(跳跃后不产生土狼时间)
### 8.3 墙壁检测
- 在角色左右两侧各发出一条水平射线
- 检测 Wall 层
- 结果:`isWallLeft` / `isWallRight`
- 区分"可抓墙"和"不可抓墙"(通过墙壁属性标记)
---
## 9. 移动配置参数
| 参数 | 推荐值 | 说明 |
|------|--------|------|
| `walkSpeed` | 6.0 | 地面行走速度units/s|
| `runSpeed` | 9.0 | 地面奔跑速度units/s|
| `jumpForce` | 16.0 | 跳跃初始垂直速度 |
| `doubleJumpForce` | 14.0 | 双跳初始垂直速度 |
| `wallJumpAwayHForce` | 10.0 | 背墙跳水平分量 |
| `wallJumpTowardHForce` | 3.0 | 对墙跳水平分量 |
| `wallJumpVerticalForce` | 15.0 | 墙跳垂直分量 |
| `wallJumpInputLockDuration` | 0.1s | 背墙跳后水平输入锁定时长 |
| `wallGrabSlideSpeed` | 2.0 | 抓墙下滑速度(低于记忆高度时)|
| `dashSpeed` | 18.0 | 地面冲刺速度 |
| `dashDuration` | 0.18s | 地面冲刺时长 |
| `dashCooldown` | 0.5s | 冲刺冷却时间 |
| `aerialDashSpeed` | 15.0 | 空中冲刺速度 |
| `aerialDashDuration` | 0.20s | 空中冲刺时长 |
| `plungeSpeed` | 28.0 | 下冲速度 |
| `plungeImpactRadius` | 2.0 | 下冲冲击波半径 |
| `plungeBounceForce` | 4.0 | 落地后弹起速度 |
| `fallMultiplier` | 2.5 | 下落额外重力倍率 |
| `lowJumpMultiplier` | 2.0 | 短跳额外重力倍率 |
| `apexGravityScale` | 0.6 | 跳跃顶点重力缩减倍率 |
| `coyoteTimeDuration` | 0.12s | 土狼时间窗口 |
| `jumpBufferDuration` | 0.15s | 跳跃预输入缓冲窗口 |
| `groundCheckSize` | (0.8, 0.05) | 地面检测矩形尺寸 |
| `wallCheckDistance` | 0.1 | 墙壁检测射线长度 |

View File

@@ -0,0 +1,232 @@
# 06 · 形态系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**:三形态(天魂/地魂/命魂)的数据模型、切换契约、武器适配与技能体系。
---
## 目录
1. [形态设计目标](#1-形态设计目标)
2. [形态数据模型](#2-形态数据模型)
3. [形态切换契约](#3-形态切换契约)
4. [三形态规格表](#4-三形态规格表)
5. [技能体系](#5-技能体系)
6. [武器与形态联动](#6-武器与形态联动)
7. [形态配置参数](#7-形态配置参数)
---
## 1. 形态设计目标
| 目标 | 说明 |
|------|------|
| **风格差异化** | 三形态提供截然不同的战斗体验,不仅仅是皮肤替换 |
| **情境适应** | 不同区域/敌人类型鼓励不同形态,但不强制 |
| **无缝切换** | 切换不中断当前动作,下次输入时生效 |
| **可扩展** | 新增形态只需新增数据,不需要修改现有逻辑 |
---
## 2. 形态数据模型
```
DataModel FormData {
formId : ID // 形态唯一标识(如 "SkyForm"
displayName : String // 显示名称("天魂"
description : String // 一句话描述
accentColor : Color // HUD/特效色调
──────────────────────────────────
defaultWeapon : Ref<WeaponData> // 默认武器(连击动画+伤害来源+判定尺寸)
soulSkill : Ref<SkillData> // 魂技能(消耗灵力)
spiritSkill1 : Ref<SkillData> // 魄技能 1消耗魄元
spiritSkill2 : Ref<SkillData> // 魄技能 2消耗魄元
──────────────────────────────────
switchAnimation : Optional<AnimationID> // 切换前摇动画(无则立即切换)
unlockCondition : Optional<Ref<AbilityData>> // 解锁条件null=初始可用)
}
```
```
DataModel WeaponData {
weaponId : ID
formId : ID // 归属形态
──────────────────────────────────
attack1Anim : AnimationID // 第一击动画
attack2Anim : AnimationID // 第二击动画
attack3Anim : AnimationID // 第三击动画
airAttackAnim : AnimationID
upAttackAnim : AnimationID
downAttackAnim : AnimationID
──────────────────────────────────
hitBoxConfig : List<HitBoxSpec> // 各攻击段的判定区域规格
damageMultipliers: Map<AttackType, Number> // 各攻击段伤害倍率
}
```
---
## 3. 形态切换契约
### 3.1 切换输入映射
| 输入槽 | 默认形态 | 说明 |
|--------|---------|------|
| 槽位 0 | 天魂SkyForm| 键位 1 / 手柄方向键左 |
| 槽位 1 | 地魂EarthForm| 键位 2 / 手柄方向键下 |
| 槽位 2 | 命魂DeathForm| 键位 3 / 手柄方向键右 |
> **设计决策**:使用有序槽位而非形态枚举
> **原因**:未来新增第四形态时,只需增加新输入槽和新 FormData不改变现有逻辑
### 3.2 切换时机
```
玩家按下形态切换键
→ 目标形态 = 对应槽位的 FormData
目标形态 == 当前形态?
├─ 是 → 忽略输入
└─ 否 →
目标形态有切换前摇动画?
├─ 是 → 播放前摇,前摇完成后切换
└─ 否 → **立即** 更新 CurrentForm不中断当前动作
→ 发出 OnFormChanged 事件(传入新 FormData
→ WeaponSlot 接收事件,自动切换为新形态的默认武器
→ 下次攻击/技能输入时,使用新形态的动画和参数
```
### 3.3 切换的"无缝"原则
- 切换**本身无前摇**(默认配置下)
- 当前动作(如攻击、冲刺)**不被打断**
- 下一次攻击输入才体现新形态效果
- 技能(魂技能/魄技能)绑定于形态,切换后技能槽内容随之改变
---
## 4. 三形态规格表
### 4.1 天魂形态SkyForm
| 属性 | 规格 |
|------|------|
| 战斗风格 | 轻快连击,空中机动性强 |
| 攻击判定 | 较小范围,近身贴脸 |
| 连击特点 | 第三击向上抛起敌人 |
| 魂技能 | **天光斩**:前冲斩击,穿透多个敌人,灵力消耗 100 |
| 魄技能 1 | **天翔步**:瞬间向上跳跃(额外跳),魄元消耗 40 |
| 魄技能 2 | **灵鸟护体**:短暂无敌 + 反伤护盾,魄元消耗 70 |
| 定位 | 灵活流,擅长空中平台战斗 |
### 4.2 地魂形态EarthForm
| 属性 | 规格 |
|------|------|
| 战斗风格 | 重击范围大,霸体强 |
| 攻击判定 | 较大范围,有前置停顿 |
| 连击特点 | 第三击震地,范围伤害 |
| 魂技能 | **大地震怒**:跳起落下产生冲击波,灵力消耗 100 |
| 魄技能 1 | **岩甲护卫**:护盾层(吸收 X 点伤害),魄元消耗 50 |
| 魄技能 2 | **地裂踏**:踏地减速附近敌人,魄元消耗 60 |
| 定位 | 坦克流,擅长对峙 Boss 和密集敌人 |
### 4.3 命魂形态DeathForm
| 属性 | 规格 |
|------|------|
| 战斗风格 | 中等速度,弹反增益显著 |
| 攻击判定 | 适中范围,攻击后有追加效果 |
| 连击特点 | 每击命中后留下诅咒标记,第三击引爆全部标记 |
| 魂技能 | **命魂索链**:召唤锁链束缚最近敌人,灵力消耗 100 |
| 魄技能 1 | **命魂反噬**:下次弹反触发额外爆炸伤害,魄元消耗 45 |
| 魄技能 2 | **命域领域**:短暂降低附近敌人攻击力,魄元消耗 65 |
| 定位 | 弹反流,弹反策略强化,适合精通玩家 |
---
## 5. 技能体系
### 5.1 技能数据模型
```
DataModel SkillData {
skillId : ID
skillName : String
description : String
resourceType : ResourceType // SoulPower 或 SpiritPower
resourceCost : Integer // 消耗量
──────────────────────────────────
castAnimation : AnimationID // 前摇动画
castDuration : Duration // 前摇时长
effectDelay : Duration // 前摇结束后效果开始的延迟
──────────────────────────────────
effectType : SkillEffectType
effectParams : Map<String, Number> // 效果参数(因技能类型而异)
──────────────────────────────────
canCastAirborne : Boolean // 是否可在空中施放
interruptible : Boolean // 前摇是否可被打断(冲刺/受伤仍可打断)
}
```
### 5.2 技能效果类型
| 类型 | 说明 | 典型参数 |
|------|------|---------|
| `Projectile` | 发射弹射物 | `speed`, `damage`, `piercing` |
| `Melee` | 近战范围攻击 | `radius`, `damage`, `knockback` |
| `Shield` | 护盾效果 | `absorbAmount`, `duration` |
| `Buff` | 正面增益 | `buffType`, `duration`, `magnitude` |
| `Debuff` | 负面施加 | `debuffType`, `duration`, `radius` |
| `Summon` | 召唤实体 | `entityId`, `duration`, `count` |
| `Mobility` | 移动型技能 | `distance`, `direction`, `invincible` |
### 5.3 资源消耗规则
| 资源类型 | 消耗时机 | 失败处理 |
|---------|---------|---------|
| 灵力 | 技能执行前检查,通过则立即扣除 | 不足时技能键无响应 |
| 魄元 | 技能执行前检查,通过则立即扣除 | 不足时技能键无响应 |
---
## 6. 武器与形态联动
### 6.1 武器切换流程
```
OnFormChanged 事件触发
→ WeaponSlot 更新为新形态的 defaultWeapon
→ CombatHandler 刷新以下内容:
├─ 当前连击段的动画片段
├─ HitArea 尺寸与偏移
├─ DamagePacket 中的伤害倍率来源
└─ 重置连击计数器ComboState → None
```
### 6.2 护符武器覆盖
特定护符可覆盖某形态的默认武器(只改变该形态):
- 护符持有 `WeaponData` 引用
- 装备时:对应槽位的 `FormData.defaultWeapon` 临时被护符武器替代
- 卸装时:恢复原 `FormData.defaultWeapon`
---
## 7. 形态配置参数
| 参数 | 说明 |
|------|------|
| 三形态 FormData | 见第 4 节规格表 |
| 各形态武器 HitBox 尺寸 | 在各 WeaponData 中配置 |
| 技能消耗量 | 在各 SkillData 中配置 |
| 技能前摇时长 | 在各 SkillData 中配置 |
> **扩展原则**:新增形态时,只需:
> 1. 创建新的 `FormData`(含武器和技能引用)
> 2. 将其加入形态配置列表的新槽位
> 3. 为新槽位绑定输入键位
>
> 无需修改任何现有系统逻辑。

View File

@@ -0,0 +1,289 @@
# 07 · 敌人系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**敌人实体模型、AI 行为规范、Boss 系统设计与掉落体系。
---
## 目录
1. [设计原则](#1-设计原则)
2. [敌人实体模型](#2-敌人实体模型)
3. [AI 行为规范](#3-ai-行为规范)
4. [寻路与感知](#4-寻路与感知)
5. [Boss 系统](#5-boss-系统)
6. [电报系统(攻击预警)](#6-电报系统攻击预警)
7. [敌人分级体系](#7-敌人分级体系)
8. [掉落系统](#8-掉落系统)
---
## 1. 设计原则
| 原则 | 说明 |
|------|------|
| **可读性优先** | 每个攻击都有明确的预警(电报),让玩家有机会反应 |
| **弹反友好** | 大多数攻击标记为可弹反,鼓励玩家使用核心机制 |
| **行为数据化** | AI 行为通过数据配置组合,不硬编码 |
| **职责分离** | 移动、攻击、感知各为独立模块,互不依赖 |
---
## 2. 敌人实体模型
### 2.1 实体构成
```
EnemyEntity协调器
├── EnemyStats ← 属性HP、防御、速度等
├── AIController ← 行为决策BehaviorTree 或 FSM
├── MovementAgent ← 路径跟随与物理移动
├── CombatModule ← 攻击执行、HitArea 管理
├── HurtBox ← 受击响应
├── PerceptionModule ← 感知(视野、听觉、自定义触发器)
├── LootModule ← 死亡掉落处理
└── TelegraphSystem ← 攻击电报(预警视觉效果)
```
### 2.2 敌人属性模型
```
DataModel EnemyStatsData {
enemyId : ID
displayName : String
──────────────────────────────────
maxHP : Integer
defense : Number // 伤害减免0.0~1.0
poiseLevel : PoiseLevel // 霸体等级
──────────────────────────────────
moveSpeed : Number // 基础移动速度
detectionRange : Number // 视野/感知范围
attackRange : Number // 近战攻击范围
──────────────────────────────────
lootTableId : Ref<LootTableData> // 掉落表
geoReward : {min: Integer, max: Integer} // Geo 掉落范围
killCreditType : KillCreditType // 击杀积分类型(普通/精英/Boss
}
```
---
## 3. AI 行为规范
### 3.1 行为树节点类型
AI 行为通过**行为树Behavior Tree**组织,行为节点类型:
| 节点类型 | 符号 | 说明 |
|---------|------|------|
| 选择节点Selector| `?` | 依次尝试子节点,第一个成功即返回成功 |
| 序列节点Sequence| `→` | 依次执行子节点,所有成功才返回成功 |
| 条件节点Condition| `[C]` | 判断某个条件,返回成功/失败 |
| 动作节点Action| `[A]` | 执行一个具体行为 |
| 装饰节点Decorator| `{D}` | 修改子节点行为(如重复、反转、冷却)|
### 3.2 通用敌人行为树模板
```
RootSelector
├─ ? 死亡检测
│ └─ [C] HP <= 0 → [A] 播放死亡动画 → [A] 触发掉落 → [A] 销毁
├─ ? 受击响应
│ └─ [C] 正在受击 → [A] 受击动画(若霸体被打断)
├─ ? 攻击决策Selector
│ ├─ ? 近战攻击
│ │ └─ [C] 玩家在攻击范围内
│ │ → [C] 攻击冷却结束
│ │ → [A] 播放电报
│ │ → [A] 执行攻击
│ │
│ └─ ? 远程攻击
│ └─ [C] 玩家在视野内但超出近战范围
│ → [C] 远程攻击冷却结束
│ → [A] 播放电报
│ → [A] 发射弹射物
├─ ? 追击Sequence
│ ├─ [C] 已感知到玩家
│ └─ [A] 向玩家移动(路径跟随)
└─ ? 巡逻/待机Sequence
└─ [A] 执行巡逻路径(或原地待机)
```
### 3.3 AI 感知状态
| 状态 | 条件 | 行为 |
|------|------|------|
| `Idle`(待机)| 未感知到玩家 | 巡逻或原地等待 |
| `Alert`(警戒)| 听到声音或余光检测 | 转向检查,不主动追击 |
| `Chase`(追击)| 视野内确认玩家 | 向玩家移动 |
| `Combat`(战斗)| 玩家在攻击范围内 | 执行攻击序列 |
| `Stunned`(硬直)| 受到重击/被弹反 | 暂停所有行为 |
---
## 4. 寻路与感知
### 4.1 寻路规范
| 规范项 | 说明 |
|--------|------|
| 寻路类型 | 2D 导航网格NavMesh或路径点系统 |
| 更新频率 | 目标位置变化超过阈值时重新规划路径(不每帧更新)|
| 跳跃/下落 | 可配置是否允许跳跃/从平台下落追击 |
| 障碍物 | 动态障碍物(其他敌人)不重新规划路径,简单规避 |
### 4.2 感知规范
```
DataModel PerceptionConfig {
visionRange : Number // 视野半径
visionAngle : Number // 视野角度(正前方±角度)
hearingRange : Number // 听觉半径(玩家移动/攻击触发)
alertDuration : Duration // Alert 状态持续时长
lostTargetDelay : Duration // 失去视野到放弃追击的延迟
}
```
---
## 5. Boss 系统
### 5.1 Boss 实体模型
Boss 继承普通敌人的所有能力,附加以下扩展:
```
DataModel BossData extends EnemyStatsData {
bossName : String
arenaId : ID // 专属战斗场景
──────────────────────────────────────
phases : List<BossPhaseData> // 阶段列表(按顺序触发)
introSequenceId : ID // 入场演出序列
defeatSequenceId: ID // 失败演出序列
──────────────────────────────────────
bgmPhase1 : AudioID
bgmPhase2 : Optional<AudioID> // 阶段变更时切换音乐
}
DataModel BossPhaseData {
phaseIndex : Integer
hpThreshold : Number // 触发阶段变更的 HP 百分比0~1.0
availableAttacks: List<Ref<AttackPatternData>> // 该阶段可用攻击
attackWeights : Map<ID, Number> // 各攻击的选取权重
aggressionScale : Number // 攻击频率倍率
}
```
### 5.2 Boss 攻击模式模型
```
DataModel AttackPatternData {
patternId : ID
patternName : String
──────────────────────────────
telegraphType : TelegraphType // 电报类型
telegraphDuration: Duration // 电报持续时间
──────────────────────────────
attackType : BossAttackType // 攻击类型
executionParams : Map<String, Number> // 执行参数
cooldown : Duration // 攻击后冷却
──────────────────────────────
isParryable : Boolean // 是否可被玩家弹反
counterWindowId : Optional<ID> // 弹反后的脆弱窗口
}
```
### 5.3 Boss 阶段转换流程
```
Boss HP 降至 PhaseData.hpThreshold 以下
→ AIController 暂停(短暂无敌帧 0.5s
→ 播放相变动画/特效
→ 发出 OnBossPhaseChanged 事件bossId, newPhase
→ 更新可用攻击列表和攻击权重
→ 音频系统切换音乐(若配置了 bgmPhase2
→ 恢复 AIController
```
---
## 6. 电报系统(攻击预警)
所有攻击**必须**有电报,给玩家反应时间:
| 电报类型 | 视觉表现 | 适用场景 |
|---------|---------|---------|
| `PoseHold` | 攻击前摇姿势定格 | 近战重击 |
| `GroundIndicator` | 地面出现警告范围标记 | 落地冲击、AoE |
| `TrajectoryLine` | 显示弹射物飞行路径 | 精准弹射物 |
| `BodyGlow` | 敌人身体特定颜色发光 | 不可弹反:红色;可弹反:黄色 |
| `SoundCue` | 特定音效(与视觉结合)| 所有攻击必备 |
### 电报颜色规范
| 颜色 | 含义 |
|------|------|
| 黄色/金色 | 可弹反攻击 |
| 红色/橙色 | 不可弹反攻击 |
| 紫色 | 特殊/Boss 专属机制 |
| 白色 | 环境危险(非敌人攻击)|
---
## 7. 敌人分级体系
| 等级 | 名称 | AI 复杂度 | 霸体 | 典型特征 |
|------|------|----------|------|---------|
| T1 | 杂兵 | 简单2-3种行为| Fragile | 单一攻击,跑向玩家 |
| T2 | 普通敌人 | 中等3-5种行为| Standard | 有攻击组合,有简单规避 |
| T3 | 精英怪 | 较高5-7种行为| Armored | 有阶段变化,有独特机制 |
| T4 | Boss | 高多阶段完整AI| Boss | 多阶段、多攻击、电报复杂 |
---
## 8. 掉落系统
### 8.1 掉落表模型
```
DataModel LootTableData {
tableId : ID
entries : List<LootEntry>
guaranteedDrops : List<Ref<ItemData>> // 必掉物品(如 Boss 必掉能力道具)
}
DataModel LootEntry {
itemId : Ref<ItemData>
baseWeight : Number // 基础权重(相对概率)
minCount : Integer
maxCount : Integer
difficultyScale : Number // 难度缩放系数Easy 减少Hard 增加)
}
```
### 8.2 掉落概率计算
```
各 Entry 的实际权重 = baseWeight × 难度系数
总权重 = 所有 Entry 实际权重之和
某 Entry 概率 = 该 Entry 实际权重 / 总权重
保底机制:
若稀有物品连续 20 次未掉落 → 下次强制掉落
保底计数存入 SaveData跨死亡保留
```
### 8.3 掉落触发流程
```
OnEnemyDied 事件触发
→ 掉落系统读取死亡敌人的 LootTableData
→ 计算本次掉落结果(权重随机 + 保底检查)
→ 在死亡位置生成掉落物实体
→ Geo 直接加入玩家账户(不生成实体,减少拾取摩擦)
→ 特殊道具生成拾取实体,玩家走近自动拾取
```

View File

@@ -0,0 +1,272 @@
# 08 · 世界系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**:地图结构、房间系统、场景切换管线、存档点与可交互物。
---
## 目录
1. [世界结构层级](#1-世界结构层级)
2. [世界数据模型](#2-世界数据模型)
3. [场景切换管线](#3-场景切换管线)
4. [存档点系统](#4-存档点系统)
5. [可交互物规范](#5-可交互物规范)
6. [收集品系统](#6-收集品系统)
7. [世界状态事件目录](#7-世界状态事件目录)
---
## 1. 世界结构层级
```
World全局
└── Region区域如 废墟之都 / 灵泉洞窟)
└── Zone地区区域内的子区域
└── Room房间最小加载单元
└── Sector区块房间内的逻辑分区
```
| 层级 | 说明 | 数量(预期)|
|------|------|-----------|
| World | 整个游戏世界 | 1 |
| Region | 玩家感知的"大区",通常有独特视觉主题 | 5-8 |
| Zone | Region 内的子区域,对玩家透明(内部分组)| 20-30 |
| Room | 场景加载单元,对应单个可加载场景文件 | 100-200 |
---
## 2. 世界数据模型
### 2.1 地区Region数据
```
DataModel RegionData {
regionId : ID
displayName : String
description : String
──────────────────────────────────
ambientAudioId : AudioID // 区域环境音乐
visualTheme : Ref<VisualThemeData> // 色调/视觉风格
──────────────────────────────────
rooms : List<Ref<RoomData>> // 该区域包含的所有房间
connections : List<RegionConnection> // 与其他区域的连接
}
```
### 2.2 房间Room数据
```
DataModel RoomData {
roomId : ID
regionId : ID // 归属区域
──────────────────────────────────
sceneAssetId : ID // 场景资源引用
spawnPoints : Map<String, Vector2> // 命名生成点(对应门/入口的落地位置)
──────────────────────────────────
connections : List<RoomConnection> // 与其他房间的连接(门/通道)
hasBoss : Boolean
bossId : Optional<ID>
──────────────────────────────────
discoverable : Boolean // 是否出现在地图上
mapGridPos : Vector2 // 在地图 UI 中的格位置
}
```
### 2.3 房间连接模型
```
DataModel RoomConnection {
fromRoomId : ID
fromDoorId : String // 本房间出口标识
toRoomId : ID
toDoorId : String // 目标房间入口标识
──────────────────────────────────
direction : ConnectionDirection // 上/下/左/右
gateId : Optional<ID> // 门控null=无门限制)
}
```
---
## 3. 场景切换管线
### 3.1 切换触发条件
玩家进入出口触发器Exit Trigger触发场景切换流程
```
玩家进入 ExitTrigger
→ 读取 RoomConnection目标房间、目标入口
→ 检查门控条件(若有 gateId
├─ 未满足 → 门保持关闭,播放提示
└─ 满足 → 继续流程
→ 发出 OnRoomTransitionBegin 事件
→ 播放转场动画(淡出、卷帘等)
→ 保存当前房间的必要运行时状态(敌人死亡/开关/收集品)
→ 异步加载目标场景
→ 等待加载完成
→ 将玩家生成到目标房间的指定 spawnPoint
→ 播放转场进入动画
→ 发出 OnRoomTransitionComplete 事件roomId
```
### 3.2 玩家状态保留
| 状态 | 切换时处理 |
|------|-----------|
| HP | 保留(不恢复)|
| 灵力 | 保留 |
| 魄元 | 保留 |
| 灵泉 | 保留 |
| 当前形态 | 保留 |
| Geo | 保留 |
| 无敌帧 | 清除 |
| 速度/冲量 | 清除(生成时重置)|
### 3.3 房间状态持久化
| 状态类型 | 持久化条件 |
|---------|----------|
| 敌人死亡 | 永久(本局游戏内)|
| 开关/机关激活 | 按道具/进程判断(大多数永久)|
| 收集品拾取 | 永久 |
| Boss 死亡 | 永久,切换不重置 |
| 普通敌人死亡 | 重新进入房间后刷新 |
---
## 4. 存档点系统
### 4.1 存档点数据模型
```
DataModel BonfireData {
bonfireId : ID
roomId : ID
position : Vector2
──────────────────────────────────
displayName : String
isActivated : Boolean // 运行时状态,存入 SaveData
fastTravelEnabled: Boolean // 是否支持快速旅行
}
```
### 4.2 激活流程
```
玩家靠近存档点 → 显示交互提示
玩家按下交互键:
若未激活:
→ 播放激活动画
→ 设置 BonfireData.isActivated = true
→ 解锁快速旅行列表中该存档点
激活后(每次交互):
→ 恢复玩家 HP 至最大值
→ 恢复灵泉
→ 重置敌人刷新状态(普通敌人复活)
→ 保存游戏进度到 SaveData
→ 发出 OnBonfireRested 事件
```
> **设计决策**:存档点恢复灵泉
> **原因**:灵泉是 HP 恢复资源,应随存档点恢复;但死亡后不恢复灵泉,强化死亡惩罚
### 4.3 死亡重生规则
- 死亡时:玩家重生于**最后一次使用的存档点**
- 重生时HP 恢复至最大值,**灵泉不恢复**
- 玩家持有的 Geo 遗留在死亡位置(遗骸机制,见 [进程系统](09_ProgressionSystem.md)
---
## 5. 可交互物规范
### 5.1 交互契约
```
Interface IInteractable {
canInteract(player: IPlayerEntity) → Boolean
onInteract(player: IPlayerEntity) → Void
getPromptText() → String
}
```
所有可交互物实现此接口,由玩家系统负责检测并调用。
### 5.2 标准交互物类型
| 类型 | 触发条件 | 行为 |
|------|---------|------|
| 存档点Bonfire| 玩家靠近 + 按交互键 | 激活 / 休息 |
| NPC | 玩家靠近 + 按交互键 | 开始对话(见叙事系统)|
| 商店Vendor| 玩家靠近 + 按交互键 | 打开商店 UI |
| 门Gate| 玩家靠近,满足条件自动开启 | 开启动画 + 移除碰撞 |
| 传送门Portal| 玩家触碰 | 传送到目标地点 |
| 机关Mechanism| 玩家靠近 + 按交互键 | 激活关联机关(开门/升降台)|
| 收集品Collectible| 玩家触碰 / 按键 | 拾取(见收集品系统)|
---
## 6. 收集品系统
### 6.1 收集品数据模型
```
DataModel CollectibleData {
collectibleId : ID
collectibleType : CollectibleType // 见下表
roomId : ID // 所在房间
──────────────────────────────────
displayName : String
description : String
──────────────────────────────────
pickupEffect : PickupEffectType // 拾取时发生什么
pickupParams : Map<String, Number>
}
```
### 6.2 收集品类型
| 类型 | 说明 | 效果 |
|------|------|------|
| `MaxHPUp` | 生命上限增加 | `maxHP += pickupParams["amount"]` |
| `MaxSoulUp` | 灵力上限增加 | `maxSoul += pickupParams["amount"]` |
| `MaxSpiritUp` | 魄元上限增加 | `maxSpirit += pickupParams["amount"]` |
| `AbilityUnlock` | 解锁新能力 | 发出 `OnAbilityUnlocked` 事件 |
| `MapFragment` | 解锁区域地图 | 揭示对应区域地图 |
| `Lore` | 世界观文本 | 加入图鉴/日志 |
| `GeoCache` | 货币缓存 | 立即给予 Geo |
### 6.3 拾取流程
```
玩家触碰 CollectibleEntity
→ 检查 SaveData 该 collectibleId 是否已拾取
├─ 已拾取 → 忽略(不重复拾取)
└─ 未拾取 →
→ 播放拾取动画/音效
→ 执行 pickupEffect更新玩家数据
→ 将 collectibleId 标记为已拾取(写入 SaveData
→ 发出 OnCollectiblePickedUp 事件
→ 销毁实体(本局不再出现)
```
---
## 7. 世界状态事件目录
| 事件 | 触发时机 | 载荷 |
|------|---------|------|
| `OnRoomTransitionBegin` | 场景切换开始 | `fromRoomId, toRoomId` |
| `OnRoomTransitionComplete` | 场景切换完成 | `roomId` |
| `OnRoomFirstVisit` | 首次进入某房间 | `roomId` |
| `OnBonfireActivated` | 首次激活存档点 | `bonfireId` |
| `OnBonfireRested` | 使用存档点休息 | `bonfireId` |
| `OnCollectiblePickedUp` | 拾取收集品 | `collectibleId, collectibleType` |
| `OnGateOpened` | 门控解锁 | `gateId, roomId` |
| `OnBossRoomEntered` | 进入 Boss 房间 | `roomId, bossId` |
| `OnBossDefeated` | Boss 死亡 | `bossId` |

View File

@@ -0,0 +1,246 @@
# 09 · 进程系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**能力解锁、门控系统、Geo 遗骸机制与游戏完成度追踪。
---
## 目录
1. [进程设计原则](#1-进程设计原则)
2. [能力系统](#2-能力系统)
3. [门控系统](#3-门控系统)
4. [Geo 遗骸机制](#4-geo-遗骸机制)
5. [完成度追踪](#5-完成度追踪)
6. [护符系统](#6-护符系统)
7. [进程事件目录](#7-进程事件目录)
---
## 1. 进程设计原则
| 原则 | 说明 |
|------|------|
| **有机探索** | 能力解锁带来新的区域通路,而非仅仅提升战斗数值 |
| **无死门** | 不存在因获取顺序错误而无法继续的情况 |
| **多路径** | 大多数门控有多种解锁方式(能力/密钥/支线)|
| **数据驱动** | 所有解锁条件用数据定义,逻辑层不硬编码 |
---
## 2. 能力系统
### 2.1 能力数据模型
```
DataModel AbilityData {
abilityId : ID
displayName : String
description : String
──────────────────────────────────
abilityType : AbilityType // 移动/战斗/探索/形态
unlockSource : UnlockSource // 如何获得Boss掉落/收集品/商店)
──────────────────────────────────
gatesUnlocked : List<GateType> // 该能力解锁哪些门控类型
movementFlags : Flags<MovementAbilityFlag> // 移动系统的能力开关
combatFlags : Flags<CombatAbilityFlag> // 战斗系统的能力开关
}
```
### 2.2 核心能力列表
| 能力 ID | 名称 | 获取方式 | 解锁内容 |
|--------|------|---------|---------|
| `DoubleJump` | 双跳 | 初始能力(教程)| 空中二段跳 |
| `WallGrab` | 墙抓 | 区域 1 Boss | 抓墙/墙跳 |
| `AerialDash` | 空中冲刺 | 区域 2 Boss | 8方向空中冲刺 |
| `Plunge` | 下冲 | 区域 3 收集品 | 下冲攻击 |
| `SwimAbility` | 游泳 | 区域 4 收集品 | 进入液体区域 |
| `SkyFormUnlock` | 天魂解锁 | 剧情节点 | 天魂形态 |
| `EarthFormUnlock` | 地魂解锁 | 剧情节点 | 地魂形态 |
| `DeathFormUnlock` | 命魂解锁 | 剧情节点 | 命魂形态 |
| `SoulSkillUpgrade` | 魂技能升级 | 特定 NPC | 魂技能二阶效果 |
### 2.3 能力解锁流程
```
玩家获得 AbilityData来源Boss掉落/拾取/购买)
→ 发出 OnAbilityUnlocked 事件abilityId
→ 更新 SaveData.unlockedAbilities 集合
→ 玩家系统注册新 movementFlags 和 combatFlags
→ 门控系统重新评估所有已知门控状态
→ 地图更新(若新能力打开了新路径)
```
---
## 3. 门控系统
### 3.1 门控数据模型
```
DataModel GateData {
gateId : ID
roomId : ID
displayName : String
──────────────────────────────────
conditions : List<GateCondition> // 解锁条件满足任一即可OR逻辑
unlockMode : GateUnlockMode // 永久解锁 / 单次通过
──────────────────────────────────
lockedVisual : VisualState
unlockedVisual : VisualState
tooltipText : String // 玩家尝试进入未解锁门时显示
}
DataModel GateCondition {
conditionType : GateConditionType
targetId : ID // 依条件类型能力ID/密钥ID/任务ID
quantity : Optional<Integer> // 若需要数量(如 Geo 消耗)
}
```
### 3.2 门控条件类型
| 条件类型 | 说明 | 示例 |
|---------|------|------|
| `HasAbility` | 玩家已解锁某能力 | 需要双跳才能通过高台 |
| `HasItem` | 玩家持有某关键道具 | 需要古城钥匙 |
| `BossDefeated` | 某 Boss 已被击败 | 区域终 Boss 死后大门开启 |
| `QuestComplete` | 某任务/剧情已完成 | NPC 认可后打开秘道 |
| `PaidGeo` | 支付 Geo商人/通行税)| 进入特定区域收费 |
| `WorldFlag` | 某世界标记为真 | 触发了某机关后解锁 |
---
## 4. Geo 遗骸机制
### 4.1 规则规范
```
玩家死亡:
→ 当前持有的 Geo 全部转移为遗骸GeoShade
→ 遗骸实体生成于死亡位置
→ 玩家的 Geo 归零
→ 若已有遗骸(上一次死亡未取回):
→ 旧遗骸永久消失(及其中的 Geo
→ 新遗骸替代旧遗骸
玩家到达遗骸位置并触碰:
→ 恢复遗骸中的全部 Geo
→ 遗骸消失
→ 发出 OnGeoShadeRecovered 事件
```
### 4.2 数据模型
```
DataModel GeoShadeState {
exists : Boolean
geoAmount : Integer
deathPosition : Vector2
roomId : ID // 遗骸所在房间(跨房间持久存在)
}
```
> **设计决策**:再次死亡前遗骸永久消失
> **原因**:来自 Hollow Knight 的核心惩罚机制。失去 Geo 的焦虑推动玩家谨慎前进,而非莽撞行事。
---
## 5. 完成度追踪
### 5.1 完成度指标
```
DataModel CompletionData {
──── 主线进程 ────────────────────
mainQuestStage : Integer // 当前主线阶段
endingUnlocked : Optional<EndingType>
──── 探索进度 ────────────────────
roomsVisited : Set<ID> // 已访问房间
mapRevealPercent: Number // 0.0~1.0
──── 收集进度 ────────────────────
maxHPUpCount : Integer // 已拾取 HP 上限碎片数
maxSoulUpCount : Integer // 已拾取灵力上限碎片数
maxSpiritUpCount: Integer // 已拾取魄元上限碎片数
lorePiecesFound : Set<ID>
──── 挑战进度 ────────────────────
bossesDefeated : Set<ID>
bossesParried : Set<ID> // 弹反过 Boss 至少一次
secretsFound : Set<ID>
}
```
### 5.2 三结局条件
| 结局 | 触发条件 |
|------|---------|
| **结局 A**(普通结局)| 击败最终 Boss未满足结局 B/C 条件 |
| **结局 B**(完全结局)| 击败所有 Boss + 主线所有剧情节点 + 特定 NPC 任务完成 |
| **结局 C**(隐藏结局)| 发现隐藏区域、触发特定世界标记序列 + 结局 B 条件 |
---
## 6. 护符系统
### 6.1 护符数据模型
```
DataModel CharmData {
charmId : ID
displayName : String
description : String
──────────────────────────────────
notchCost : Integer // 装备槽位消耗
──────────────────────────────────
effects : List<CharmEffect>
comboCharms : Optional<List<ID>> // 与哪些护符组合触发强化效果
comboEffects : Optional<List<CharmEffect>>
}
DataModel CharmEffect {
effectType : CharmEffectType
params : Map<String, Number>
}
```
### 6.2 护符槽位规则
| 规则项 | 说明 |
|--------|------|
| 初始槽位 | 3 个 Notch |
| 扩展方式 | 特定 NPC付 Geo增加 Notch |
| 最大 Notch | 11 个(初始 3 + 最多可购买 8|
| 过量装备 | 可强行装备超过槽位(超出则 HP 上限临时减半)|
### 6.3 护符效果类型
| 类型 | 说明 | 参数 |
|------|------|------|
| `StatModifier` | 修改某资源上限/恢复量 | `statType`, `delta` |
| `DamageModifier` | 修改某类攻击的伤害倍率 | `attackType`, `multiplier` |
| `WeaponOverride` | 覆盖某形态的武器 | `formId`, `weaponId` |
| `SkillModifier` | 修改技能效果参数 | `skillId`, `paramKey`, `newValue` |
| `AerialDashCount` | 增加空中冲刺充能次数 | `additionalCharges` |
| `ParryWindowExpand` | 扩大弹反时间窗口 | `addedSeconds` |
| `OnHitEffect` | 命中时附加效果 | `effectType`, `chance` |
---
## 7. 进程事件目录
| 事件 | 触发时机 | 载荷 |
|------|---------|------|
| `OnAbilityUnlocked` | 获得新能力 | `abilityId` |
| `OnGateUnlocked` | 门控条件满足,门开启 | `gateId` |
| `OnCharmEquipped` | 装备护符 | `charmId, slotIndex` |
| `OnCharmUnequipped` | 卸下护符 | `charmId` |
| `OnCharmComboActivated` | 护符组合效果触发 | `charmIds: List<ID>` |
| `OnGeoShadeCreated` | 死亡创建遗骸 | `position, amount` |
| `OnGeoShadeRecovered` | 拾取遗骸 | `amount` |
| `OnGeoShadeLost` | 遗骸永久消失 | `amount` |
| `OnEndingUnlocked` | 结局条件满足 | `endingType` |
| `OnCompletionMilestone` | 完成度达到里程碑 | `milestoneId, percent` |

View File

@@ -0,0 +1,168 @@
# 10 · UI 系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**:游戏 UI 分层架构、HUD 元素规范、菜单系统与 UI 事件契约。
---
## 目录
1. [UI 设计原则](#1-ui-设计原则)
2. [UI 分层架构](#2-ui-分层架构)
3. [HUD 规范](#3-hud-规范)
4. [菜单系统](#4-菜单系统)
5. [UI 数据绑定契约](#5-ui-数据绑定契约)
6. [UI 事件目录](#6-ui-事件目录)
---
## 1. UI 设计原则
| 原则 | 说明 |
|------|------|
| **非侵入** | HUD 元素尽量小,不遮挡游戏视野 |
| **信息即时** | 关键状态变化立即反映HP减少不延迟|
| **纯响应式** | UI 不主动查询游戏状态,只响应事件 |
| **逻辑无关** | UI 不包含任何游戏逻辑,仅负责显示与交互 |
---
## 2. UI 分层架构
从最低层到最高层:
| 层级 | 名称 | 内容 | 说明 |
|------|------|------|------|
| Layer 0 | Game World | 游戏本体 | 非 UI |
| Layer 1 | World UI | 对话气泡、头顶血条 | 跟随世界坐标 |
| Layer 2 | HUD | 血量/资源/护符 | 全程显示 |
| Layer 3 | Notification | 弹出提示、成就 | 临时覆盖 |
| Layer 4 | Overlay Menu | 暂停、设置 | 半透明遮罩 |
| Layer 5 | Full-screen | 存档读取、过场动画 | 完全遮盖 |
| Layer 6 | Transition | 场景转场动画 | 最高优先级 |
---
## 3. HUD 规范
### 3.1 HUD 元素列表
| 元素 | 位置 | 显示内容 |
|------|------|---------|
| HP 条 | 左上角 | 当前 HP / 最大 HP格子式显示 |
| 灵泉指示 | HP 条下方 | 灵泉数量图标×N|
| 灵力条 | 左下角 | 当前灵力(填充环形或条形)|
| 魄元条 | 灵力条旁 | 当前魄元 |
| 形态指示 | 右下角 | 当前形态图标 + 三形态小图标 |
| 技能槽 | 形态指示旁 | 当前形态的魂技能/魄技能图标 + 冷却遮罩 |
| Geo 计数 | 右上角(可选)| 当前 Geo 数量 |
| Boss 血条 | 屏幕下方居中 | 仅 Boss 战显示,含阶段分段标记 |
### 3.2 HP 显示规范
- 每格 = 固定 HP 量(如每格 = 50 HP
- 满格:实心图标
- 半格(受伤未满格):半空状态
- 空格:空心图标
- 当前 HP < 20%:格子闪烁警告
- HP 变化时:格子动画(减少时格子渐消,恢复时渐现)
### 3.3 形态指示规范
- 当前激活形态:大图标高亮
- 其他两形态:小图标、低亮度
- 切换时:当前形态图标播放切换动画,新形态高亮
---
## 4. 菜单系统
### 4.1 菜单层级
```
主菜单
├── 继续游戏
├── 新游戏
├── 存档选择
└── 设置
├── 图像设置
├── 音频设置
├── 操控设置
└── 辅助设置
暂停菜单(游戏中)
├── 继续
├── 地图
├── 护符装备
├── 道具/图鉴
├── 设置
└── 返回主菜单(确认弹窗)
```
### 4.2 菜单状态管理
```
菜单系统维护一个菜单栈Stack:
打开菜单:
→ 新菜单 push 到栈顶
→ 暂停游戏时间(若为暂停菜单)
→ 新菜单获得输入焦点
关闭/返回:
→ 当前菜单 pop
→ 若栈空 → 游戏恢复运行
关闭所有菜单:
→ 清空栈
→ 游戏立即恢复
```
---
## 5. UI 数据绑定契约
UI 不直接访问游戏数据,通过以下接口获取信息:
```
Interface IHUDDataProvider {
getPlayerHP() → {current: Integer, max: Integer}
getPlayerSoul() → {current: Integer, max: Integer}
getPlayerSpirit() → {current: Integer, max: Integer}
getSpringCount() → Integer
getCurrentForm() → FormData
getActiveCharms() → List<CharmData>
getGeoAmount() → Integer
}
Interface IBossHUDProvider {
getBossHP() → {current: Integer, max: Integer}
getBossPhaseCount() → Integer
getBossPhaseThresholds() → List<Number> // 各阶段 HP 百分比
getBossName() → String
}
```
UI 监听事件更新显示,不轮询接口(接口仅供初始化或主动请求使用)。
---
## 6. UI 事件目录
| 事件 | 触发者 | UI 响应 |
|------|--------|---------|
| `OnPlayerHPChanged` | 玩家系统 | 刷新 HP 格子 |
| `OnPlayerSoulChanged` | 玩家系统 | 刷新灵力条 |
| `OnPlayerSpiritChanged` | 玩家系统 | 刷新魄元条 |
| `OnSpringCountChanged` | 玩家系统 | 刷新灵泉图标 |
| `OnFormChanged` | 形态系统 | 刷新形态指示 |
| `OnBossEncounterBegin` | 世界系统 | 显示 Boss 血条 |
| `OnBossHPChanged` | Boss | 刷新 Boss 血条 |
| `OnBossPhaseChanged` | Boss | Boss 血条阶段标记动画 |
| `OnBossDefeated` | Boss | 隐藏 Boss 血条(动画)|
| `OnSkillCooldownUpdated` | 技能系统 | 刷新技能冷却遮罩 |
| `OnCharmEquipped` | 进程系统 | 刷新护符栏 |
| `OnNotification` | 任意系统 | 显示弹出通知 |
| `OnMenuOpen` | 输入系统 | 打开暂停菜单 |
| `OnDialogueStarted` | 叙事系统 | 显示对话框 |
| `OnDialogueEnded` | 叙事系统 | 隐藏对话框 |

View File

@@ -0,0 +1,182 @@
# 11 · 音频系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**:音频分层架构、自适应音乐系统、音效触发契约与音量管理。
---
## 目录
1. [音频设计原则](#1-音频设计原则)
2. [音频分层架构](#2-音频分层架构)
3. [自适应音乐系统](#3-自适应音乐系统)
4. [音效SFX规范](#4-音效sfx规范)
5. [音频总线与混音](#5-音频总线与混音)
6. [音频事件目录](#6-音频事件目录)
---
## 1. 音频设计原则
| 原则 | 说明 |
|------|------|
| **情境响应** | 音乐随战斗/探索状态自动切换层级 |
| **游戏反馈优先** | 每个玩家动作都有清晰音效反馈(攻击/受伤/弹反)|
| **非侵入性** | 非战斗状态音乐保持安静,不与游戏音效竞争 |
| **数据驱动** | 音频配置和触发条件通过数据定义,不硬编码 |
---
## 2. 音频分层架构
```
AudioSystem协调器
├── MusicPlayer ← 背景音乐、自适应分层
├── AmbiencePlayer ← 环境音效(风声、水声等)
├── SFXPool ← 音效池,管理 SFX 实例
└── VoicePlayer ← 对话/语音(若有)
```
---
## 3. 自适应音乐系统
### 3.1 音乐状态机
```
[Silence] ──加载区域──► [Exploration]
[Exploration] ──感知敌人──► [CombatIntro]
[CombatIntro] ──0.5s──► [Combat]
[Combat] ──清场/脱战─────► [CombatOutro]
[CombatOutro] ──1.0s────► [Exploration]
[Exploration] ──进入Boss房间──► [BossIntro]
[BossIntro] ──播放完毕──► [BossPhase1]
[BossPhase1] ──阶段转换──► [BossPhase2]
[BossPhase2] ──Boss死亡──► [BossVictory]
[BossVictory] ──播放完毕──► [Exploration]
```
### 3.2 音乐分层Layer
每首区域音乐由多个同步的音频层叠加:
| 层 | 说明 | 探索 | 战斗 |
|----|------|:----:|:----:|
| Base | 旋律主干 | ✓ | ✓ |
| Percussion | 打击乐 | 弱 | ✓ |
| Tension | 紧张弦乐 | ✗ | ✓ |
| Ambient | 环境填充 | ✓ | ✗ |
| BossLayer | Boss 专属 | ✗ | Boss战 |
层级混合通过音量淡入淡出crossfade实现切换时间可配置。
### 3.3 音乐数据模型
```
DataModel MusicData {
musicId : ID
regionId : ID // 归属区域
──────────────────────────────────
layers : Map<MusicLayer, AudioID> // 各层音频资源
bpm : Integer // 用于节拍同步切换
loopStart : Duration
loopEnd : Duration
──────────────────────────────────
transitionMode : TransitionMode // 即时/等待小节/淡入淡出
crossfadeDuration: Duration
}
```
---
## 4. 音效SFX规范
### 4.1 音效触发契约
```
Interface IAudioTrigger {
playOneShot(audioId: AudioID, position: Optional<Vector2>) → Void
playLooping(audioId: AudioID, handle: out AudioHandle) → Void
stopLooping(handle: AudioHandle) → Void
playWithVariation(audioId: AudioID, pitchRange: {min,max}) → Void
}
```
### 4.2 玩家动作音效列表
| 动作 | 音效 | 变调范围 |
|------|------|---------|
| 普通攻击 1/2/3 | `sfx_attack_1/2/3` | ±0.05 |
| 受伤 | `sfx_hurt` | ±0.1 |
| 死亡 | `sfx_death` | — |
| 弹反成功 | `sfx_parry_success` | — |
| 弹反失败(时机错误)| `sfx_parry_fail` | — |
| 跳跃 | `sfx_jump` | ±0.08 |
| 双跳 | `sfx_double_jump` | ±0.05 |
| 冲刺 | `sfx_dash` | — |
| 空中冲刺 | `sfx_aerial_dash` | — |
| 下冲落地 | `sfx_plunge_land` | — |
| 墙跳 | `sfx_wall_jump` | ±0.05 |
| 形态切换 | `sfx_form_switch_{formId}` | — |
| 技能施放 | `sfx_skill_{skillId}` | — |
| 灵泉使用 | `sfx_spring_use` | — |
| 拾取 Geo | `sfx_geo_collect` | ±0.1 |
### 4.3 音效变调规则
- **随机变调**pitchVariation相同音效连续触发时随机微调音调避免"机器声"感
- **连击变调**:攻击 1→2→3 音效各自递进(音调略微提升,体现连击感)
---
## 5. 音频总线与混音
### 5.1 音频总线层级
```
Master Bus
├── Music Bus
│ ├── Exploration Layer
│ ├── Combat Layer
│ └── Boss Layer
├── SFX Bus
│ ├── Player SFX
│ ├── Enemy SFX
│ └── World SFX
└── UI Bus
├── Menu SFX
└── Notification SFX
```
### 5.2 音量控制规范
| 总线 | 用户可调 | 默认值 |
|------|---------|--------|
| Master Bus | ✓ | 100% |
| Music Bus | ✓ | 80% |
| SFX Bus | ✓ | 100% |
| UI Bus | ✓ | 90% |
> 所有用户音量设置持久化到 UserSettings独立于 SaveData
---
## 6. 音频事件目录
音频系统监听以下事件执行音频响应:
| 监听事件 | 音频响应 |
|---------|---------|
| `OnPlayerAttack` | 播放攻击音效 |
| `OnPlayerHurt` | 播放受伤音效 |
| `OnPlayerDied` | 播放死亡音效,切换到 Silence |
| `OnParrySuccess` | 播放弹反成功音效 + 子弹时间音效处理 |
| `OnFormChanged` | 播放形态切换音效 |
| `OnBonfireRested` | 播放存档音效 |
| `OnBossEncounterBegin` | 切换到 BossIntro 音乐 |
| `OnBossPhaseChanged` | 切换音乐层/音乐轨道 |
| `OnBossDefeated` | 播放 BossVictory 音乐 |
| `OnRoomTransitionBegin` | 淡出当前音乐 |
| `OnRoomTransitionComplete` | 根据新房间/区域加载新音乐 |
| `OnCollectiblePickedUp` | 播放拾取音效(按类型)|
| `OnEnemyDied` | 播放敌人死亡音效 |

View File

@@ -0,0 +1,165 @@
# 12 · 经济系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**Geo 货币体系、商店系统、掉落经济平衡与消费循环设计。
---
## 目录
1. [经济设计原则](#1-经济设计原则)
2. [Geo 货币体系](#2-geo-货币体系)
3. [商店系统](#3-商店系统)
4. [物品数据模型](#4-物品数据模型)
5. [经济平衡参数](#5-经济平衡参数)
6. [经济事件目录](#6-经济事件目录)
---
## 1. 经济设计原则
| 原则 | 说明 |
|------|------|
| **单一货币** | 只有 Geo避免多货币系统带来的混乱 |
| **死亡有意义** | 遗骸机制使 Geo 有风险感,强化决策权重 |
| **非强制消费** | 所有商店物品为锦上添花,不买也能通关 |
| **价格成长** | 后期物品价格随区域进程递增 |
---
## 2. Geo 货币体系
### 2.1 Geo 获取来源
| 来源 | 数量范围 | 说明 |
|------|---------|------|
| 击杀普通敌人 | 1-15 | 随敌人等级变化 |
| 击杀精英怪 | 20-60 | |
| 击杀 Boss | 100-500 | |
| 拾取 GeoCache 收集品 | 固定值 | 地图中隐藏的 Geo 堆 |
| 出售物品 | 物品定价 50% | 部分物品可出售 |
| 完成 NPC 任务 | 任务奖励 | |
### 2.2 Geo 消耗途径
| 途径 | 数量范围 | 说明 |
|------|---------|------|
| 商店购买护符 | 100-1200 | 主要消耗 |
| 商店购买消耗品 | 20-200 | |
| Notch 扩展 | 递增150/300/500...| 扩展护符槽位 |
| 门控通行费 | 固定值 | 某些区域收费通道 |
| NPC 服务 | 固定值 | 特殊 NPC 的服务费 |
### 2.3 Geo 上限
| 规则 | 说明 |
|------|------|
| 携带上限 | 无上限(设计上不设置)|
| 遗骸上限 | 携带量全部转为遗骸 |
| 显示格式 | 整数,无小数点 |
---
## 3. 商店系统
### 3.1 商店数据模型
```
DataModel ShopData {
shopId : ID
shopkeeperNpcId : ID // 关联 NPC
──────────────────────────────────
inventory : List<ShopItemEntry>
refreshCondition: Optional<RefreshCondition> // 何时刷新库存
welcomeText : String
}
DataModel ShopItemEntry {
itemId : Ref<ItemData>
basePrice : Integer
stock : Optional<Integer> // null = 无限库存
availableCondition: Optional<GateCondition> // 解锁条件null=始终可购)
discountFlags : Optional<Flags<DiscountSource>> // 折扣来源
}
```
### 3.2 购买流程
```
玩家在商店 UI 选择物品并确认购买:
→ 检查玩家 Geo >= itemPrice
├─ 不足 → 显示"Geo 不足"提示,取消
└─ 足够 →
→ 扣除 playerGeo -= itemPrice
→ 将物品加入玩家库存(或直接应用效果)
→ 若有 stockstock -= 1
→ stock == 0 时,从列表中移除该项
→ 发出 OnShopPurchase 事件
```
---
## 4. 物品数据模型
```
DataModel ItemData {
itemId : ID
displayName : String
description : String
──────────────────────────────────
itemType : ItemType // 护符/消耗品/关键道具/图鉴
stackable : Boolean
maxStack : Optional<Integer> // null = 无限叠加
──────────────────────────────────
useEffect : Optional<Ref<UseEffectData>> // 使用效果(消耗品)
sellValue : Optional<Integer> // null = 不可出售
}
```
### 4.1 物品类型
| 类型 | 说明 | 叠加 |
|------|------|------|
| `Charm` | 护符,装备型 | 不叠加(每种只有 1 个)|
| `Consumable` | 消耗品,使用即激活效果 | 叠加,最多 9 个/槽 |
| `KeyItem` | 关键道具,推进剧情/开门 | 不叠加 |
| `LoreItem` | 世界观物品,加入图鉴 | 不叠加 |
| `Map` | 区域地图碎片 | 不叠加 |
---
## 5. 经济平衡参数
### 5.1 区域 Geo 密度(参考)
| 游戏阶段 | 单次刷怪平均 Geo | 护符平均价格 |
|---------|---------------|------------|
| 早期区域1-2 区)| 5-20 | 100-300 |
| 中期区域3-4 区)| 15-50 | 400-700 |
| 后期区域5+ 区)| 40-120 | 800-1200 |
> **平衡原则**:购买一件中期护符约需清扫 2-3 个房间的敌人,不应过长也不应过短。
### 5.2 Notch 扩展价格表
| 第 N 次扩展 | 价格Geo|
|-----------|-----------|
| 1 | 150 |
| 2 | 300 |
| 3 | 500 |
| 4 | 800 |
| 5 | 1200 |
| 6+ | 递增 500/次 |
---
## 6. 经济事件目录
| 事件 | 触发时机 | 载荷 |
|------|---------|------|
| `OnGeoChanged` | Geo 数量变化 | `delta: Integer, newTotal: Integer` |
| `OnShopOpened` | 打开商店 | `shopId` |
| `OnShopPurchase` | 完成购买 | `shopId, itemId, price` |
| `OnItemAcquired` | 获得任何物品 | `itemId, source` |
| `OnItemUsed` | 使用消耗品 | `itemId` |
| `OnNotchExpanded` | 扩展护符槽 | `newNotchCount, geoCost` |

View File

@@ -0,0 +1,216 @@
# 13 · 存档系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**存档数据结构Schema、读写契约、版本迁移与存档槽管理。
---
## 目录
1. [存档设计原则](#1-存档设计原则)
2. [存档数据 Schema](#2-存档数据-schema)
3. [存档读写契约](#3-存档读写契约)
4. [存档触发时机](#4-存档触发时机)
5. [版本迁移规范](#5-版本迁移规范)
6. [存档槽管理](#6-存档槽管理)
---
## 1. 存档设计原则
| 原则 | 说明 |
|------|------|
| **单一来源** | 存档数据是游戏状态的唯一持久化来源 |
| **原子性** | 存档要么完整写入,要么不写(不产生损坏的存档)|
| **版本兼容** | 旧版存档可被新版游戏读取(向前兼容)|
| **与引擎无关** | 存档格式为纯数据JSON / 结构化二进制),不依赖引擎序列化 |
---
## 2. 存档数据 Schema
### 2.1 顶层结构
```
DataModel SaveData {
──── 元信息 ────────────────────────
schemaVersion : Integer // 用于版本迁移
saveSlot : Integer // 存档槽编号1-3
lastSaveTime : String // ISO 8601 时间戳
playTimeSecs : Integer // 游戏内游玩时长(秒)
──── 玩家状态 ──────────────────────
player : PlayerSaveData
──── 世界状态 ──────────────────────
world : WorldSaveData
──── 进程 ──────────────────────────
progression : ProgressionSaveData
──── 经济 ──────────────────────────
economy : EconomySaveData
}
```
### 2.2 玩家存档数据
```
DataModel PlayerSaveData {
currentHP : Integer
maxHP : Integer
maxSoulPower : Integer
maxSpiritPower : Integer
springCount : Integer
geoAmount : Integer
currentFormId : ID
unlockedFormIds : List<ID>
equippedCharmIds: List<ID> // 当前装备的护符
inventoryItems : List<{itemId: ID, count: Integer}>
lastBonfireId : ID // 死亡后重生位置
lastRoomId : ID // 关闭游戏前所在房间
lastPosition : Vector2 // 关闭游戏前的精确位置
}
```
### 2.3 世界存档数据
```
DataModel WorldSaveData {
visitedRoomIds : List<ID>
activatedBonfireIds: List<ID>
defeatedBossIds : List<ID>
permanentlyDeadEnemyIds: List<ID> // 不刷新的特殊敌人
openedGateIds : List<ID>
activatedSwitchIds: List<ID>
collectedCollectibleIds: List<ID>
geoShade : Optional<GeoShadeState> // 见 09_ProgressionSystem
}
```
### 2.4 进程存档数据
```
DataModel ProgressionSaveData {
unlockedAbilityIds: List<ID>
mainQuestStage : Integer
completedQuestIds: List<ID>
worldFlags : Map<String, Boolean> // 任意世界状态标记
notchCount : Integer
purchasedNotchCount: Integer
loreFoundIds : List<ID>
secretsFoundIds : List<ID>
lootPityCounters: Map<ID, Integer> // 保底计数itemId → 未掉落次数)
}
```
### 2.5 经济存档数据
```
DataModel EconomySaveData {
ownedCharmIds : List<ID> // 已获得(非已装备)的护符
shopPurchaseHistory: Map<ID, List<ID>> // shopId → 已购买 itemId 列表
}
```
---
## 3. 存档读写契约
```
Interface ISaveSystem {
// 写入
save(data: SaveData, slot: Integer) → SaveResult
// 读取
load(slot: Integer) → Optional<SaveData>
// 检查
slotExists(slot: Integer) → Boolean
getSlotMetadata(slot: Integer) → Optional<SaveSlotMeta>
// 删除(需二次确认)
deleteSlot(slot: Integer) → Void
}
DataModel SaveSlotMeta {
slot : Integer
lastSaveTime : String
playTimeSecs : Integer
lastRoomName : String
playerHP : Integer
maxHP : Integer
}
DataModel SaveResult {
success : Boolean
error : Optional<String>
}
```
---
## 4. 存档触发时机
| 触发事件 | 存档类型 | 说明 |
|---------|---------|------|
| `OnBonfireRested` | 完整存档 | 最主要的存档时机 |
| `OnCollectiblePickedUp`(关键道具)| 完整存档 | 防止关键进程丢失 |
| `OnBossDefeated` | 完整存档 | Boss 死亡立即存档 |
| `OnAbilityUnlocked` | 完整存档 | 防止能力解锁丢失 |
| 游戏退出(发出退出信号)| 位置快照 | 保存位置,不算正式存档进度 |
> **设计决策**:不提供手动存档按钮
> **原因**:存档点机制是游戏节奏设计的一部分,强制存档会破坏张力
---
## 5. 版本迁移规范
### 5.1 版本号规则
- `schemaVersion` 每次修改 SaveData 结构时递增
- 不向后兼容(新版存档无法在旧版游戏中读取)
- 向前兼容(旧版存档可被新版游戏迁移后读取)
### 5.2 迁移契约
```
Interface ISaveMigrator {
canMigrate(fromVersion: Integer, toVersion: Integer) → Boolean
migrate(data: RawSaveData, fromVersion: Integer) → SaveData
}
```
### 5.3 迁移策略
| 变更类型 | 处理方式 |
|---------|---------|
| 新增字段 | 旧存档中该字段赋默认值 |
| 删除字段 | 读取时忽略多余字段 |
| 字段重命名 | 迁移器执行映射 |
| 字段类型变更 | 迁移器执行转换逻辑 |
| 无法迁移 | 提示玩家,提供新开局选项(不自动删除)|
---
## 6. 存档槽管理
| 规格项 | 值 |
|--------|-----|
| 存档槽数量 | 3 个 |
| 每槽独立 | 完全独立,互不影响 |
| 新游戏 | 选择存档槽 → 若已有存档显示警告 → 确认后覆盖 |
| 存档文件位置 | 用户数据目录(不与游戏安装目录混存)|
| 备份 | 写入前备份旧文件为 `.bak`,写入成功后删除备份 |

View File

@@ -0,0 +1,248 @@
# 14 · 叙事系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**对话系统、世界标记、NPC 状态管理与三结局触发逻辑。
---
## 目录
1. [叙事设计原则](#1-叙事设计原则)
2. [对话系统](#2-对话系统)
3. [世界标记系统](#3-世界标记系统)
4. [NPC 状态管理](#4-npc-状态管理)
5. [任务系统](#5-任务系统)
6. [结局触发系统](#6-结局触发系统)
7. [叙事事件目录](#7-叙事事件目录)
---
## 1. 叙事设计原则
| 原则 | 说明 |
|------|------|
| **环境叙事优先** | 通过场景、图鉴和道具描述讲述故事,减少强制对话 |
| **非线性探索** | 玩家可按任意顺序解锁叙事内容 |
| **状态感知** | NPC 的对话随玩家进程改变(不重复同一句话)|
| **数据驱动** | 所有对话树、条件和触发均为数据,不硬编码 |
---
## 2. 对话系统
### 2.1 对话数据模型
```
DataModel DialogueData {
dialogueId : ID
speakerId : ID // NPC ID 或 "player"
──────────────────────────────────
nodes : List<DialogueNode>
entryNodeId : ID // 起始节点
}
DataModel DialogueNode {
nodeId : ID
type : DialogueNodeType // Text / Choice / Condition / Action
──────────────────────────────────
// type = Text:
text : Optional<String> // 支持本地化 Key
portrait : Optional<PortraitID>
nextNodeId : Optional<ID> // null = 对话结束
// type = Choice:
choices : Optional<List<DialogueChoice>>
// type = Condition:
condition : Optional<DialogueCondition>
trueNodeId : Optional<ID>
falseNodeId : Optional<ID>
// type = Action:
action : Optional<DialogueAction>
nextNodeId : Optional<ID>
}
DataModel DialogueChoice {
choiceText : String
nextNodeId : ID
availableCondition: Optional<DialogueCondition> // null=始终显示
}
```
### 2.2 对话条件类型
```
DataModel DialogueCondition {
conditionType : DialogueConditionType
targetId : Optional<ID>
value : Optional<Number>
}
```
| 条件类型 | 说明 |
|---------|------|
| `HasAbility` | 玩家已解锁某能力 |
| `HasItem` | 玩家持有某物品 |
| `WorldFlagTrue` | 某世界标记为 true |
| `QuestStageIs` | 某任务处于指定阶段 |
| `BossDefeated` | 某 Boss 已被击败 |
| `FirstVisit` | 玩家第一次和此 NPC 对话 |
### 2.3 对话动作类型
| 动作类型 | 说明 |
|---------|------|
| `SetWorldFlag` | 设置世界标记 |
| `GiveItem` | 给予物品 |
| `GiveGeo` | 给予 Geo |
| `UnlockAbility` | 解锁能力 |
| `AdvanceQuest` | 推进任务阶段 |
| `PlayCutscene` | 播放过场动画 |
---
## 3. 世界标记系统
### 3.1 世界标记规范
```
WorldFlags: Map<String, Boolean>
```
- 存储于 `ProgressionSaveData.worldFlags`
- Key 为字符串,建议命名格式:`区域_事件_描述`(如 `region1_boss_firstEncounter`
- 标记只能设为 true不支持撤销设计保证单向性
- 叙事系统、门控系统、AI 系统均可读取世界标记
### 3.2 设置世界标记的来源
| 来源 | 说明 |
|------|------|
| 对话动作 `SetWorldFlag` | 对话选择/到达某节点时触发 |
| 门控开启 | 某些门控开启时自动设置 |
| Boss 死亡 | OnBossDefeated 事件触发时自动设置 |
| 收集品拾取 | 特定关键收集品触发 |
| 手动触发器 | 场景中的触发区域触碰时 |
---
## 4. NPC 状态管理
### 4.1 NPC 数据模型
```
DataModel NPCData {
npcId : ID
displayName : String
──────────────────────────────────
locations : List<NPCLocationEntry> // NPC 在不同阶段的位置
dialogueVersions: List<NPCDialogueVersion> // 不同进程阶段的对话
}
DataModel NPCLocationEntry {
condition : Optional<DialogueCondition> // 满足时位于此处null=默认)
roomId : ID
position : Vector2
}
DataModel NPCDialogueVersion {
condition : Optional<DialogueCondition> // 满足时使用此对话null=默认)
dialogueId : ID
priority : Integer // 多个条件同时满足时取最高优先级
}
```
### 4.2 对话版本选取规则
```
玩家与 NPC 交互时:
枚举 NPCData.dialogueVersions按 priority 降序)
→ 找第一个 condition 满足(或 condition 为 null的版本
→ 使用该版本的 dialogueId 开始对话
若无任何版本满足 → 使用默认兜底对话("......"
```
---
## 5. 任务系统
### 5.1 任务数据模型
```
DataModel QuestData {
questId : ID
displayName : String
description : String
──────────────────────────────────
stages : List<QuestStageData>
rewardOnComplete: Optional<QuestReward>
}
DataModel QuestStageData {
stageIndex : Integer
description : String
completionCondition: DialogueCondition // 推进到下一阶段的条件
}
DataModel QuestReward {
geoAmount : Optional<Integer>
itemIds : List<ID>
abilityId : Optional<ID>
worldFlagsToSet : List<String>
}
```
---
## 6. 结局触发系统
### 6.1 结局条件回顾
(详细条件见 [09_ProgressionSystem.md](09_ProgressionSystem.md) 第 5 节)
| 结局 | 类型 | 核心条件 |
|------|------|---------|
| 结局 A | 普通 | 击败最终 Boss |
| 结局 B | 完全 | Boss 全清 + 主线剧情完整 + 关键 NPC 任务 |
| 结局 C | 隐藏 | 结局 B + 隐藏世界标记序列激活 |
### 6.2 结局判定流程
```
最终 Boss 死亡时:
检查结局 C 条件(最严格)
├─ 满足 → 触发结局 C
└─ 不满足 →
检查结局 B 条件
├─ 满足 → 触发结局 B
└─ 不满足 → 触发结局 A
触发结局 X:
→ 发出 OnEndingTriggered 事件endingType
→ 播放对应过场动画序列
→ 记录到 SaveDataendingUnlocked
→ 游戏结束流程
```
---
## 7. 叙事事件目录
| 事件 | 触发时机 | 载荷 |
|------|---------|------|
| `OnDialogueStarted` | 开始对话 | `dialogueId, npcId` |
| `OnDialogueNodeReached` | 到达对话节点 | `nodeId` |
| `OnDialogueChoiceMade` | 玩家做出选择 | `choiceIndex, nextNodeId` |
| `OnDialogueEnded` | 对话结束 | `dialogueId` |
| `OnWorldFlagSet` | 世界标记变更 | `flagKey` |
| `OnQuestStarted` | 任务开始 | `questId` |
| `OnQuestAdvanced` | 任务推进 | `questId, newStage` |
| `OnQuestCompleted` | 任务完成 | `questId` |
| `OnEndingTriggered` | 结局触发 | `endingType` |
| `OnCutsceneStarted` | 过场动画开始 | `cutsceneId` |
| `OnCutsceneEnded` | 过场动画结束 | `cutsceneId` |

70
Docs/DesignSpec/README.md Normal file
View File

@@ -0,0 +1,70 @@
# 泽灵Zeling· 系统设计规范文档集
> **定位**:本文档集是**引擎无关的系统设计规范**,描述游戏的功能需求、行为契约与数据模型。
> **读者**:任何引擎的开发者、设计师、产品经理。
> **不包含**:特定语言的代码、引擎插件名称、运行时 API 调用。
> **区别于**
> - `Docs/Design/` — Unity 实现文档(与 Animancer、Cinemachine 等深度绑定)
> - `Docs/Architecture/` — Unity 代码架构文档asmdef、命名空间、Inspector
>
> 本文档集可作为用任何引擎Godot、Unreal、自研重实现该游戏的**权威规范**。
---
## 导航索引
| 序号 | 文档 | 一句话描述 |
|:----:|------|-----------|
| — | **[设计规范与符号约定](./00_DesignConventions.md)** | 文档写法、数据类型符号、状态机符号、接口定义格式 |
| 01 | [系统架构总览](./01_SystemArchitecture.md) | 设计哲学、系统全景图、模块边界、通信模式 |
| 02 | [事件与消息系统](./02_EventMessaging.md) | 观察者模式约定、事件目录、发布/订阅契约 |
| 03 | [玩家系统规范](./03_PlayerSystem.md) | 玩家实体构成、资源模型、状态机、输入契约 |
| 04 | [战斗系统规范](./04_CombatSystem.md) | 伤害数据包、命中/受击管线、状态效果、连击 |
| 05 | [移动系统规范](./05_MovementSystem.md) | 移动参数、跳跃模型、冲刺、墙壁力学 |
| 06 | [形态系统规范](./06_FormSystem.md) | 三形态模型、切换契约、资源适配、武器联动 |
| 07 | [敌人系统规范](./07_EnemySystem.md) | 敌人实体、AI 行为模型、Boss 系统、掉落 |
| 08 | [世界系统规范](./08_WorldSystem.md) | 房间/场景模型、切换流程、存档点、可交互物 |
| 09 | [进程系统规范](./09_ProgressionSystem.md) | 能力解锁、进度门控、Boss 进程、完成度 |
| 10 | [UI 系统规范](./10_UISystem.md) | HUD 元素、屏幕状态机、数据绑定契约 |
| 11 | [音频系统规范](./11_AudioSystem.md) | 自适应音乐分层、SFX 事件模型、音频区域 |
| 12 | [经济系统规范](./12_EconomySystem.md) | 货币模型、商店契约、掉落模型、平衡参数 |
| 13 | [存档系统规范](./13_SaveSystem.md) | 完整数据 Schema、存取契约、版本迁移 |
| 14 | [叙事系统规范](./14_NarrativeSystem.md) | 故事节点、世界状态注册、NPC 对话版本 |
---
## 阅读建议
```
入门阅读(了解全局):
00_DesignConventions → 01_SystemArchitecture → 02_EventMessaging
核心玩法:
03_PlayerSystem → 04_CombatSystem → 05_MovementSystem → 06_FormSystem
世界与敌人:
07_EnemySystem → 08_WorldSystem → 09_ProgressionSystem
支撑系统:
10_UISystem / 11_AudioSystem / 12_EconomySystem
持久化与叙事:
13_SaveSystem / 14_NarrativeSystem
```
---
## 核心设计词汇表
| 术语 | 含义 |
|------|------|
| **系统System** | 一组紧密相关的行为和数据的逻辑单元,有明确输入/输出边界 |
| **数据模型Data Model** | 描述一类信息的字段结构,不含行为逻辑 |
| **行为契约Behavioral Contract** | 系统对外承诺的操作接口与结果保证 |
| **事件Event** | 系统间一次性的状态通知,携带有限载荷 |
| **配置数据Config Data** | 设计时填写、运行时只读的参数表 |
| **运行时状态Runtime State** | 游戏运行中动态变化的数据 |
| **门控Gate** | 要求玩家满足条件才能通过的进度锁 |
| **形态Form** | 玩家可切换的战斗风格实例(天魂/地魂/命魂)|
| **房间Room** | 游戏世界最小独立场景单元 |
| **频道Channel** | 事件的具名发布通路,类型安全 |