Files
zeling_v2/Docs/Design/07_FeedbackSystem.md
2026-05-08 11:04:00 +08:00

352 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 07 · 反馈系统
> **命名空间** `BaseGames.Feedback`
> **所属文档集** [← 返回索引](./README.md) · [总览](./00_Overview.md)
> **依赖** Feel v4.3 · `BaseGames.Core.Events` · `BaseGames.Combat`
---
## 目录
1. [设计原则](#1-设计原则)
2. [Feel MMF_Player 体系](#2-feel-mmf_player-体系)
3. [PlayerFeedback — 玩家反馈配置](#3-playerfeedback--玩家反馈配置)
4. [EnemyFeedback — 敌人反馈配置](#4-enemyfeedback--敌人反馈配置)
5. [时间管理:子弹时间集成](#5-时间管理子弹时间集成)
6. [镜头震动集成](#6-镜头震动集成)
7. [音效管理](#7-音效管理)
8. [粒子特效规范](#8-粒子特效规范)
9. [UI 反馈](#9-ui-反馈)
10. [事件频道驱动的全局反馈](#10-事件频道驱动的全局反馈)
11. [FeedbackConfigSO](#11-feedbackconfigso)
12. [编辑器友好设计](#12-编辑器友好设计)
---
## 1. 设计原则
- **零耦合**:所有反馈通过**事件频道**或 `UnityEvent` 触发GameLogic 不直接调用 Feedback
- **数据驱动**:每种反馈配置为独立 `MMF_Player` 资产,可在 Inspector 中调节,无需修改代码
- **分层设计**本地反馈HitBox 命中瞬间)→ 全局反馈OnHitConfirmed 频道)→ 时间管理ParrySuccess
- **像素风格适配**:优先使用 Shader 闪光Flash而非粒子减少视觉噪声
---
## 2. Feel MMF_Player 体系
### MMF_Player 命名规范
所有 `MMF_Player` GameObject 命名格式:`MMF_{Owner}_{EventName}`
示例:
- `MMF_Player_OnHit`(玩家受击)
- `MMF_Player_OnAttackHit`(玩家攻击命中)
- `MMF_Player_OnParrySuccess`(弹反成功)
- `MMF_Enemy_OnHit`(敌人受击)
- `MMF_Enemy_OnDeath`(敌人死亡)
### MMF_Player Feedback 类型速查
| Feel Feedback 类型 | 常用场景 | 关键参数 |
|-------------------|---------|---------|
| `MMF_Flash` | Sprite 受击白闪 | `FlashColor`, `Duration` |
| `MMF_SpriteRenderer` | 闪烁/变色 | `Color`, `Blink Duration` |
| `MMF_Particles` | 命中粒子特效 | `ParticleSystem` 引用 |
| `MMF_AudioSource` | 播放音效 | `AudioClip`, `Volume`, `Pitch Variance` |
| `MMF_CinemachineImpulse` | 镜头震动 | `ImpulseSource`, `Velocity` |
| `MMF_FreezeFrame` | 命中冻帧 | `FreezeDuration` |
| `MMF_TimeScale` | 子弹时间 | `TimeScale`, `Duration` |
| `MMF_TextMeshPro` | 伤害数字弹出 | `Value`, `Animation Curve` |
| `MMF_Position` | 物体位移(扑克震动)| `Displacement`, `Curve` |
| `MMF_Scale` | 冲击缩放(南瓜弹)| `Scale`, `Duration` |
| `MMF_Enable` | 启用/禁用 GameObject | `Target` |
---
## 3. IFeedbackPlayer 接口 — 反馈抽象层
为了让游戏逻辑(`PlayerCombat``EnemyBase`)与具体的 Feel/MMF_Player 实现零耦合,所有反馈调用必须通过 `IFeedbackPlayer` 接口进行:
```csharp
/// <summary>
/// 反馈执行器的抽象接口。
/// GameLogic 依赖此接口,而非具体的 MMF_Player 引用。
/// </summary>
public interface IFeedbackPlayer
{
void PlayHit(HitWeight weight); // 命中反馈(轻/中/重)
void PlayParrySuccess(); // 弹反成功
void PlayTakeHit(); // 玩家受击
void PlayDeath(); // 死亡演出
void PlayHeal(); // 治疗
void PlayLandImpact(); // 落地冲击
void PlayAttackWhoosh(); // 攻击挥动音效
void PlayJumpLaunch(); // 起跳
void TriggerPreset(string presetId); // 通过 ID 触发任意预设(动画事件用)
void PlaySFXById(string sfxId); // 通过 ID 播放音效(动画事件用)
}
```
**使用规范**
- `PlayerCombat``PlayerMovement` 等逻辑组件持有 `IFeedbackPlayer _feedback`(通过 Inspector 注入)
- 测试时可替换为 `NullFeedbackPlayer`(空实现),完全不需要 Feel 资产
- 新增反馈类型时先在接口声明,再在 `PlayerFeedback` 中实现,保持单一变更点
---
## 4. PlayerFeedback — 玩家反馈配置
`PlayerFeedback` 组件挂载在 Player Prefab 下,实现 `IFeedbackPlayer` 接口,聚合所有玩家相关 `MMF_Player`
### 受击反馈OnTakeHit
| Feedback | 参数 | 视觉效果 |
|----------|------|---------|
| `MMF_Flash` | 白色0.15s | Sprite 白闪(经典受击感)|
| `MMF_AudioSource` | SFX_Player_Hurt | 受伤音效(随机 Pitch 0.9~1.1|
| `MMF_CinemachineImpulse` | Medium0.5强度 | 镜头震动 |
| `MMF_FreezeFrame` | 0.033s | 2帧冻帧 |
| `MMF_Position` | 朝击退反方向 1 unit | 玩家轻微弹开 |
### 攻击命中反馈OnAttackHit
| Feedback | 参数 | 说明 |
|----------|------|------|
| `MMF_Particles` | HitSpark Prefab命中点生成 | 金属火花粒子 |
| `MMF_AudioSource` | SFX_Attack_Hit随机 3 个变体之一)| 命中音效 |
| `MMF_FreezeFrame` | 0.033s2帧| 命中停顿感 |
| `MMF_CinemachineImpulse` | Light0.2强度 | 轻微镜头震 |
### 弹反成功反馈OnParrySuccess
| Feedback | 参数 | 说明 |
|----------|------|------|
| `MMF_Flash` | 金色0.1s | Sprite 金光闪烁 |
| `MMF_Particles` | ParryFlash Prefab全屏金色光圈| 弹反标志性特效 |
| `MMF_AudioSource` | SFX_Parry_Success金属碰撞音| 清脆弹反音效 |
| `MMF_CinemachineImpulse` | Parry0.7强度,带方向 | 有方向性震动 |
| `MMF_TimeScale` | 0.25×0.2s | 子弹时间(与 ParrySystem 同步)|
| `MMF_FreezeFrame` | 0.066s4帧| 更长冻帧强调击中感 |
### 治疗反馈OnHeal
| Feedback | 参数 | 说明 |
|----------|------|------|
| `MMF_Flash` | 蓝色0.2s | 恢复光效 |
| `MMF_Particles` | HealParticle Prefab | 向上飘散的蓝色粒子 |
| `MMF_AudioSource` | SFX_Heal | 回血音效 |
### 死亡反馈OnDeath
| Feedback | 参数 | 说明 |
|----------|------|------|
| `MMF_AudioSource` | SFX_Player_Death | 死亡音效 |
| `MMF_CinemachineImpulse` | Heavy1.0强度 | 强烈震动 |
| `MMF_FreezeFrame` | 0.1s6帧| 死亡冻帧 |
| `MMF_Enable` | 禁用 HurtBox | 防止死亡后继续受击 |
---
## 4. EnemyFeedback — 敌人反馈配置
`EnemyFeedback` 挂在每个敌人 Prefab 下:
### 受击反馈OnHit
| Feedback | 参数 | 说明 |
|----------|------|------|
| `MMF_Flash` | 白色0.1s | 受击白闪 |
| `MMF_AudioSource` | SFX_Enemy_Hurt按敌人类型变体| 受伤音效 |
| `MMF_Particles` | HitSpark命中位置| 命中粒子 |
### 被弹反反馈OnParried
| Feedback | 参数 | 说明 |
|----------|------|------|
| `MMF_Flash` | 金色0.15s | 与玩家弹反视觉对应 |
| `MMF_SpriteRenderer` | 扭曲/Shader 效果 | 硬直视觉反馈 |
| `MMF_AudioSource` | SFX_Parry_Impact | 被弹反音效 |
| `MMF_Position` | 小幅后退 | 轻微击退特效 |
### 死亡反馈OnDeath
| Feedback | 参数 | 说明 |
|----------|------|------|
| `MMF_AudioSource` | SFX_Enemy_Death | 死亡音效 |
| `MMF_Particles` | DeathParticle Prefab | 死亡解体粒子 |
| `MMF_Enable` | 禁用 Rigidbody2D / Colliders | 防止尸体物理继续 |
| `MMF_FreezeFrame` | 0.05s3帧| 击杀冻帧 |
---
## 5. 时间管理:子弹时间集成
子弹时间通过两套系统协调:
### Feel MMTimeManager主要
- 注册所有 `MMTimeManager` Listener
- `ParrySystem.TriggerParrySuccess()``MMTimeScaleEvent` 广播
- `MMTimeManager` 接收后修改 `Time.timeScale`
- 时间恢复Lerp 方式平滑还原(`LerpSpeed = 20`
### 与 Animancer 的配合
- Animancer 动画默认使用 `Time.timeScale`(跟随子弹时间减速)
- 特例UI 动画、音频播放必须用 `Time.unscaledDeltaTime`
- 检测敌人动画速率被子弹时间自动降低(无需额外配置)
---
## 6. 镜头震动集成
详见 [02_CameraSystem](./02_CameraSystem.md#6-镜头震动cinemachineimpulse)。
### Feel 与 Cinemachine Impulse 的桥接
Feel `MMF_CinemachineImpulse` → 内部调用 `CinemachineImpulseSource.GenerateImpulse()``CinemachineImpulseListener` 响应。
**PlayerFeedback 中每种震动类型对应 ImpulseSource**Inspector 中拖入引用):
| 震动类型 | 来源 ImpulseSource | 说明 |
|---------|-------------------|------|
| Light | `ImpulseSource_Light` | 普通命中 |
| Medium | `ImpulseSource_Medium` | 玩家受伤 |
| Heavy | `ImpulseSource_Heavy` | 玩家死亡 / Boss 重击 |
| Parry | `ImpulseSource_Parry` | 弹反成功(带方向性)|
---
## 7. 音效管理
### 音效 SO 资产结构
`AudioEventSO` 封装音效播放参数(非特定 AudioSource通过事件频道解耦
| 字段 | 类型 | 说明 |
|------|------|------|
| `Clips` | `AudioClip[]` | 随机选取其中一个播放 |
| `Volume` | `float` | 基础音量0~1|
| `PitchMin` | `float` | Pitch 随机范围最小值 |
| `PitchMax` | `float` | Pitch 随机范围最大值 |
| `MixerGroup` | `AudioMixerGroup` | SFX / Music / UI 混音组 |
所有 `MMF_AudioSource` 通过 `AudioEventSO` 播放音效(而非直接引用 `AudioClip`),实现随机音调变化。
### 音效资产路径
```
Assets/Audio/SFX/
├── Player/
│ ├── SFX_Player_Hurt.asset
│ ├── SFX_Player_Death.asset
│ ├── SFX_Attack_Hit.asset ← 含3个变体Clip
│ ├── SFX_Parry_Success.asset
│ └── SFX_Heal.asset
├── Enemies/
│ ├── SFX_Enemy_Hurt_Generic.asset
│ └── SFX_Enemy_Death_Generic.asset
└── World/
├── SFX_Footstep.asset
└── SFX_Landing.asset
```
---
## 8. 粒子特效规范
| 特效 Prefab | 触发来源 | 生命周期 | 说明 |
|-------------|---------|---------|------|
| `FX_HitSpark` | 攻击命中 | 0.3s 后自销毁 | 金属火花4~6 粒子)|
| `FX_ParryFlash` | 弹反成功 | 0.5s 后自销毁 | 金色光圈(全屏扩散)|
| `FX_HealParticle` | 玩家治疗 | 1.0s 后自销毁 | 蓝色粒子上飘 |
| `FX_EnemyDeath` | 敌人死亡 | 1.5s 后自销毁 | 解体粒子(按敌人主题色)|
| `FX_DustCloud` | 玩家落地 | 0.5s 后自销毁 | 落地灰尘 |
| `FX_DashTrail` | 玩家冲刺 | 0.4s 后自销毁 | 冲刺残影Sprite Fade|
**像素风格适配**:所有粒子使用 `Render Mode: Billboard`,粒子贴图与角色 PPU32 PPU保持一致避免模糊。
---
## 9. UI 反馈
### 伤害数字FloatingText
- 触发:`OnHitConfirmed` 事件频道
- 从对象池中取 `FloatingText` Prefab在命中位置生成
- 数字内容:`DamageInfo.FinalDamage`
- 弹反反击:数字显示为金色 + 粗体(`FinalDamage` 为 3× 时额外标记)
- 动画:向上飘 0.8s + 淡出
### HP 血条动画
- `OnPlayerHPChanged` 事件频道驱动
- 满格 HP 以绿色实心显示,扣除部分以红色渐出动画表示(延迟 0.2s 开始缩短,视觉缓冲)
### Soul 槽动画
- `OnSoulChanged` 事件频道驱动
- 弹反成功 +33 时Soul 槽显示**扫光动画**(金色高亮流过 1/3 槽)
---
## 10. 事件频道驱动的全局反馈
`GlobalFeedbackController` 组件(单例,挂在 `[Managers]` GO 上)监听全局频道并触发对应 `MMF_Player`
| 频道 | 触发反馈 |
|------|---------|
| `OnPlayerDied` | 停止音乐、播放死亡演出(慢慢音量淡出,画面泛红)|
| `OnBossFightStarted` | 停止环境音乐、淡入 Boss 战BGM |
| `OnBossFightEnded` | Boss 死亡演出(震动+特效+音乐过渡)|
| `OnRoomEntered` | 淡出 → 淡入(场景切换遮罩)|
---
## 11. FeedbackConfigSO
`FeedbackConfigSO` 全局配置,存放于 `Assets/ScriptableObjects/Config/FeedbackConfigSO.asset`
| 参数 | 类型 | 推荐值 | 说明 |
|------|------|--------|------|
| `HitFreezeFrames` | `int` | 2 | 命中冻帧帧数 |
| `ParryFreezeFrames` | `int` | 4 | 弹反冻帧帧数 |
| `SfxVolumeMaster` | `float` | 1.0 | SFX 总音量0~1|
| `HapticsEnabled` | `bool` | true | 手柄震动开关P2|
| `FloatingTextPoolSize` | `int` | 20 | 伤害数字对象池大小 |
| `ParticlePoolSize` | `int` | 30 | 粒子特效对象池大小 |
---
## 12. 编辑器友好设计
### MMF_Player 预览支持
Feel 原生支持在 Editor 非 Play Mode 下预览 `MMF_Player`
- Inspector 底部的"▶ Play"按钮可单独预览每组反馈
- `Preview in Editor` 模式下验证视觉效果,无需进入 Play Mode
### FX 对象池监控Play Mode Inspector
```
┌─ GlobalFeedbackController ──────────────────────┐
│ FloatingText Pool: 14 / 20 available │
│ Particle Pool : 28 / 30 available │
│ ───────────────────────────────────────────── │
│ [Test: Player Hurt] [Test: Parry Success] │
│ [Test: Enemy Hit ] [Test: Boss Phase Change] │
└────────────────────────────────────────────────┘
```
### 反馈效果全局开关(调试用)
`FeedbackDebugOverlay`(编辑器 Play Mode 下叠加):
- `[Toggle Screen Shake]`:开关镜头震动
- `[Toggle Freeze Frames]`:开关冻帧
- `[Toggle Particles]`:开关粒子特效
- `[Toggle Bullet Time]`:开关子弹时间
方便策划/美术人员单独调试某类反馈。