Unity中Update调用GameObject.FindGameObjects的性能开销及优化方案
磁铁收集金币的性能优化问题
我正在开发一款游戏,需要实现玩家用磁铁收集金币的功能,以下是金币生成代码:
// Spawn Coins In Air void SpawnCoinsInAir () { float yPos = transform.position.y + 1f; float zPos = transform.position.z + 4f; for (int i = 0 ; i < 10 ; i += 1) { int line = i == 0 ? RandomsPlayerController.Instance.CurrentLineIdx : Random.Range(0, RandomsPlayerController.Instance.Lines.Count); for (int j = 0 ; j < Random.Range(3,6) ; j += 1) { zPos += j + 10f; var it = Instantiate( coinTransform, new Vector3(RandomsPlayerController.Instance.Lines [line].x,yPos , zPos), Quaternion.identity ); // 随机Y轴旋转 it.transform.DORotate ( Utilities.VectorY (Vector3.zero, Random.Range(-360,360)), 1f, RotateMode.FastBeyond360 ) .SetEase (Ease.InOutSine); Destroy (it, actionDuration + 1f); } } }
目前游戏中已生成所有金币,我需要通过标签查找每个金币并按最短距离收集。我想了解在Update函数中调用GameObject.FindGameObjectsWithTag查找所有金币的性能开销是多少,以及是否有兼顾性能的实现方法?以下是当前实现代码:
private void UseMagnet () { // 收集金币 foreach (var coin in GameObject.FindGameObjectsWithTag ("coin")) continue; } private void Update () => UseMagnet();
关于GameObject.FindGameObjectsWithTag在Update中的性能开销
GameObject.FindGameObjectsWithTag每次调用都会遍历整个场景内所有活跃的GameObject,逐一检查标签是否匹配。如果场景中金币数量较多(几十上百个),再加上其他大量场景对象,每帧调用会带来明显的CPU占用,直接导致帧率下降。- 这种遍历是同步阻塞主线程的,在移动平台等CPU性能有限的设备上,性能问题会被进一步放大。
兼顾性能的实现方法
1. 维护全局金币列表(推荐方案)
在金币生成和销毁时自动维护列表,彻底避免每帧遍历场景:
- 创建一个单例管理器(比如
CoinManager),用List<GameObject>或HashSet<GameObject>存储所有活跃金币。 - 修改金币生成代码,实例化后将金币加入列表:
var it = Instantiate(...); CoinManager.Instance.AddCoin(it); - 在金币的脚本中通过
OnDestroy回调,从列表中移除自身:void OnDestroy() { CoinManager.Instance.RemoveCoin(gameObject); } - 后续在
UseMagnet中直接遍历该列表,还能快速按距离排序:private void UseMagnet () { var sortedCoins = CoinManager.Instance.ActiveCoins .OrderBy(coin => Vector3.Distance(transform.position, coin.transform.position)) .ToList(); foreach (var coin in sortedCoins) { // 执行金币收集逻辑(比如移动到玩家位置) // ... } }
2. 减少查找调用频率
如果磁铁并非持续激活,仅在玩家触发技能期间执行查找(或处理列表),而不是每帧调用:
- 给磁铁设置激活状态标记,只有标记为激活时才执行收集逻辑,避免无意义的每帧运算。
3. 用物理触发器限定收集范围
如果磁铁有有效收集半径,给玩家添加SphereCollider(设为触发器),只处理进入范围的金币:
- 给玩家对象添加
SphereCollider,勾选Is Trigger并调整半径为磁铁有效范围。 - 在玩家脚本中实现触发器回调维护近距金币列表:
private List<GameObject> _nearbyCoins = new List<GameObject>(); void OnTriggerEnter(Collider other) { if (other.CompareTag("coin")) { _nearbyCoins.Add(other.gameObject); } } void OnTriggerExit(Collider other) { if (other.CompareTag("coin")) { _nearbyCoins.Remove(other.gameObject); } } - 之后在
UseMagnet中遍历_nearbyCoins,按距离排序后执行收集。
4. 对象池优化(针对金币频繁生成场景)
如果金币是重复生成销毁的,使用对象池可以避免实例化/销毁的额外开销,同时对象池本身会维护所有金币实例(活跃/非活跃),可直接从中获取活跃金币列表。
内容的提问来源于stack exchange,提问作者user17248503
相关产品推荐
相关产品推荐

