Unity的awake和start以及active和static的调用顺序 - chunlieater/chunlifeet GitHub Wiki

unity的场景列表中的物体如果active是true时,在runtime状态下就会自动执行该物体上所有组件的awake函数,而不管该组件本身是否active。

如果场景列表中的物体active是false,则该物体上所有组件的awake都不会被执行。

awake函数的执行顺序是随机的,场景列表中哪个物体的组件先被初始化,并没有一定的规律。

  • start函数则是在场景列表中所有物体上的awake都执行完毕后,再开始调用,调用的顺序和awake的执行顺序相同,如果一个物体上的组件本身active为false,则该组建的start不被执行。
  • 动态创建物体时也是如此,在场景中动态创建一个物体时,该物体如果active是true,则创建时该物体上组建的awake会立即执行,如果该物体的active时false,则该物体上的组建都不会执行awake,等后续程序激活该物体active时,再执行active。
  • 还有就是,不管场景物体是否active,其它物体组件中想查找到这个物体的属性,还是可以找到的。但是有时候要注意一下调用顺序,比如一个物体的组件在其start中将另外一个物体的active关掉了,那么这个物体如果awake执行顺序在后面,导致该物体的start执行晚于前者,那他身上的组件就不能执行start了。除非在runtime状态下再次打开该物体的active。一句话:如果想引用某物体的组件,那么请不要在引用前将它关闭active

所以如果想在一个物体的组件里引用场景其它物体的组件属性,那么最好的方法是设置一个初始化的init函数,并在awake或者start中进行初始化调用。 至于放在哪个函数里,看组件的调用情况而定,比如想在一个脚本中引用自身的其他脚本属性作为变量,那就放在awake里,这样如果在其他物体的脚本中的start中动态生成了该脚本的物体,它就可以立刻执行awake给自己的变量赋值,并且进行其他初始化,如果放在start里,则因为要等引用它的start执行完毕才能执行该物体的start,所以可能初始化失败。

注意一点,如果被引用物体的组建属性没有初始化,则引用过来也是空,即便被引用物体后来进行了赋值,对引用物体来说也毫无影响,因为引用类型是按地址值传递的,在引用时已经进行了地址复制,被引用物体赋值时则是另开了内存空间,和引用过去的地址已经不一样了。

如果在一个物体组件里引用了另外一个物体的属性,那么不管这个物体是否active,都是可以引用成功的。简单来说物体是否active,只决定了它是否进入unity的循环系统,并不影响其存在。

所以如果想避免一个物体的组件引用另一个物体组件里的属性出现空值,最好是在引擎里手动把被引用的属性赋值,用[serializeFiled]。这样不管被引用的物体是否在运行时启动,它的组件属性都是可以被其它物体引用的。

还有动态生成的物体,动态生成的物体只有在实例化的时候才会调用awake,同样遵循先awake后start的顺序,比如如果在一个初始物体的组件的awake中生成了一个新物体,那么新物体的组件就会立刻调用自身的awake,然后所有awake调用完毕,再调用初始物体的组件start,再调用新物体的组件start。 另一个例子,如果在初始物体组件的start中生成了一个新物体,那么新物体的组件也会立刻调用awake,初始物体start退出后,新物体再执行自身的start。

又发现静态构造函数执行顺序也很诡异,**只要预制体中挂载了某脚本,不管场景中是否有该预制体,也不管该组件是否被勾选,在运行时这些脚本的静态构造函数和构造函数都会首先执行一次。**在这期间构造函数里调用unity相关组件的代码是不允许的,所以这样的脚本不能直接挂在预制体上,需要在其他脚本awake或者start里动态调用该组件的静态变量来激活。

总结一下 1 构造函数里不能用new来初始化unity组件,可以用addComponent,但是如果该组件已经挂载于预制体,那addComponent也不行。 2 构造函数的执行时间是,如果组件挂载到预制体,那运行时首先执行这些组件的构造函数,如果是在场景中的预制体上挂载了组件,那还要执行两次(原因不明)。 3 awake和start的执行时间是,如果挂载到预制体,并且在场景中,物体和脚本都是ative,那顺序执行awake和start,如果脚本关闭物体打开,则只运行awake,如果物体关闭,则都不运行。但构造函数不论如何都会运行。