浅谈Unity中的MonoBehaviour

从第一次在unity创建C#脚本开始就会发现每个脚本都继承了一个叫MonoBehviour的类,那么MonoBehaviour是做什么的呢?


首先明确一点,Unity引擎将所有的代码的执行都放在了一个主线程中,Update,FixedUpdate等都是按照Unity调度机制依次自行,没有任何一个地方是并行执行的,都是依次执行的。
浅谈Unity中的MonoBehaviour_第1张图片
一层层进入MonoBehaviour父类,不妨看出MonoBehaviour间接继承了Component,所以继承自MonoBehaviour脚本的作用其中之一就是充当组件的角色,所以当我们需要将一个自定义脚本以组件的形式添加到对应的GameObject时(Unity中Inspector面板中的每一项都称为组件,包括创建的脚本),该脚本是必须要继承MonoBehaviour,否则无法添加为组件。


那么如果继承了MonoBehaviour,那么这个类的生命周期是怎样的呢?
首先可以将生命周期分为两种状态

  • 编辑器下的状态:只有Reset函数一个
  • 运行状态(真正的生命周期):除Reset函数外,其余函数都在运行状态执行(加特殊字段,也可以在编辑器运行)

Reset函数:

只能在编辑器模式下执行,与脚本是否激活无关,点击Reset或者添加该脚本到游戏物体上时执行(这个方法我几乎没有用过不是很常用)


Awake,OnEnable,Start函数:

Awake:仅执行一次,当游戏物体被创建的时候执行,无论该脚本是否处于激活状态,只要游戏物体处于激活状态即可执行。
在Inspector面板中的组件上有一个复选框,这是在脚本中有生命周期函数时才会显示的,但是测试后发现只有Awake和Reset并不会出现复选框,因为这两个函数与脚本是否激活无关。
OnEnable:可执行多次,当组件设置enable = true或游戏物体设置active = true时调用(包括脚本一开始处于激活状态时运行游戏的第一次)。
Start:仅执行一次,当游戏物体处于激活状态并且脚本也处于激活状态才可以执行。



上述是生命周期的开始,先将生命周期的运行跳过,先来说说生命周期的结束。

 

OnApplicationQuit,OnDisable,OnDestroy函数:

OnApplicationQuit:当你的应用程序退出时,会先调用OnApplicationQuit。
OnDisable:除在OnApplicationQuit调用时会调用,还会在设置enable = false 或设置active = false时调用(如果脚本一开始处于未激活状态则不会调用)。
OnDestory:除在OnApplicationQuit调用时会调用,还有在Destroy游戏物体时调用。

 



看完了生命周期的开始与结束,下面是比较重要的部分——生命周期的运行过程

浅谈Unity中的MonoBehaviour_第2张图片
FixedUpdate,Update,LateUpdate函数:
可以将生命周期的运行以Update函数作为分割线,FixedUpdate,物理系统,输入系统的相关函数都是在Update之前先执行,携程是在Update之前LateUpdate函数之后执行。



FixedUpdate与Update:
FixedUpdate:每帧调用而且每帧的时间间隔是固定的,不受帧率(FPS)影响。每一帧的时间是在Edit->Project Settings->Time面板中的Fixed Timestep设置,默认为0.02s,则0.02s为一帧也就是每帧之间的间隔是0.02s。
对于更新频率比较稳定的物理系统来说就合适放在FixedUpdate中处理
Update:每帧调用但是每帧的时间间隔不固定,受到帧率影响,而帧率(FPS)与电脑性能有关。
例如FPS是30,则每秒执行30帧,如果FPS是10,则每秒执行10帧。是不稳定的。

在Update和FixedUpdate中输出Time.time:因为FixedUpdate每帧之间是固定的时间间隔0.02秒(Fixed Timestep设置的值),所以不难发现FixedUpdate中的Time.time数值每帧都加了固定0.02,而Update中是不稳定的。
浅谈Unity中的MonoBehaviour_第3张图片

不能在Update中直接移动的原因:

例如:
浅谈Unity中的MonoBehaviour_第4张图片
1.人物移动会出现抖动:因为Update中每帧的间隔是不一样的,所以移动不是平滑的则会抖动。
2.性能不同的设备帧率不同:假如A和B两台电脑,A电脑的帧率为50则人物一秒钟移动50米,B电脑的帧率为30则人物一秒钟移动30米,这肯定是不行的。

 

解决办法就是乘上Time.deltaTime。

->Time类的相关详解:Time类详解


 

LateUpdate比较好理解:
LateUpdate:与Update一样每帧之间的时间是不固定的,受到帧率影响,而帧率(FPS)与电脑性能有关。
LateUpdate是三个Update中最后一个执行的,举个例子,比如一家三口要出去玩,每个人的起床都在Update中执行,则出发应该在LateUpdate中执行,这样就保证了在都都起床之后才出发。

我们在Update,FixedUpdate,LateUpdate中都输出Time.realtimeSinceStartup,明显发现每一帧都是FixedUpdate->Update->LateUpdate的执行顺序。
浅谈Unity中的MonoBehaviour_第5张图片
所以相机的跟随一般在LateUpdate中实现,这样可以确保在人物移动结束后再进行相机的跟随。



很重要的一点,官方对于LateUpdate解释成一句话:LateUpdate是在所有Update函数调用后被调用。
比如写两个脚本,挂载到同一个游戏物体上,同时拥有这三个关于Update的方法,运行查看输出结果。
浅谈Unity中的MonoBehaviour_第6张图片浅谈Unity中的MonoBehaviour_第7张图片

浅谈Unity中的MonoBehaviour_第8张图片
每个游戏物体中脚本上的同名函数会被放在同一个线程中,执行完Awake线程再执行Stat线程.....
也就是说在Unity中所有脚本的Awake函数执行完才会执行Start函数,所有Start函数执行完才会执行Update函数。


普通类与继承MonoBehaviour类的区别:

1.继承MonoBehaviour的类不需要创建它的实例,也不能自己创建(用new),因为从MonoBehaviour继承过来的类Unity会自动创建实例,并且调用被重载的函数(Awake,Start.....)。
如果用new会出现以下警告:


警告说的很明显,就是如果继承了MonoBehaviour就不允许你用new创建,你可以用添加组件的方式替代,或者根本没有基类(不继承MonoBehaviour的普通类),但是好像并不影响程序,这点很奇怪。
2.不继承MonoBehaviour不能使用Invoke,Coroutine,print以及生命周期函数等。
3.不继承MonoBehaviour不能挂在到Inspector上,也就是说不能当组件使用也不能看到一些数据。

你可能感兴趣的:(Unity技术)