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

Unity触发器无法同时检测Player与Peter标签的问题求助

问题解决思路

你的核心问题是把PlayerIn和PeterIn声明成了静态变量,导致所有触发器脚本实例共用同一组状态值,跨触发器的对象进入/退出会互相干扰。要解决这个问题,需要让每个触发器独立维护自身的检测状态,再通过全局逻辑判断是否存在满足条件的触发器。

方案1:每帧全局检查(简单直接)

第一步:修改触发器检测脚本

每个触发器实例维护自己的独立状态,对外暴露当前是否满足触发条件:

public class TriggerDetector : MonoBehaviour
{
    // 改为实例变量,每个触发器单独存储状态
    private bool _playerIn = false;
    private bool _peterIn = false;
    // 根据你的场景,手电筒如果是全局的可以改成静态引用,否则每个触发器绑定自己的
    public Flashlight flashlight;

    // 对外提供当前是否满足条件的判断方法
    public bool IsBothInside()
    {
        return _playerIn && _peterIn && flashlight.FlashLightIsOn;
    }

    private void OnTriggerEnter(Collider other) 
    {
        if (other.CompareTag("Player"))
            _playerIn = true;
        if (other.CompareTag("Peter"))
            _peterIn = true;
    }

    private void OnTriggerExit(Collider other) 
    {
        if (other.CompareTag("Player"))
            _playerIn = false;
        if (other.CompareTag("Peter"))
            _peterIn = false;
    }
}

第二步:全局状态判断

在PeterSystem中遍历所有触发器实例,只要有一个满足条件就触发追击:

public class PeterSystem : MonoBehaviour
{
    public static bool PlayerSpooted = false;
    private TriggerDetector[] _allTriggers;

    void Start()
    {
        // 一次性获取场景中所有触发器检测脚本
        _allTriggers = FindObjectsOfType<TriggerDetector>();
    }

    void Update()
    {
        // 默认设为未发现,遍历后只要有一个满足就改为发现
        PlayerSpooted = false;
        foreach (var trigger in _allTriggers)
        {
            if (trigger.IsBothInside())
            {
                PlayerSpooted = true;
                break; // 找到符合条件的就停止遍历,节省性能
            }
        }
    }
}

方案2:事件驱动优化(性能更优)

如果场景中触发器数量较多(比如50个),每帧遍历会有轻微性能消耗,可以改成状态变化时才触发检查:

修改触发器脚本(添加事件通知)

public class TriggerDetector : MonoBehaviour
{
    private bool _playerIn = false;
    private bool _peterIn = false;
    public Flashlight flashlight;
    // 定义状态变化事件,全局监听
    public static event Action OnTriggerConditionChanged;

    private void OnTriggerEnter(Collider other) 
    {
        bool wasSatisfied = IsBothInside();
        
        if (other.CompareTag("Player"))
            _playerIn = true;
        if (other.CompareTag("Peter"))
            _peterIn = true;

        // 只有状态发生变化时才通知全局
        if (wasSatisfied != IsBothInside())
            OnTriggerConditionChanged?.Invoke();
    }

    private void OnTriggerExit(Collider other) 
    {
        bool wasSatisfied = IsBothInside();
        
        if (other.CompareTag("Player"))
            _playerIn = false;
        if (other.CompareTag("Peter"))
            _peterIn = false;

        if (wasSatisfied != IsBothInside())
            OnTriggerConditionChanged?.Invoke();
    }

    public bool IsBothInside()
    {
        return _playerIn && _peterIn && flashlight.FlashLightIsOn;
    }
}

修改PeterSystem(监听事件)

public class PeterSystem : MonoBehaviour
{
    public static bool PlayerSpooted = false;
    private TriggerDetector[] _allTriggers;

    void Start()
    {
        _allTriggers = FindObjectsOfType<TriggerDetector>();
        // 注册事件监听
        TriggerDetector.OnTriggerConditionChanged += UpdateDetectionState;
        // 初始检查一次状态
        UpdateDetectionState();
    }

    void OnDestroy()
    {
        // 取消事件注册,避免内存泄漏
        TriggerDetector.OnTriggerConditionChanged -= UpdateDetectionState;
    }

    void UpdateDetectionState()
    {
        PlayerSpooted = false;
        foreach (var trigger in _allTriggers)
        {
            if (trigger.IsBothInside())
            {
                PlayerSpooted = true;
                break;
            }
        }
    }
}

额外注意事项

  • 确保Player和Peter的Collider组件中,至少有一个带有Rigidbody(即使设为Kinematic),否则Unity不会触发触发器事件。
  • 检查标签拼写是否完全匹配(区分大小写),避免因标签错误导致检测失效。
  • 如果手电筒是全局唯一的,可将flashlight改为静态引用,无需每个触发器单独绑定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 16:05:09