# 互动条件校验方案(仅方案,不写代码) 更多“条件/消耗/解锁/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 提示”。