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

Unity多GameObject与管理器通信优化及批量通知方案咨询

Unity场景交互与事件系统问题

场景背景

  • 场景内存在名为ResourceManager的GameObject,其脚本包含多个整数变量(如resourceAmount)
  • NPC对象由预制体动态生成,带有NPC标签,仅在场景运行时创建
  • 当前实现:NPC触发碰撞器时,在Start()方法中通过FindObjectOfType<ResourceManager>()获取实例,执行resourceManager.resourceAmount +=2

疑问1

若有100个NPC使用该脚本,是否会出现问题?是否应替换为Action、Delegate或Event系统?若使用事件系统,是否由ResourceManager监听事件?100个NPC调用同一脚本触发事件是否有问题?

疑问2

当ResourceManager中资源量达到100时,需通知所有NPC执行指定脚本,当前考虑按标签查找对象但担心CPU开销,求高效实现方案。


解决方案

疑问1解答

  • 当前实现的问题
    FindObjectOfType是遍历场景所有活跃对象来匹配类型的API,100个NPC同时在Start()中调用它,会在初始化帧产生大量重复遍历操作,很可能导致帧时间波动。另外这种硬编码查找的方式耦合性极强,后续ResourceManager的类型或挂载对象变动时,所有NPC脚本都需要修改,维护成本很高。

  • 必须替换为事件/委托系统
    肯定要换,事件系统能彻底解耦NPC与ResourceManager,同时性能更优。

  • 事件系统的正确实现方式
    让ResourceManager暴露一个静态事件,NPC无需主动查找ResourceManager,直接触发事件传递资源增量即可;ResourceManager自身监听该事件,在回调中处理资源增加逻辑。

示例代码:

ResourceManager脚本

public class ResourceManager : MonoBehaviour
{
    // 定义静态事件,传递资源增量
    public static event Action<int> OnResourceGained;
    public int resourceAmount;

    void OnEnable() => OnResourceGained += AddResource;
    void OnDisable() => OnResourceGained -= AddResource;

    void AddResource(int amount)
    {
        resourceAmount += amount;
    }
}

NPC脚本

public class NPC : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        // 触发条件满足时,直接调用事件传值
        ResourceManager.OnResourceGained?.Invoke(2);
    }
}
  • 100个NPC触发事件无问题
    完全不用担心,事件的底层是委托链,这里只有ResourceManager一个订阅者,触发事件的开销和直接调用resourceAmount +=2几乎一致。就算后续增加更多订阅者,事件系统的处理效率也远高于批量查找对象。

疑问2解答

使用GameObject.FindGameObjectsWithTag确实存在CPU开销问题,每次调用都需要遍历全场景对象,资源满的瞬间若场景对象较多,容易导致卡顿。高效的解决方案是主动维护NPC集合:

核心思路

在ResourceManager中维护一个NPC的集合,NPC在激活时自动加入集合,销毁/失活时自动移除。当资源量达到100时,直接遍历该集合通知所有NPC,无需每次去场景中查找。

代码实现

修改ResourceManager脚本

public class ResourceManager : MonoBehaviour
{
    public static event Action<int> OnResourceGained;
    // 用HashSet替代List,提升频繁增删操作的效率
    public static HashSet<NPC> activeNPCs = new HashSet<NPC>();
    public int resourceAmount;

    void OnEnable() => OnResourceGained += AddResource;
    void OnDisable() => OnResourceGained -= AddResource;

    void AddResource(int amount)
    {
        resourceAmount += amount;
        if (resourceAmount >= 100)
        {
            NotifyAllNPCs();
        }
    }

    void NotifyAllNPCs()
    {
        foreach (var npc in activeNPCs)
        {
            npc.DoResourceFullLogic();
        }
    }
}

修改NPC脚本

public class NPC : MonoBehaviour
{
    void OnEnable() => ResourceManager.activeNPCs.Add(this);
    void OnDisable() => ResourceManager.activeNPCs.Remove(this);

    void OnTriggerEnter(Collider other)
    {
        ResourceManager.OnResourceGained?.Invoke(2);
    }

    // 资源满时执行的指定逻辑
    public void DoResourceFullLogic()
    {
        // 此处编写具体逻辑,如切换状态、播放动画等
        Debug.Log("资源已满,执行NPC指定逻辑");
    }
}

额外优化

如果NPC数量极多(如数千个),可将通知逻辑改为协程分批执行,避免单帧处理过多逻辑导致卡顿:

IEnumerator NotifyAllNPCsCoroutine()
{
    int count = 0;
    foreach (var npc in activeNPCs)
    {
        npc.DoResourceFullLogic();
        // 每处理10个NPC就暂停一帧,平衡性能
        if (++count % 10 == 0)
            yield return null;
    }
}

然后将NotifyAllNPCs中的调用替换为StartCoroutine(NotifyAllNPCsCoroutine());


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 00:50:07