# 条件交互与消耗型交互(技术参考) 更多“条件/消耗/解锁/UI 提示/指向高亮”的统一说明见: - [InteractionSystem_Technical_Reference.md](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Docs/InteractionSystem_Technical_Reference.md) ## 目的 在不改动现有 `IInteractable` 行为脚本(门/机关/拾取物等)的前提下,为交互调用链增加“资格校验 / 消耗 / 解锁”能力,并通过给场景物体挂载条件组件完成配置。 ## 交互链路(当前项目实现) - 玩家按下交互键后,`PlayerController.PerformInteract()`: - Raycast 命中交互层物体 - 收集命中物体与其父物体上的所有 `IInteractable` - 在调用 `Interact()` 前,查找命中物体父级上的所有 `InteractConditionBehaviour` 并逐一 `CanInteract(...)` - 任意条件失败:阻止交互,并输出失败原因 - 全部条件通过:依次调用 `IInteractable.Interact()` - 交互成功后,逐一回调 `InteractConditionBehaviour.OnInteractSucceeded(...)`(用于消耗、解锁等) 相关代码入口: - 玩家交互入口:[PlayerController.PerformInteract](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Player/PlayerController.cs#L367-L459) - 条件基类:[InteractConditionBehaviour](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/InteractConditionBehaviour.cs) ## 如何给门/机关配置条件 ### 无条件交互(默认) - 不挂任何 `InteractConditionBehaviour` 子类组件 - 玩家会直接调用目标上的 `IInteractable.Interact()` ### 单条件:需要某个物品(可选消耗/解锁) 挂载组件:[RequireItemsInteractCondition](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/RequireItemsInteractCondition.cs) - `mode = RequireAll` - `requirements`:只填 1 条(`requiredItemId` + `requiredAmount`) - `consumeOnSuccess`:成功交互后扣除数量 - `unlockAfterSuccess`:成功一次后标记已解锁,后续无需再次校验/消耗 - `unlockState`:可选,绑定 [InteractUnlockState](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/InteractUnlockState.cs) 用于后续 UI/渲染读取解锁状态 - `failReason`:失败提示(可不填) - `allowIfInventoryMissing`:背包系统缺失时是否仍放行(慎用) ### 多条件(AND):挂多个条件组件 - 同一个门/机关根节点上挂多个 `InteractConditionBehaviour` 子类 - 玩家侧按顺序逐个校验,任意失败都会阻止交互 示例:门需要钥匙 + 玩家饥饿值必须大于 10(后者可由你后续新增一个“玩家状态条件”组件实现)。 ### 多道具条件(OR/AND):一个组件内表达 挂载组件:[RequireItemsInteractCondition](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/RequireItemsInteractCondition.cs) - `mode` - `RequireAll`:所有条目都满足才通过(等价 AND) - `RequireAny`:任意一个条目满足就通过(OR) - `requirements`:配置多个条目(`requiredItemId` + `requiredAmount`) - `consumeOnSuccess` - `RequireAll`:依次扣除所有条目数量 - `RequireAny`:扣除“触发通过的那一条”的数量 - `unlockAfterSuccess`:同上 - `unlockState`:可选,绑定 `InteractUnlockState` 作为解锁状态源 ### 解锁状态与外观切换(锁隐藏/电池显示) 挂载组件:[InteractUnlockState](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/InteractUnlockState.cs) - `unlocked`:当前是否解锁(用于初始状态) - `activateWhenUnlocked`:解锁后需要显示/激活的物体(例如发电机上的电池模型) - `deactivateWhenUnlocked`:解锁后需要隐藏/取消激活的物体(例如门锁子物体) - (可选)勾选 `blockInteractionWhenLocked`:让该组件本身参与“互动条件校验”,未解锁时会直接阻止互动并返回 `lockedFailReason` - (可选)联动事件:可在 `onUnlocked / onLocked / onValueChanged(bool)` 上挂接音效、UI、剧情等 ### 解锁门禁(未解锁时禁止互动) 挂载组件:[RequireUnlockedInteractCondition](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/RequireUnlockedInteractCondition.cs) - `unlockState`:绑定某个 `InteractUnlockState`(未绑定时默认在父级查找) - `failReason`:失败提示 - 也可以不挂本条件组件,改为在 `InteractUnlockState` 上勾选 `blockInteractionWhenLocked` 达到同样的“未解锁不可互动”效果 ### 交互成功后解锁其它目标(发电机解锁开关) 挂载组件:[UnlockStatesOnInteractSucceeded](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/Conditions/UnlockStatesOnInteractSucceeded.cs) - `unlockSelf`:是否同时解锁自身(可用于仅通过一次后永久放行) - `targetStates`:需要被解锁的其它 `InteractUnlockState`(例如某个开关的解锁状态) ### 指向高亮(外轮廓发光) 该效果用于“玩家指向可交互对象时显示外轮廓”,并按可互动/被锁定显示不同颜色。 1) 给可交互对象挂载:[InteractOutlineTarget](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Interaction/InteractOutlineTarget.cs) - 默认会自动查找子物体 Renderer;也可手动指定 `renderers` 2) 给玩家挂载:[PlayerAimOutlineHighlighter](file:///e:/Unity/Unity%20Program/Test_2022/Assets/_Project/Scripts/Player/PlayerAimOutlineHighlighter.cs) - `cameraTransform / interactRange / interactLayer` 与交互射线保持一致 - `outlineMaterial`:使用 `Project/InteractOutline` Shader 创建的材质 - `canInteractColor`:可直接互动颜色(默认偏绿) - `lockedColor`:需要解锁颜色(默认偏橙) 3) Shader 资源: - `Assets/_Project/Shaders/InteractOutline.shader`(Shader 名称:`Project/InteractOutline`) ## 推荐的挂载位置 - 推荐将条件组件挂在“门/机关根节点”上(通常是 collider 的父物体或同一层级) - 玩家侧通过 `GetComponentsInParent()` 获取,因此挂在父级更稳妥 ## 失败原因与 UI - 当前失败原因会在玩家侧以日志输出(来自条件组件的 `failReason` 或自动生成) - 如果后续要接 UI 提示,建议从 `PlayerController` 的“条件失败分支”作为统一出口对接(而不是散落在每个交互脚本里) ## 注意事项 - 条件校验与消耗均基于 `ItemData.id`(string),请确保 id 唯一且稳定 - `unlockAfterSuccess` 的解锁状态是运行期内存状态;如果你需要跨存档持久化,建议后续接入存档系统再把“已解锁”写入存档