Unity优化篇 | Unity脚本代码优化策略,快速获取 游戏对象 和 组件 的方法文末送书

Posted 呆呆敲代码的小Y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity优化篇 | Unity脚本代码优化策略,快速获取 游戏对象 和 组件 的方法文末送书相关的知识,希望对你有一定的参考价值。

📢前言

  • 本文是 Unity优化篇 系列的一篇文章,同时也包是含在 『Unity系统学习专栏⭐️』里的文章。
  • 本专栏是我总结的Unity学习类的文章,适合Unity入门和进阶的小伙伴。
  • 订阅该专栏之后 Unity基础知识学习Unity 进阶技巧Unity 优化 篇 几个专栏的文章都可以查看。
  • 对Unity感兴趣的小伙伴千万不要错过哦,目前专栏正在优惠中,具体内容可以看该专栏的导航帖。
  • 本篇文章就来讲一下 Unity中的脚本代码优化策略,一起来学习一下吧!

🎬Unity脚本代码优化策略

上一篇文章中介绍了 Unity中的优化概念Unity自带的性能分析工具Profiler 以及 性能分析的方法
在看完这篇文章之后应该对Unity中的优化有了一个大概的观念。

那接下来就是来看优化中最常用的 脚本代码优化 了。

编写脚本会占据大量的开发时间,而且脚本编写是一个含义宽泛的术语。
所以本系列只限于Unity相关的脚本,包括MonoBehaviourGameObject 和 相关功能的问题。

大概内容如下所示:

获取游戏对象访问组件空引用快速检索协程GameObjectTransform的使用使用合适的数据结构游戏对象间通信数学计算合理反序列化行为等。

下面就来挨着学习一下相关内容。


📒获取游戏对象的方法

在Unity中获取游戏对象是经常会用到的。

我们要 获取某些游戏对象 对其进行一些逻辑处理 或者 拿到该游戏对象身上的某些组件进行某些处理。

而且在Unity中获取游戏对象的方法有很多种,下面一起来看一下。

  1. GameObject.Find() 通过对象名称(Find方法)
  2. Transform.Find() 通过对象名称(Find方法)
  3. GameObject.FindWithTag 通过标签获取单个游戏对象(FindWithTag方法)
  4. GameObject.FindGameObjectWithTag() 通过标签获取单个游戏对象(FindGameObjectWithTag方法)
  5. Transform.FindObjectOfType() 通过类型获取单个游戏对象(FindObjectOfType方法)
  6. Transform.FindObjectsOfType() 通过类型获取多个游戏对象(FindObjectsOfType方法)
  7. transform.GetChild() 通过索引获取单个游戏对象
  8. 通过 Public 声明对象,从检查器面板上手动拖拽获取

Unity中查找获取游戏对象的方法应该都在这里了,下面简单来介绍下这几种方法。


🏳️‍🌈GameObject.Find()

GameObject.Find() 通过对象名称(Find方法)

该方法通过输入对象名称的方式获取对应的游戏对象

static GameObject  Find  (string  name )

使用示例如下:

    // 找对象
    GameObject parent = GameObject.Find("GameObject/ObjectName");

特点:

  • 全局查找参数名称游戏物体;
  • 不对禁用(隐藏)物体进行查找;
  • 若有同名物体时根据层级关系进行查找。

🏳️‍🌈Transform.Find()

Transform.Find() 通过对象名称(Find方法)

该方法也是通过输入对象名称的方式获取对应的游戏对象

GameObject.Find() 使用方法一致,不过也是有区别的
具体请看这篇【100个 Unity小知识点】 | Unity两种获取游戏对象的方法,GameObject.Find 和 Transform.Find区别

public Transform Find(string name);

使用示例:

// 找对象
    Transform parent = transform.Find("Root/ObjectName");

特点:

  • 只能找其子物体,不能找其同级或更高层级物体
  • 找子物体时不考虑是否被禁用(隐藏)
  • 找多层子物体时需写全路径(否则即使存在也找不到)

🏳️‍🌈GameObject.FindWithTag()

GameObject.FindWithTag() 通过标签获取单个游戏对象(FindWithTag方法)

返回一个用tag做标识的活动的对象,如果没有找到则为null。

