UPL5-7——GameObject 优化建议

更快的空引用检测

我们在进行开发时,经常会进行 GameObject 对象判空处理,常常会使用类似这样的判空处理

1
2
3
4
if (gameObject != null)
{
// 存在再处理
}

其实不仅是 GameObject​,MonoBehaviour 等等 Unity 自带的内容如果这样进行判空处理,都是最简单的方式,但是这种方式并不是效率最高的

这里推荐一种更快的判空处理方式,利用 System.Object​ 中的引用判断方法 ReferenceEquals

1
2
3
4
if (!System.Object.ReferenceEquals(gameObject, null))
{
// 存在再处理
}

虽然它的写法有些复杂,但是但从性能上考虑,它的消耗更低,执行的更快,直接进行了引用地址的比较

这是因为通过 ==​ / !=​ 对 null​ 的判断,还是会进行桥接访问的方式,会有额外的桥接开销
System.Object​ 中的 ReferenceEquals 方法,并不会进行桥接访问,只做引用对比

但是,使用时需要注意陷阱,即如果销毁了 GameObject对象,但是并没有对 GameObject对象进行置空,那么通过两种判断方式,获取的结果是不同的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public GameObject obj;    // 在外部关联一个GameObject

private void Start()
{
DestroyImmediate(obj);

if (obj != null)
{
Debug.Log("!= null 对象不为空");
}
if (!ReferenceEquals(obj, null))
{
Debug.Log("ReferenceEquals(obj, null) 引用不为空");
}
}

image

避免检索字符串属性

我们应该尽量避免使用 GameObject​ 中的字符串属性直接进行判断,比如 tag​、layer​、name 等,因为会产生桥接
如果需要每帧使用这些属性进行逻辑处理,建议先缓存他们,或者用相关 API 替代

例如,针对 tag​ 的比较,可以将 gameObject.tag == "TagName"​ 改为 gameObject.CompareTag("123")

减少 Find 相关方法的使用

GameObject​ 中提供了很多 Find 相关方法
比如:

  • GameObject.Find(string name)
  • GameObject.FindWithTag(string tag)

等等

我们应该减少这些方法的使用,因为他们的底层会进行遍历和字符串比较,性能表现非常差,由于是通过字符串查找,容错性也较差
我们应该通过以下方式来优化

  1. 初始化时找到对象并缓存
  2. 直接拖拽引用进行关联
  3. 对于需要全局使用的对象,可以通过单例模式的方式进行对象管理

等等

减少 SendMessage 相关方法的使用

Unity 中提供 SendMessage​、BroadcastMessage()​、SendMessageUpwards()​ 相关方法,用于基于字符串反射式的进行消息传递,通知其他组件执行某些逻辑
这些方法内部,会遍历 GameObject 上所有组件,对每个组件进行反射式调用判断,并且每次都需要重新查找,匹配字符串,效率非常低下
它们是 低性能、高开销、不可控的“黑箱通信”,应尽量避免使用

我们可以通过以下方式进行优化

  1. 缓存对应组件,直接调用对应组件中的方法
  2. 利用基于观察者设计模式的事件中心,实现低耦合跨脚本跨对象的逻辑调用

等等