Files

114 lines
8.0 KiB
Markdown
Raw Permalink Normal View History

2026-07-08 19:53:15 +08:00
# 互动条件校验方案(仅方案,不写代码)
更多“条件/消耗/解锁/UI 提示/指向高亮”的统一说明见:
- [InteractionSystem_Technical_Reference.md](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Docs/InteractionSystem_Technical_Reference.md)
## 现状梳理(基于已存在脚本)
- 玩家侧:`PlayerController.PerformInteract()` 通过 Raycast 命中物体后,查找 `IInteractable`(本体或父物体)并直接调用 `Interact()`
- 目前已接入“条件组件”前置判断:在调用 `Interact()` 前,查找父级上的 `InteractConditionBehaviour` 并逐一校验;失败时会输出失败原因日志。
- 物体侧:`DoorController``LeverController``ToggleRotateInteractable` 都实现了 `IInteractable`,其 `Interact()` 只负责执行动作(切换状态 + DOTween 动画/旋转),并各自提供了 `GetInteractPrompt()`(但玩家侧目前未使用)。
- 道具侧:`HandController` 负责“手持外观”;`ItemUsageSystem` 负责“使用道具输入 -> 触发特定 UI/消耗逻辑”;两者与“场景交互门/拉杆”无直接条件绑定。
结论:当前架构是“玩家统一触发 -> 物体自行执行”,适合无条件互动;一旦加入“需要特定道具/状态才能互动”,就需要在 `Interact()` 调用前增加一层“条件判定”。
### 补充:你新增的 `ToggleRotateInteractable` 对架构的影响
- 这个脚本本质上把“门/拉杆这类‘开关式旋转交互’”抽象成了一个通用组件:
- 支持多目标(`RotationTarget[]`)、轴选择、Off/On 两套欧拉角、初始化自动检测、Tween 配置、以及 `UnityEvent`On/Off/StateChanged)。
- 这意味着“行为层”已经开始走向通用化/组件化,比在每个 Door/Lever 单独写逻辑更利于复用。
- 对“需要特定道具才能互动”的需求来说,这反而更适合采用“独立条件组件”的方案:条件不应该写进 `ToggleRotateInteractable`,否则它就不再通用。
### 注意:`IInteractable` 目前只定义了 `Interact()`
- 你现在的 `IInteractable` 并不包含 `GetInteractPrompt()`Door/Lever/ToggleRotateInteractable 只是各自额外提供了这个方法)。
- 因此玩家侧如果想统一显示交互提示/失败原因,建议走“可选接口/可选组件”的方式,例如:
- 提示文本走一个独立的接口(如 `IInteractPrompt`)或提示组件;
- 条件失败原因由“条件组件”提供;
- 玩家侧优先查这些可选能力,查不到就降级为默认提示。
## 推荐方向:新增“条件校验脚本/组件”,并让玩家侧在调用前统一校验(推荐)
### 核心思路
保持现有 `DoorController` / `LeverController` / `ToggleRotateInteractable` 的职责单一:只做“行为与动画”,不要把“钥匙/任务状态/场景权限”等规则硬塞进每个 Controller。
新增一个专门用于校验互动条件的脚本(组件化挂载在可交互物体上或其父物体上),由玩家的交互入口(`PlayerController.PerformInteract()`)在调用 `Interact()` 前统一检查:
- **通过**:照常调用 `IInteractable.Interact()`
- **不通过**:不调用 `Interact()`,并输出一个统一的“失败提示”(后续可接 UI 提示、音效、日志)。
### 为什么推荐“新增组件”,而不是直接改现有 Controller
- 可复用:同一套条件组件可以给门、箱子、机关、NPC 对话等复用,而不是每个交互脚本都写一遍“检查背包是否有钥匙”。
- 可配置:Inspector 上直接配置“需要的道具ID/数量/是否消耗/需要的剧情标记”等,设计迭代成本更低。
- 可扩展:未来加入“多个条件组合(AND/OR)”“一次性权限”“任务阶段限制”等,只需要扩展条件组件,不必改每个交互物体的行为脚本。
- 更符合分层:玩家侧是统一入口、物体侧是具体行为、条件侧是规则/权限。
### 你需要“动哪些地方”
1. **新增脚本(建议新增到 `Interaction` 或新建 `Interaction/Conditions` 目录)**
- 作为一个“可选组件”:只有需要条件的物体才挂它。
2. **轻改 `PlayerController.PerformInteract()`**
-`interactable.Interact()` 之前:尝试在命中物体/父物体上查找“条件组件”,存在则执行校验,不存在则默认允许互动。
3. (可选,后续做 UI 时再做)**把 `GetInteractPrompt()` 也纳入玩家侧**
- 由于 `IInteractable` 不包含 prompt,建议用“可选接口/可选组件”的方式整合提示文本。
### 本项目落地实现(当前仓库)
- 条件组件脚本路径:
- `Assets/_Project/Scripts/Interaction/Conditions/RequireItemsInteractCondition.cs`(多道具 OR/AND
- `Assets/_Project/Scripts/Interaction/Conditions/RequireUnlockedInteractCondition.cs`(基于解锁状态的门禁)
- `Assets/_Project/Scripts/Interaction/Conditions/UnlockStatesOnInteractSucceeded.cs`(交互成功后解锁目标)
- `Assets/_Project/Scripts/Interaction/InteractUnlockState.cs`(解锁状态源,可驱动外观显示/隐藏;可选参与门禁校验并抛出状态事件)
- 玩家交互入口已接入条件校验:`Assets/_Project/Scripts/Player/PlayerController.cs`
- 使用方式(Inspector 配置):
- 在需要“道具限制”的可交互物体上(或其父物体上)挂载 `RequireItemsInteractCondition`
- 若只需要单道具:`mode=RequireAll` + `requirements` 仅配置 1 条(`requiredItemId``ItemData.id`
- 如需“用掉钥匙/道具”,勾选 `consumeOnSuccess`
- 如需“一次解锁后永久放行(本次运行内)”,勾选 `unlockAfterSuccess`
- 解锁后不会再消耗道具,也不会再校验背包数量
- 可选绑定 `unlockState` 指向 `InteractUnlockState`,将解锁状态暴露给后续 UI/渲染系统
- (可选)设置 `failReason` 作为失败提示文本
### 补充文档
- 条件交互与消耗型交互的配置与语义说明见:`Assets/_Project/Docs/Interaction_条件交互与消耗型交互.md`
### 条件组件建议的能力范围(不写代码,只定职责)
最小可用版本(MVP):
- 配置“需要某个物品(按 `id`)与数量”
- 返回“是否允许互动”
- 返回“失败原因文本”(例如“需要钥匙”“需要撬棍”)
进阶版本(可逐步加入):
- 是否消耗道具(成功互动后扣除数量)
- 多条件组合(需要 A 与 B / 需要 A 或 B)
- 关联剧情/关卡状态(如某个 Flag/QuestState
- 互动成功后触发事件(UnityEvent),用于开门同时触发音效/任务推进等
## 备选方案 1:直接扩展 `IInteractable`(不推荐作为第一步)
做法:把 `IInteractable` 升级为 `CanInteract(Interactor)` / `TryInteract(Interactor)` 这种形式,让每个交互脚本自己判断并返回失败原因。
缺点:
- 需要修改所有实现 `IInteractable` 的脚本,未来新增交互也必须实现这些判断。
- 条件逻辑会散落在各个 Controller 中,不利于复用与统一配置。
适用场景:项目规模很小、交互类型很少,并且每个交互的条件都高度定制且不可复用。
## 备选方案 2:新增“InteractionSystem(玩家侧系统)”作为中枢(可选)
做法:把 Raycast、提示、条件判断、调用 Interact 等集中到一个 `InteractionSystem`(挂在玩家上),`PlayerController` 只负责移动/视角。
优点:玩家控制与交互解耦更清晰;但这属于结构重构,适合你准备做完整交互 UI 与复杂逻辑时再做。
## 最终建议(可执行结论)
- **结论**:建议**新增一个“互动条件校验组件脚本”**,并在 `PlayerController` 的交互入口处统一调用它;现有行为脚本只保留行为,不要塞入道具校验。
- **落地策略**:先做“需要某个物品ID才能互动 + 失败提示”,跑通全链路;再逐步扩展为“消耗、组合条件、剧情状态、UI 提示”。