Unity OnTrigger事件下如何为每个碰撞体配置独立TextArea与自定义行为
解决方案
现有问题梳理
- 初始版本硬编码
NoEntry/NoExit两种状态,用全局字符串数组按索引读取提示,无法给不同碰撞体做差异化配置 - 已实现的
ColliderInfo组件仅支持朝向指定Transform旋转的逻辑,不支持固定角度旋转,触发逻辑写死,扩展新行为需要反复修改核心代码 - 触发检测和配置耦合度高,新增碰撞规则需要调整原有字段结构,维护成本高
重构实现
第一步:定义通用枚举与可序列化配置结构
首先把触发行为抽象成枚举和可序列化配置类,Unity可以直接在Inspector面板序列化显示这类结构,不需要额外插件:
// 触发行为类型枚举,后续加新逻辑直接在这里加枚举值即可 public enum TriggerActionType { None, RotateTowardsTarget, // 朝向指定物体旋转后退 RotateByFixedAngle, // 按固定角度旋转后退 PlayerDeath, // 触发玩家死亡(坠落、触碰陷阱等) Teleport // 预留传送等其他行为 } // 单个触发行为的配置块,所有行为参数统一存在这里 [System.Serializable] public class TriggerActionConfig { public TriggerActionType actionType = TriggerActionType.None; // 旋转类共用参数 public float bounceBackDistance = 1f; // 触发后后退距离 // RotateTowardsTarget 专属参数 public Transform targetTransform; // RotateByFixedAngle 专属参数 public Vector3 fixedEulerAngle = new Vector3(0, 180, 0); // 后续新行为的参数直接往下加即可,比如传送的目标点、死亡的延迟时间 }
第二步:重构ColliderInfo组件
替换原来单独的rotateTowards字段,把进入、退出触发的行为完全独立配置,保留原有的文本输入字段:
using UnityEngine; public class ColliderInfo : MonoBehaviour { [TextArea(3,6)] public string onEnterTip; [TextArea(3,6)] public string onExitTip; public TriggerActionConfig enterAction; public TriggerActionConfig exitAction; }
第三步:编写触发逻辑执行器
把原来写死的旋转逻辑改成按配置类型分发执行,触发检测脚本只负责拿到碰撞体上的ColliderInfo、显示提示、调用行为执行逻辑:
using UnityEngine; using TMPro; public class PlayerTrigger : MonoBehaviour { [SerializeField] private TextMeshProUGUI tipText; [SerializeField] private Transform playerRoot; [SerializeField] private float tipShowDuration = 2f; private float _tipTimer; private void Update() { if (_tipTimer > 0) { _tipTimer -= Time.deltaTime; if (_tipTimer <= 0) tipText.text = string.Empty; } } private void OnTriggerEnter(Collider other) { if (!other.TryGetComponent(out ColliderInfo colInfo)) return; // 显示进入提示 if (!string.IsNullOrEmpty(colInfo.onEnterTip)) { tipText.text = colInfo.onEnterTip; _tipTimer = tipShowDuration; } // 执行进入行为 ExecuteAction(colInfo.enterAction); } private void OnTriggerExit(Collider other) { if (!other.TryGetComponent(out ColliderInfo colInfo)) return; // 显示退出提示 if (!string.IsNullOrEmpty(colInfo.onExitTip)) { tipText.text = colInfo.onExitTip; _tipTimer = tipShowDuration; } // 执行退出行为 ExecuteAction(colInfo.exitAction); } // 核心行为分发逻辑,加新行为只需要在这里加switch分支 private void ExecuteAction(TriggerActionConfig config) { if (config == null || config.actionType == TriggerActionType.None) return; switch (config.actionType) { case TriggerActionType.RotateTowardsTarget: if (config.targetTransform == null) break; Vector3 lookDir = config.targetTransform.position - playerRoot.position; lookDir.y = 0; playerRoot.rotation = Quaternion.LookRotation(lookDir); playerRoot.position -= playerRoot.forward * config.bounceBackDistance; break; case TriggerActionType.RotateByFixedAngle: playerRoot.Rotate(config.fixedEulerAngle, Space.Self); playerRoot.position -= playerRoot.forward * config.bounceBackDistance; break; case TriggerActionType.PlayerDeath: // 这里写自定义的玩家死亡逻辑,比如播动画、回复活点 Debug.Log("触发死亡判定"); break; case TriggerActionType.Teleport: // 预留传送逻辑 break; } } }
(可选)优化Inspector面板显示
如果不想在面板看到当前行为类型用不到的冗余字段,可以加一个简单的编辑器脚本,面板会自动根据选中的行为类型显示对应参数:
using UnityEditor; using UnityEngine; [CustomEditor(typeof(ColliderInfo))] public class ColliderInfoDrawer : Editor { SerializedProperty _enterTip, _exitTip, _enterAction, _exitAction; private void OnEnable() { _enterTip = serializedObject.FindProperty("onEnterTip"); _exitTip = serializedObject.FindProperty("onExitTip"); _enterAction = serializedObject.FindProperty("enterAction"); _exitAction = serializedObject.FindProperty("exitAction"); } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(_enterTip); EditorGUILayout.PropertyField(_exitTip); DrawConfigBlock(_enterAction, "进入触发配置"); DrawConfigBlock(_exitAction, "退出触发配置"); serializedObject.ApplyModifiedProperties(); } private void DrawConfigBlock(SerializedProperty config, string blockName) { EditorGUILayout.Space(8); EditorGUILayout.LabelField(blockName, EditorStyles.boldLabel); SerializedProperty actionType = config.FindPropertyRelative("actionType"); EditorGUILayout.PropertyField(actionType); TriggerActionType curType = (TriggerActionType)actionType.enumValueIndex; EditorGUILayout.PropertyField(config.FindPropertyRelative("bounceBackDistance")); switch (curType) { case TriggerActionType.RotateTowardsTarget: EditorGUILayout.PropertyField(config.FindPropertyRelative("targetTransform")); break; case TriggerActionType.RotateByFixedAngle: EditorGUILayout.PropertyField(config.FindPropertyRelative("fixedEulerAngle")); break; } } }
注意:上述编辑器脚本必须存放到项目内名为
Editor的特殊文件夹下才能正常生效,该文件夹下的代码仅在编辑器环境运行,不会被打入正式游戏包。
配置方法
- 所有碰撞体独立挂载
ColliderInfo组件,直接在组件上填写对应进入/退出提示文本即可,不需要维护全局文本数组 - NoEntry类碰撞体:进入行为选
RotateByFixedAngle,Y轴角度设为180,调整后退距离,不需要拖任何目标物体 - NoExit类碰撞体:退出行为选
RotateTowardsTarget,把场景中需要玩家后退后面向的点位/物体拖到目标字段即可 - 死亡陷阱类碰撞体:进入行为选
PlayerDeath,填好死亡提示文本即可 - 后续新增触发逻辑,只需要在枚举里加值、在配置类加对应参数、在执行逻辑里加分支,不需要改动原有组件结构
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