static GameObject FindWithTag (string tag) 

使用示例:

GameObject p1= GameObject.FindWithTag("player1");
if (null != p1)

	Debug.Log("p1!");

Tag标签 可以在面板上进行 添加和复制,先选中游戏对象然后进行操作。

特点:

  • 查找不到禁用物体,使用时需确认要查找的物体是启用(显示)状态;
  • 有多个有游戏物体使用同一标签时,尽量不使用FindWithTag此方式查找单一游戏体,因为查找顺序会受到层级影响;
  • 查找未定义标签会报错,使用时需确认查找的字符串是已定义的标签;
  • 查找的标签是已定义但是未使用过,会找不到游戏物体,返回空值。

🏳️‍🌈GameObject.FindGameObjectWithTag()

FindGameObjectWithTag() 通过标签获取单个游戏对象(FindGameObjectWithTag方法)

FindGameObjectWithTag 和 上面的FindWithTag用法一致,功能也是一样的

他俩的具体区别我暂时没有发现,如果知道的小伙伴欢迎评论区告诉我呀!

public static GameObject FindGameObjectWithTag(string tag);

🏳️‍🌈Transform.FindGameObjectsWithTag()

Transform.FindObjectsOfType() 通过类型获取多个游戏对象(FindObjectsOfType方法)

public static GameObject[] FindGameObjectsWithTag(string tag);

返回一个用tag做标识的活动的游戏物体的列表

上面说了一下Transform.FindObjectOfType() 方法,与这个 Transform.FindObjectsOfType() 的方法使用起来也是一样的。

只不过该方法可以根据 标签 查找当前场景中 所有这个标签的游戏物体 并返回所有物体的数组。

所以他们的区别就是一次性 获取单个符合要求的游戏对象获取多个游戏对象

要注意的是Transform.FindObjectsOfType() 返回的是一个游戏对象的数组,找不到则返回null


🏳️‍🌈GameObject.FindObjectOfType()

Transform.FindObjectOfType() 通过类型获取单个游戏对象(FindObjectOfType方法)

public static T FindObjectOfType<T>() where T : Object;

返回类型为type的活动的第一个游戏对象

使用示例:

        Camera c1 = GameObject.FindObjectOfType<Camera>();
        Debug.Log("游戏对象c1:" + c1);

特点:

  • 查找不到禁用物体,使用时需确认要查找的物体是启用(显示)状态;
  • 查找场景中不存在类型时会返回null,不会报错;

🏳️‍🌈GameObject.FindObjectsOfType()

Transform.FindObjectsOfType() 通过类型获取多个游戏对象(FindObjectsOfType方法)

public static T[] FindObjectsOfType<T>() where T : Object;

该方法和上面的方法使用方式一样,只不过返回的是一个该类型的数组

使用示例:

  Camera[] c2 = Transform.FindObjectOfType<Camera>();
  Debug.Log("游戏对象c2:" + c2);

🏳️‍🌈transform.GetChild()

transform.GetChild() 通过索引获取单个游戏对象

public Transform GetChild(int index);

使用实例:

//找一级子物体的第一个物体
Transform child1 = transform.GetChild(0);

使用方式:几个层级就几个GetChild(),参数就是当前层级的第几个物体(从0开始)

特点:

  • 以自身为基础,查找子物体(注意索引从0开始,写多报错)
  • 可以使用transform.parent.parent 的形式无限向上,然后再GetChild(),就达到了查找父层级或更高层级物体的目的
  • 弊端是依赖游戏物体的层级关系,使用时需确保层级关系相对稳定。若不稳定会导致每次修改游戏体时还要修改代码,这就加大了工作量了。

🏳️‍🌈通过 Public 声明对象,从检查器面板上手动拖拽获取

在Unity中除了上面的几种通过代码获取游戏对象的方法之外。

还有一种很常用的方式来获取游戏对象或者组件,那就是使用Public修饰符来声明对象。

    public GameObject _g1;
    public Button _button1;


这种方式在我们日常开发的时候也可以使用,因为直观方便还不用多写代码就可以直接获取对象。

网上对比最多的就是 PublicTransform.Find 的方式哪种更消耗性能。

