You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity如何适配OnTrigger触发逻辑并实现通用碰撞检测脚本

Unity 碰撞区域拦截逻辑改造方案

问题场景说明

当前项目需要实现两类碰撞区域的玩家越界拦截:玩家越界后自动减速、停留提示、转身返回安全区,两类区域触发顺序完全相反:

  • 大尺寸活动范围:玩家初始在区域内部,离开碰撞体(OnTriggerExit) 判定为越界,回到碰撞体(OnTriggerEnter)判定为返回安全区
  • 小尺寸火焰禁区:玩家初始在区域外部,进入碰撞体(OnTriggerEnter) 判定为越界,离开碰撞体(OnTriggerExit)判定为返回安全区
    碰撞体区域示意图
    (左侧小碰撞体为火焰禁区,游戏启动时玩家位于大碰撞体活动范围内)

① 触发顺序兼容实现逻辑

核心思路是放弃硬绑定「Exit=越界、Enter=返回」的固定逻辑,给每个碰撞体增加区域类型标记,统一越界/返回的执行入口:

  • 为每个碰撞体配置IsInnerArea布尔值:大活动范围类区域勾选该值,判定逻辑为「Exit触发越界,Enter触发返回」;火焰禁区类区域不勾选该值,判定逻辑为「Enter触发越界,Exit触发返回」
  • 增加全局状态锁isHandling,同一时间只处理一个越界事件,避免多个碰撞体触发时协程冲突、逻辑打架
  • 抽离统一的越界处理方法和返回恢复方法,不管是Enter还是Exit事件触发,只要匹配对应判定规则就调用统一逻辑,无需重复编写代码

② 单脚本挂载玩家的通用配置实现

无需把脚本挂在每个碰撞体上,直接把脚本挂在玩家对象上,通过自定义可序列化配置类实现多碰撞体独立参数配置:

  • 定义[System.Serializable]的BoundaryConfig类,每个配置项包含:碰撞体引用、对应提示文本、转身朝向目标点、是否为内部区域类型
  • 脚本中公开List<BoundaryConfig> boundaryConfigs列表,在Inspector面板中可以直接增删区域,每个区域独立配置所有参数,不需要维护多个列表的下标对应关系
  • 触发碰撞事件时,遍历配置列表匹配当前碰撞到的对象,读取对应配置执行逻辑即可

完整修改后代码

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityStandardAssets.Characters.ThirdPerson;

public class DistanceCheck : MonoBehaviour
{
    [Header("全局参数配置")]
    public float lerpDuration = 1f;
    public float rotationSpeed = 5f;
    public float returnTriggerDistance = 5f;
    public float waitTimeBeforeReturn = 3f;
    public GameObject descriptionTextImage;
    public TextMeshProUGUI text;
    public ThirdPersonUserControl thirdPersonUserControl;

    [Header("所有边界区域配置")]
    public List<BoundaryConfig> boundaryConfigs = new List<BoundaryConfig>();

    private Animator anim;
    private float timeElapsed = 0;
    private float startValue = 1;
    private float endValue = 0;
    private float valueToLerp = 0;
    private bool startRotating = false;
    private bool slowOnBack = true;
    private bool isHandling = false;
    private Vector3 exitPosition;
    private float distance;
    private BoundaryConfig currentActiveBoundary;

    [System.Serializable]
    public class BoundaryConfig
    {
        public Collider targetCollider;
        [TextArea(1,2)]
        public string tipText;
        public Transform rotateTarget;
        [Tooltip("勾选=玩家初始在区域内,离开算越界;不勾选=玩家初始在区域外,进入算越界")]
        public bool isInnerArea = true;
    }

    void Start()
    {
        anim = transform.GetComponent<Animator>();
    }

    private void FixedUpdate()
    {
        if (startRotating && currentActiveBoundary != null)
        {
            Vector3 lookDir = currentActiveBoundary.rotateTarget.position - transform.position;
            lookDir.y = 0;
            transform.rotation = Quaternion.RotateTowards(transform.rotation,
                Quaternion.LookRotation(lookDir),
                rotationSpeed * Time.deltaTime);
        }

        if (exitPosition != Vector3.zero && slowOnBack)
        {
            distance = Vector3.Distance(transform.position, exitPosition);
        }

        if (distance > returnTriggerDistance && slowOnBack)
        {
            slowOnBack = false;
            StartCoroutine(SlowDown());
        }
    }

    private void OnTriggerExit(Collider other)
    {
        BoundaryConfig config = GetMatchConfig(other);
        if (config == null || isHandling) return;

        // 内部区域Exit才是越界,非内部区域Exit是返回安全区
        if (config.isInnerArea)
        {
            TriggerBoundaryHandle(config);
        }
        else
        {
            TriggerReturnHandle();
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        BoundaryConfig config = GetMatchConfig(other);
        if (config == null) return;

        // 非内部区域Enter才是越界,内部区域Enter是返回安全区
        if (!config.isInnerArea && !isHandling)
        {
            TriggerBoundaryHandle(config);
        }
        else if (config.isInnerArea && config == currentActiveBoundary)
        {
            TriggerReturnHandle();
        }
    }

    private BoundaryConfig GetMatchConfig(Collider other)
    {
        foreach (var config in boundaryConfigs)
        {
            if (config.targetCollider == other)
            {
                return config;
            }
        }
        return null;
    }

    private void TriggerBoundaryHandle(BoundaryConfig config)
    {
        isHandling = true;
        currentActiveBoundary = config;
        slowOnBack = true;
        exitPosition = transform.position;
        thirdPersonUserControl.enabled = false;
        descriptionTextImage.SetActive(true);
        text.text = config.tipText;
        StartCoroutine(SlowDown());
    }

    private void TriggerReturnHandle()
    {
        isHandling = false;
        startRotating = false;
        text.text = "";
        descriptionTextImage.SetActive(false);
        currentActiveBoundary = null;
        StopAllCoroutines();
        anim.SetFloat("Forward", startValue);
    }

    IEnumerator SlowDown()
    {
        timeElapsed = 0;
        while (timeElapsed < lerpDuration)
        {
            valueToLerp = Mathf.Lerp(startValue, endValue, timeElapsed / lerpDuration);
            anim.SetFloat("Forward", valueToLerp);
            timeElapsed += Time.deltaTime;
            yield return null;
        }

        if (isHandling)
        {
            yield return new WaitForSeconds(waitTimeBeforeReturn);
            startRotating = true;
            StartCoroutine(SpeedUp());
        }

        if (!slowOnBack)
        {
            thirdPersonUserControl.enabled = true;
        }
    }

    IEnumerator SpeedUp()
    {
        timeElapsed = 0;
        while (timeElapsed < lerpDuration)
        {
            valueToLerp = Mathf.Lerp(endValue, startValue, timeElapsed / lerpDuration);
            anim.SetFloat("Forward", valueToLerp);
            timeElapsed += Time.deltaTime;
            yield return null;
        }
    }
}

配置注意事项

  • 所有需要检测的碰撞体都要勾选Is Trigger选项
  • 玩家对象必须挂载Collider和Rigidbody组件,Rigidbody建议勾选Is Kinematic,避免物理碰撞干扰角色控制
  • 配置列表中每个区域的rotateTarget要拖入对应区域玩家返回时需要朝向的目标点,比如火焰区域就拖到火焰反方向的安全点,大活动区域就拖到活动范围中心

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 00:27:23