Files
Echo/Assets/_Project/Docs/InteractionSystem_Technical_Reference.md
T
2026-07-08 20:42:51 +08:00

234 lines
14 KiB
Markdown
Raw 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.
# 交互系统技术参考(条件 / 消耗 / 解锁 / UI 提示 / 指向高亮)
本文档用于汇总当前工程内“交互系统”的落地实现与推荐配置方式,便于后续扩展与排错。
## 1. 总览
系统目标:
- 交互行为(开门/机关/拾取等)继续由 `IInteractable.Interact()` 负责
- “能不能交互 / 失败原因 / 成功后消耗 / 成功后解锁 / 解锁后外观变化”由可配置的组件承担
- 玩家侧在调用 `Interact()` 前统一做条件校验,并把失败原因用于 UI/日志
- 玩家指向可交互对象时,提供 UI 提示与外轮廓高亮反馈
核心入口:
- 玩家按键交互调用链:[PlayerController.PerformInteract](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Player/PlayerController.cs#L367-L459)
## 2. 交互调用链(时序)
1) 玩家 Raycast 命中交互层物体
2) 收集命中 collider 与父物体上的 `IInteractable`(可多个)
3) 在调用 `Interact()` 前,获取父级所有 `InteractConditionBehaviour` 并逐一 `CanInteract(interactor, out failReason)`
- 任一失败:中止,不调用 `Interact()`,可把 `failReason` 展示到 UI
4) 全部通过:依次调用每个 `IInteractable.Interact()`
5) 交互完成后:逐一回调 `OnInteractSucceeded(interactor)`(用于消耗、解锁、触发联动)
条件基类与接口:
- [IInteractCondition.cs](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/IInteractCondition.cs)
- [InteractConditionBehaviour.cs](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/InteractConditionBehaviour.cs)
## 3. 条件 / 消耗 / 解锁(组件清单)
### 3.1 道具需求 + 可选消耗 + 可选一次解锁
组件:[RequireItemsInteractCondition](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/RequireItemsInteractCondition.cs)
用途:
- 单道具门禁(配置 1 条 requirement
- 多道具 ANDRequireAll
- 多道具 ORRequireAny
- 成功后消耗(consumeOnSuccess
- 成功后一次解锁(unlockAfterSuccess
关键语义:
- `unlockAfterSuccess` + `unlocked`(或绑定 `unlockState`)为 true 时,会直接放行,不再校验/不再消耗
- 常见排错点:测试时如果勾了 `Unlocked`,即使没有物品也会通过
- `RequireAny` + `consumeOnSuccess`:只消耗本次“触发通过”的那一条需求
### 3.2 解锁状态源(用于后续 UI/渲染读取、以及外观切换)
组件:[InteractUnlockState](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/InteractUnlockState.cs)
用途:
- 保存“是否已解锁”的状态(`IsUnlocked`
- 解锁/上锁时自动激活/隐藏目标物体
- `activateWhenUnlocked`:解锁时显示(例:发电机上的电池模型)
- `deactivateWhenUnlocked`:解锁时隐藏(例:门锁模型)
- 可选作为“门禁条件”参与交互校验
- 勾选 `blockInteractionWhenLocked` 后:当 `unlocked=false` 会阻止交互,并返回 `lockedFailReason`
- 默认不勾选:仅作为状态源与外观切换,不影响交互(兼容旧场景配置)
- 可选对外广播状态事件(便于联动 UI/音效/剧情)
- `onUnlocked` / `onLocked` / `onValueChanged(bool)`
### 3.3 未解锁则禁止交互(门禁)
组件:[RequireUnlockedInteractCondition](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/RequireUnlockedInteractCondition.cs)
用途:
- 给某个开关/门/机关设置“必须已解锁才允许交互”
- `unlockState` 可不填,组件会向父级查找 `InteractUnlockState`
- 如果你希望“只要挂了 InteractUnlockState 且未解锁就不能互动”,也可以直接在 `InteractUnlockState` 上勾选 `blockInteractionWhenLocked`(无需再挂本条件组件)
### 3.4 交互成功后解锁(联动)
组件:[UnlockStatesOnInteractSucceeded](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/UnlockStatesOnInteractSucceeded.cs)
用途:
- 在某个交互成功后,解锁自身或其它目标(例如发电机解锁另一个开关)
## 4. 常用配置范式(推荐挂载位置:交互根节点)
推荐挂载位置:
- 条件/解锁相关组件挂在“门/机关根节点”(通常是 collider 的父级或同级)
- 玩家侧用 `GetComponentsInParent<InteractConditionBehaviour>()`,挂在父级更稳妥
### 4.1 无条件交互
- 只挂交互行为组件(Door/Lever/Toggle* 等),不挂任何 `InteractConditionBehaviour`
### 4.2 有锁的门:满足条件后隐藏锁,并永久解锁
门根节点:
- `DoorController`(或其它 IInteractable
- `InteractUnlockState`
- `deactivateWhenUnlocked` 填门锁子物体
- `RequireItemsInteractCondition`
- 配置钥匙需求
- `consumeOnSuccess`(需要消耗钥匙则勾)
- `unlockAfterSuccess` 勾选
- `unlockState` 指向同门上的 `InteractUnlockState`
### 4.3 发电机放电池:消耗电池、显示电池模型、解锁另一个开关
发电机根节点:
- `RequireItemsInteractCondition`(配置电池,勾选消耗 + 解锁)
- `InteractUnlockState`
- `activateWhenUnlocked` 填“隐藏的电池模型”
- `UnlockStatesOnInteractSucceeded`
- `targetStates` 填“另一个开关的 InteractUnlockState”
被解锁的开关根节点:
- `InteractUnlockState`(初始未解锁)
- `RequireUnlockedInteractCondition`(未解锁时禁止交互)
### 4.4 发电机首次启动:触发一次性旁白/对话(存档持久化)
目标:
- 发电机第一次“启动/通电”时触发一段旁白或其它事件
- 该事件只触发一次(跨存档/跨重进场景也不会再触发)
推荐做法(不改玩家交互入口,完全 Inspector 配置):
1) 让发电机“启动开关”使用 `ToggleMoveInteractable``ToggleRotateInteractable`(它们有 `onTurnedOn` / `onTurnedOff` 事件)
2) 在发电机根节点(或同级管理对象)挂 `OneShotFlagEvent`
- `flagId` 建议命名:`Generator_<UniqueName>_FirstOn`
- `autoSaveAfterTrigger` 建议勾选(触发后立刻写入存档)
3) 把开关脚本的 `onTurnedOn` 指向 `OneShotFlagEvent.Trigger`
4) 在 `OneShotFlagEvent.onFirstTriggered` 上挂你要触发的事件:
- 播放旁白:使用 `NarrationPlayByIdAction`(配置 `sequenceId`),或直接调用 `NarrationSystem.PlayById`
- 触发其它联动:任意 `UnityEvent` 目标方法都可以
运行依赖(自动注入):
- `SettingsBootstrap` 会在运行时创建 `SaveManager``PersistentFlagsService`,用于保存一次性 flags
- [SettingsBootstrap.cs](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Core/SettingsSystem/SettingsBootstrap.cs)
- [PersistentFlagsService.cs](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Core/SaveSystem/PersistentFlagsService.cs)
- [OneShotFlagEvent.cs](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Core/Triggers/OneShotFlagEvent.cs)
## 5. UI:指向提示(Reach Feed Notification
脚本落点:[PlayerController.cs](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Player/PlayerController.cs)
用途:
- 玩家指向可交互对象时:
- 条件通过:显示“可互动”通知
- 条件失败:显示“不可互动”通知(可选显示 failReason
Reach 参考:
- [ReachUI_Technical_Reference.md](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Docs/ReachUI_Technical_Reference.md)
如何加速弹出动画(FeedNotification):
- 推荐改 Animator Controller 状态 Speed`Assets/ThirdParty/UI/Reach - Complete Sci-Fi UI/Animations/HUD/FeedNotification.controller`
- 或缩短动画片段时长:`FeedNotification_In.anim / FeedNotification_Out.anim`
- 或运行时提高 Animator.speedPlayerController 已提供字段)
## 6. 视觉:指向高亮(URP 外轮廓)
实现方式:
- 指向时给目标 Renderer 追加一个“外轮廓材质”,并用材质属性控制颜色/宽度
组件:
- 目标物体:[InteractOutlineTarget](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/InteractOutlineTarget.cs)
- 玩家驱动:[PlayerAimOutlineHighlighter](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Player/PlayerAimOutlineHighlighter.cs)
Shader
- [InteractOutline.shader](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Shaders/InteractOutline.shader)`Project/InteractOutline`
颜色规则(默认实现):
- 条件全部通过:绿(可自定义)
- 条件失败且属于“未解锁门禁”:橙(可自定义)
- 其它失败:使用 blockedColor(可自定义)
## 7. 物理状态控制 (防掉落/穿模)
组件:[ItemPhysicsState](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/ItemPhysicsState.cs)
用途:
- **解决场景初始放置的物品穿模问题**:当互动物品(如药瓶、道具)放置在只有一个大 `BoxCollider` 的架子或柜子内部时,直接启用刚体会导致物品在场景一开始就被物理引擎“挤出”或弹飞。
- **配置方法**:将该组件挂载在拥有 `Rigidbody` 的互动物体上,并勾选 `startKinematic = true`
- **机制**
- 场景加载(`Awake`)时,该物体会自动锁定为 `isKinematic = true`,不受重力和碰撞弹出的影响。
- 当玩家拾取该物体存入背包,后续再次丢弃(Drop)到场景时,丢弃逻辑会自动调用 `ReleaseKinematic()` 将其恢复为完全受重力影响的物理状态,玩家此时踢到它或将其扔向墙壁都会有正常的物理反馈。
- **注意**:默认状态下 `startKinematic = false`,此时不产生任何影响。这是一项附加配置选项,不会干扰原有交互校验逻辑链。
## 8. 物品使用事件触发器 (监听左键使用)
组件:[ItemUseEventTrigger](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/ItemUseEventTrigger.cs)
用途:
- **监听特定物品在背包/手中被“使用”**:当玩家在场景中任意位置,选中特定物品并按下“使用”键(通常为左键)时,触发场景中的指定事件(例如:首次吃下某种药丸后触发旁白、首次阅读特定文件后开启某扇门)。
配置方法:
1. 在场景中创建一个空物体(或在任意管理节点上)。
2. 挂载 `ItemUseEventTrigger` 组件。
3. `Target Item`:指定你要监听的物品数据(`ItemData`)。
4. `Trigger Only Once`:勾选则表示仅在第一次使用该物品时触发(默认开启)。
5. `On Target Item Used`:配置你需要触发的 `UnityEvent`(如播放旁白、启用对象等)。
实现机制:
-`ItemUsageSystem` 核心脚本中新增了 `OnItemUsedGlobal` 静态全局事件。每次有物品被成功使用时会广播该事件。
- `ItemUseEventTrigger``OnEnable` 时自动订阅,纯旁路监听,**完全不影响任何现有的物品消耗、装备或UI显示逻辑**。
## 9. 交互计数器系统 (配合 Interact 累加并触发事件)
组件:
- 场景系统:[InteractCountThresholdSystem](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/InteractCountThresholdSystem.cs)
- 累加器(挂在被交互对象上):[InteractCountAccumulator](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/InteractCountAccumulator.cs)
用途:
- 解决“交互对象会在 Interact 后被销毁/替换(例如拾取物 Destroy 自身)导致后挂脚本失效”的场景。
-**被交互对象** 在一次“交互成功”时向 **场景中的系统对象** 累加计数;当计数达到阈值后,由系统对象触发一组 `UnityEvent`
配置方法:
1. 在场景中创建一个空物体(或在任意管理节点上)挂 `InteractCountThresholdSystem`
2. 设置 `Threshold` 为你希望触发事件所需的累计次数,并在 `On Threshold Reached` 中配置事件列表。
3. 在你想要计数的“被交互对象根节点”(通常是 collider 的父级或同级)挂 `InteractCountAccumulator`
4. `Target System` 指向第 1 步的系统对象;`Add Amount` 设置每次成功交互累加多少(默认 1)。
触发语义:
- `InteractCountAccumulator` 走的是 `InteractConditionBehaviour.OnInteractSucceeded` 回调,只会在交互条件全部通过且 `Interact()` 已执行后触发;因此适合拾取类物体在销毁前完成计数与触发。
- `InteractCountAccumulator` 只有在“从未绑定过 Target System”的情况下才会自动查找场景中的 `InteractCountThresholdSystem`;如果运行过程中 Target System 被销毁导致引用变空,则不会再随意改投到其它阈值系统上。
## 10. 排错清单
- “没有物品也能交互成功”
- 检查 `RequireItemsInteractCondition` 是否勾选了 `unlockAfterSuccess``unlocked`(或 `unlockState.unlocked`)为 true
- 检查 requirement 是否为空(为空会被视为无需资格直接放行)
- “提示/高亮不出现”
- 检查交互层(interactLayer)与射线范围(interactRange
- 检查目标是否存在 `IInteractable`(本体或父物体)
- 外轮廓:目标是否挂了 `InteractOutlineTarget`,玩家是否挂了 `PlayerAimOutlineHighlighter` 且绑定了 outlineMaterial
## 11. 相关文档索引
- 条件交互与消耗配置细节:[Interaction_条件交互与消耗型交互.md](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Docs/Interaction_%E6%9D%A1%E4%BB%B6%E4%BA%A4%E4%BA%92%E4%B8%8E%E6%B6%88%E8%80%97%E5%9E%8B%E4%BA%A4%E4%BA%92.md)
- 互动条件校验方案(设计思路):[Interaction_互动条件校验方案.md](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Docs/Interaction_%E4%BA%92%E5%8A%A8%E6%9D%A1%E4%BB%B6%E6%A0%A1%E9%AA%8C%E6%96%B9%E6%A1%88.md)
- Reach UI 速查(含通知加速):[ReachUI_Technical_Reference.md](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Docs/ReachUI_Technical_Reference.md)