文章目录
简介:我是一名Unity
游戏开发工程师,皮皮是我养的猫,会讲人话,它接到了喵星的特殊任务:学习编程,学习Unity
游戏开发。
于是,发生了一系列有趣的故事。
4.1 怎么没有老鼠组件
我:“皮皮,你在干嘛?”
皮皮:“我在给物体挂组件,可是我找了半天,也没找到老鼠组件。”
我:“哈哈哈,Unity
没有帮我们实现老鼠的组件,需要我们自己写。”
皮皮:“那我们赶紧写一个老鼠组件吧。”
我:“没问题的,但在这之前,你得先搞明白什么是组件,它的运行机制。”
4.2 Component——组件
Component
,组件,顾名思义,就是游戏物体的组成部件。
我拿出扫地机器人,对着皮皮说:“你看这个扫地机器人,它会寻路移动、扫地、避开障碍物、充电等等,它之所以具备这些功能,是因为它有轮子、边刷、激光测距传感器、电源等等这些组件,这些组件拼装在一起通过合理的协同工作,才完整地构成了一台扫地机器人,游戏物体也一样。”
我打开Unity
工程,然后选中Main Camera
节点,接着说:“看这里,这个Main Camera
,它是一个游戏对象,它身上挂了三个组件:Transform
、Camera
、AudioListener
。”
皮皮:“那一只老鼠要挂多少组件呢?”
我:“这个要看需求和设计,你可以一个组件只实现一个单一的功能,然后挂很多组件,也可以把多个功能合理地整合在一个组件里,只挂这个组件。顺便说一句,所有物体都必须带一个Transform
组件。”
皮皮似懂非懂地点点头,我摸摸皮皮的小脑袋,接着说:“还有,组件也不能滥用,新手容易犯的一个错误就是组件泛滥。优秀的程序员,会懂得合理地使用设计模式,优雅地抽象、封装,把代码打理得干净漂亮,你们喵星人都自带洁癖属性,我觉得你会是一个优秀的程序员的。”
皮皮:“是不是所有的脚本都是Component
组件?”
我:“不是的,你注意看,我们创建的脚本,默认是继承MonoBehavIoUr
的。”
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehavIoUrScript : MonoBehavIoUr
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
皮皮:“Component
组件,和这个MonoBehavIoUr
又是什么关系呢?”
我:“问的好,这里就要介绍一下Unity
中的类继承关系了。”
我拿出一张白纸,在纸上画了Unity
中的部分类继承关系。
Unity
中,所有实体最上层的基类是Object
,Component
继承Object
,Behavior
继承Component
,MonoBehavior
继承Behavior
。
注意这里的
Object
是指UnityEngine.Object
,有别于.Net
的System.object
。事实上,UnityEngine.Object
也是一个System.object
,但一个System.object
不一定是一个UnityEngine.Object
。
比如:string a = "hello cat";
,a
是一个System.object
,但它不是一个UnityEngine.Object
。
皮皮:“晕了晕了,简直就是俄罗斯套娃啊。”
我:“哈哈,所以呢,继承MonoBehavIoUr
的脚本就是Component
组件,Unity
封装的MonoBehavIoUr
还提供了协程的功能,这个后面我再教你。”
我打开Visual Studio
继续说,:“在实际开发过程中,我们也会创建一些不是继承MonoBehavIoUr
的脚本,这些脚本就不是Component
,比如我现在封装一个静态的工具类:GameUtils
。”
using System;
public class GameUtils
{
/// <summary>
/// 获取当前系统时间,例:2020/11/21 16:46:29
/// </summary>
/// <returns></returns>
public static string GetNowTime()
{
return DateTime.Now.ToString();
}
}
GameUtils.cs
是一个脚本,但它不是一个组件。只有组件才可以AddComponent
(挂组件)到物体上,同时,组件要挂到物体上才能真正被运行起来。
4.3 MonoBehavIoUr脚本的生命周期
皮皮:“你刚刚说组件要挂到物体上才能真正被运行起来,那它是怎么运行的呢?”
我:“这里就要讲讲MonoBehavIoUr
脚本的生命周期了。”
可以参见Unity
官方文档的说明:
https://docs.unity3d.com/Manual/ExecutionOrder.html
原图地址:https://docs.unity3d.com/uploads/Main/monobehaviour_flowchart.svg
皮皮:“铲屎官,郑现在再封你一个官!”
我:“嗯?”
皮皮:“翻译官。”
我:“我挑重点,一个一个函数来吧。”
4.3.1 Reset()函数
Reset
,重置函数,这个函数只有在Editor
环境下才会被调用,运行过程中是不会调用Reset
的。
它是在用户点击Inspector
面板上Rese
t按钮或者首次添加该组件时被调用。Reset
最常用于在Inspector
面板中给定一个默认值。
4.3.2 Awake()函数
Awake
,唤醒函数,注意它只会被执行一次,这个要牢记。当游戏对象被创建的时候,游戏对象绑定的脚本会在该帧内执行Awake
函数。
如果脚本所挂的物体最开始不是激活状态,则Awake
函数此时不会被调用,激活物体时才会被调用。在Inspector
窗口的最左上方,有个勾选,如果不勾选,则表示隐藏物体。
如下,物体处于隐藏状态,此时运行Unity
,NewBehavIoUrScript
脚本中的Awake
函数并不会被调用,激活物体时,Awake
函数才会被调用。
值得一提的是,如果物体是激活状态,然后脚本是非激活状态,此时运行
Unity
,NewBehavIoUrScript
脚本中的Awake
函数是会被调用的。如下,
Awake
函数照样会被执行。4.3.3 OnEnable()函数
OnEnable
,激活函数,当脚本激活时被调用,激活一次就调用一次,激活n
次,OnEnable
就调用n
次。调用顺序在Awake
之后。
4.3.4 Start()函数
Start
,启动函数,Start
函数的调用位于Awake
函数之后,Start
函数会在Update
函数第一帧之前被调用,与Awake
函数一样只会被调用一次,与Awake
函数不一样的是当脚本最开始处于不激活状态,Awake
函数会被调用,而Start
函数不会被调用,只有激活了脚本才会调用Start
函数。
4.3.5 Update()函数
Update
,帧更新函数,每一帧都会被调用。只有物体和脚本都处于激活状态下才会被调用。调用顺序在Start
函数之后,之后就每帧调用Update
函数,帧与帧之间的时间间隔不是固定的,收到运行时的cpu
、GPU
等工作负荷的影响,我们常说的卡顿,就是运行负荷太高了,此时帧与帧之间的间隔时间就会变大,出现掉帧的情况。
4.3.6 LateUpdate()函数
LateUpdate
,延迟更新函数,LateUpdate
函数会在Update
函数结束之后被调用,LateUpdate
常用为第三人称视角相机跟随,比如在Update
中处理主角的移动,在LateUpdate
中处理摄像机跟随移动。
4.3.7 FixedUpdate()函数
FixedUpdate
,固定更新函数,在游戏运行的过程中,每一帧的处理时间是不固定的,当我们需要固定间隔时间执行某些代码时,就会用到FixedUpdate
函数。FixedUpdate
的调用时间间隔,可以在编辑器中设置。点击菜单Edit - Project Settings...
,然后点击Time
,可以看到有个Fixed timestep
,它就是FixedUpdate
的调用时间间隔,默认是0.02秒
。
4.3.8 OnGUI()函数
OnGUI
,绘制界面函数。一般作为测试功能使用,如创建测试按钮。
例:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehavIoUrScript : MonoBehavIoUr
{
private void OnGUI()
{
if(GUILayout.Button("我是一个测试按钮"))
{
Debug.Log("我被点击啦");
}
}
}
4.3.9 Ondisable()函数
Ondisable
,当脚本被禁用时调用。在Inspector
窗口中,脚本前面的勾选框,就可以激活和禁用脚本。它与OnEnable
函数是对应的。
4.3.10 OnDestroy()函数
OnDestroy
,当脚本所在的物体被销毁或者脚本本身被移除的时候,OnDestroy
函数被调用。
4.4 测试生命周期
皮皮:“感慨呀,每一个脚本都是一条生命呀。”
我:“人生苦短,莫要虚度年华,来,跟着我跑一遍测试脚本,我们来跑一下脚本组件的生命周期测试。”
示例代码:
using UnityEngine;
public class NewBehavIoUrScript : MonoBehavIoUr
{
private bool hasPrintUpdate = false;
private bool hasPrintLateUpdate = false;
private bool hasPrintFixedUpdate = false;
private bool hasPrintGUI = false;
void Awake()
{
Debug.Log("---Awake---");
}
void OnEnable()
{
Debug.Log("---OnEnable---");
}
void Start()
{
Debug.Log("---Start---");
}
void FixedUpdate()
{
if (!hasPrintFixedUpdate)
{
hasPrintFixedUpdate = true;
Debug.Log("---FixedUpdate---");
}
}
void Update()
{
if (!hasPrintUpdate)
{
hasPrintUpdate = true;
Debug.Log("---Update---");
}
}
void LateUpdate()
{
if (!hasPrintLateUpdate)
{
hasPrintLateUpdate = true;
Debug.Log("---LateUpdate---");
}
}
void OnGUI()
{
if (!hasPrintGUI)
{
hasPrintGUI = true;
Debug.Log("---OnGUI---");
}
}
void Ondisable()
{
Debug.Log("---Ondisable---");
}
void OnDestroy()
{
Debug.Log("---OnDestroy---");
}
}
运行结果如下:
---Awake---
---OnEnable---
---Start---
---FixedUpdate---
---Update---
---LateUpdate---
---OnGUI---
---Ondisable---
---OnDestroy---
《学Unity的猫》——第五章:规范Unity的工程目录结构
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。