在我查阅了一些资料之后并没有看到有关于这块很详细的资料,简单测试发现这两种性能消耗的差异不大。

所以说还是要看平时个人的代码规范 和 项目要求 来选择使用哪种方式。

整理了以上几个获取对象的方法,各有特点。

但是我通常使用的就是Transform.Find(),该方法可以获取到非激活状态的游戏对象,性能消耗也不高。

获取游戏对象部分 参考了 陈言必行大佬 的这篇博客https://czhenya.blog.csdn.net/article/details/117530824
可以去这篇博客查看更详细的实例方法,获取游戏对象的方法这里就简单介绍到这,下面看一下获取组件的最佳方法。


📒获取组件的最佳方法

上面说完了获取游戏对象的几种方法,接下来再来讲一下获取组件的最快方法。

在Unity中获取组件的方法也有几种,下面一起来看一下。

获取组件的方法主要是使用 GetComponent() 方法,但是 GetComponent() 有一些变体,他们性能消耗有所不同。

  1. GetComponent(string)
  2. GetComponent()
  3. GetComponent(typeof(T))
public Component GetComponent(string type);

public T GetComponent<T>();

public Component GetComponent(Type type);

主要是通过这三个变体方法来获取组件

根据上百万次测试发现 GetComponent<T>()GetComponent(typeof(T)) 方法性能差异不大。
但是 GetComponent(string) 方法则比上面两种方法慢的多。

所以说正常情况下尽量不使用 GetComponent(string) 方法即可

大多数情况下还是使用 GetComponent<T>() 比较多,平时都在使用这个方法获取组件。

使用示例:

    Demo1 _demo1;

    _demo1 = transform.GetComponent<Demo1>();
    _demo1 = (Demo1)transform.GetComponent("Demo1");
    _demo1 = (Demo1)transform.GetComponent(typeof(Demo1));

📒移除空的回调定义

Unity中编写脚本的主要意义是从 MonoBehaviour 继承的类中编写回调函数,然后Unity会在必要时进行调用。

一般来说最常用的四个回调是 Awake()Start()Update()FixedUpdate()

我们在创建一个新脚本的时候默认会帮我们添加两个回调 Awake()Update()

关于 Awake()Update() 方法的意义就不多做讲解了,看优化篇的应该都知道是干嘛用的。

Awake()主要是在程序运行的第一帧进行调用,而Update()则是在每一帧都会进行调用。

MonoBehaviour 在场景中第一次实例化的时候,Unity会将任何定义好的回调添加到一个函数指针列表中,他会在关键时刻调用这个列表。

即使我们在Awake()Update() 中并没有写任何的方法,Unity也会挂接到这些回调中。

核心Unity引擎可能没有意识到这些函数体是空的,由于引擎调用他们的开销就会浪费少量的CPU。

但是我们每当在Unity中创建一个MonoBehaviour 脚本的时候,都会添加这两个回调。

所以说一旦项目偏大,脚本偏多,功能比较复杂之后,因为这个原因造成的性能浪费是没有任何收益的。

所以未使用的回调一定要直接删除掉,防止不必要的浪费。

下面使用3000个组件进行的测试实例,都带有Update的回调,左边是未激活组件的状态,右边则是激活组件之后的状态。

可以看出CPU使用情况的差异还是很大的,所以移除空的回调很有必要。


💬总结

  • 本文对 获取 游戏对象 和 组件 的方法进行了一个较详细的说明
  • 还包括 空的回调定义 造成的影响,当项目越大时浪费的性能则越多。
  • 关于脚本代码优化策略的内容还有一些,等下一篇博客在接着进行学习介绍。
  • 本篇文章就先讲到这里,后续的Unity脚本代码优化策略会继续更新。

🚀往期优质文章分享


🚀 优质专栏分享 🚀
  • 🎄如果感觉文章看完了不过瘾,可以来我的其他 专栏 看一下哦~
  • 🎄比如以下几个专栏:Unity基础知识学习专栏Unity游戏制作专栏Unity实战类项目 算法学习专栏
  • 🎄可以学习更多的关于Unity引擎的相关内容哦!直接点击下面颜色字体就可以跳转啦!
❤️ 游戏制作专栏 ❤️
🧡 Unity系统学习专栏 🧡
💛 Unity实战类项目 💛
💚 算法千题案例 💚
💙 Python零基础到入门 💙

【游戏开发爱好者社区】活动进行中,每周打卡送书籍等礼品,期待你的加入

🚀 社区活动,重磅来袭 🚀

【游戏开发爱好者社区】在本周重磅新推出【每日打卡】活动

🎁 新玩法,奖励升级!游戏开发爱好者社区:https://bbs.csdn.net/forums/unitygame

社区中心思想今天你学到了什么?

在社区你可以做些什么: 每日强化知识点,白嫖书籍礼品!

一个人可以走的很快,一群人才能走的更远!🔥爆C站的游戏开发爱好者社区欢迎您的加入!

更多白嫖活动详情:https://bbs.csdn.net/forums/unitygame?typeId=19603


👑评论区抽奖送书

最后在评论区进行抽取三名幸运的小伙伴送下面这本书籍

  • 这次博主多方联系,最终在清华出版社搞到了一些unity 相关的书籍,在这里搞个小活动抽奖送给大家,对看书感兴趣的小伙伴可以参加一波呀,刚开始可能参与的不多,抽中概率很大哦!

  • 《C#实践入门(快捷学习C#编程和Unity游戏开发第4版.NET开发经典名著) 》

  • 一本非常经典的C#编程名著,结合Unity开发使用效果卓佳!

  • 尤其是想从事Unity开发刚入门的小伙伴,没必要去买专门的C#书籍,这本书就可以让你了解Unity用到的C#相关知识,对症下药可以极大地提高学习效率!

🎁抽奖规则

规则如下:

🚀 规则如下🚀
  • 给本篇博客文章 点赞 收藏 评论 三连,然后在评论区评论里点赞数 排名前五的评论抽取三个小伙伴!送一本Unity的书籍!

  • 总共抽三本,中的几率还是很大的哦~ 想看书的小伙伴参与起来!

  • 中奖信息明天下午本篇文章评论区公布!记得留意呀!

  • 没抽到的,但是喜欢这本书的小伙伴也可以在网上自行购买哈,官方正品商店购买即可!

🚀 规则如上🚀

🎄推荐理由(⭐⭐⭐⭐⭐)

  •  在如今的技术环境中学习编程无疑是一项艰巨的任务,尤其是当面对众多可选的编程语言时。幸运的是,《C#实践入门(快捷学习C#编程和Unity游戏开发第4版.NET开发经典名著) 》消除了充满不确定性的学习过程,为初学者指明了一条成为有竞争力的Unity程序员的捷径。

  •  在本书中,你将首先学习编程的基本构成要素,涵盖从变量、方法、条件语句到类、面向对象编程在内的所有知识。打好基础后,你将开始探索Unity接口并创建C#脚本,进而将学到的知识转换为简单的游戏机制。

  •  在整个学习过程中,你将掌握编程方面的最佳实践以及宏观的架构知识,例如游戏管理器与灵活的应用程序架构。等到本书结束时,你将对C#的一些中级主题了然于胸,例如泛型、委托、事件等,从而在自己的项目中加以灵活运用。

书本图片如下,喜欢的小伙伴参与起来!也可自行京东官网购买哦!

京东链接:https://item.jd.com/13263338.html


温馨提示: 点击下面卡片可以获取更多编程知识,包括各种语言学习资料,上千套PPT模板和各种游戏源码素材等等资料。更多内容可自行查看哦!

以上是关于Unity优化篇 | Unity脚本代码优化策略,快速获取 游戏对象 和 组件 的方法文末送书的主要内容,如果未能解决你的问题,请参考以下文章

Unity 优化篇 | 优化专栏《导航帖》,全面学习Unity优化技巧,让我们的Unity技术上升一个档次

Unity 优化篇 | 优化专栏《导航帖》,全面学习Unity优化技巧,让我们的Unity技术上升一个档次

Unity优化篇| Unity3D场景 常用优化策略,遮挡剔除层消隐距离技术 和 LOD多层次细节

Unity优化篇| Unity3D场景 常用优化策略,遮挡剔除层消隐距离技术 和 LOD多层次细节

Unity技巧Unity中的优化技术

Unity3D性能优化之资源导入标准和属性设置篇