一、Unity关键类继承关系
(1)、继承关系如下图:
用户定义的每一个类在引擎中都属于一个组件,引擎中每个Component类定义了GameObject类型的对象,用于申明当前组件所属的游戏对象。
二、用户实现的接口与引擎调用关系
Unity3D引擎为游戏开发者提供了脚本语言(例如:C#、Javascript)编写代码,基于Unity引擎的开发者通用可通过如下如下几种方式实现接口的调用:
- 基于引擎帧更新机制调用用户实现的接口。
- 基于引擎的消息事件响应机制调用用户接口。
- 引擎的延迟调用机制。
- 其他直接调用方式。
(1)、基于引擎帧更新机制调用用户实现的接口
创建Unity引擎的C#脚本文件,会默认为类生成Start和Update函数,其中Start函数仅在Update函数第一次调用时会被引擎调用,用户通过Start实现初始化操作,类似于类的构造函数。
Unity引擎为用户提供了多种帧更新操作,例如常用的三种更新操作为:Update、FixedUpdate、LateUpdate,三种Update调用机制不相同,三种函数调用机制如下:
Update:引擎会在每帧更新的过程中遍历对象并更新该组件的Update函数。
FixedUpdate:引擎以固定时间调用组件的FixedUpdate函数。
LateUpdate:在Update函数调用之后调用,通过该功能可调整脚本执行的顺序,例如:当无敌在Update里移动时,跟随物体的相机可在LateUpdate里实现镜头移动。
用户定义组件对象默认继承MonoBehaviour,用户实现的每帧更新Update函数于引擎的PlayerLoop函数调用(该函数于Player.cpp实现),其中GetBehaviourManager()是获取管理Update方式每帧更新的调用方法,其中GetBehaviourManager().update()函数及所属类的GetBehaviourManager().update()函数内部由CommonUpdate模版实现。用户实现的FixedUpdate属于固定时间的调用方式,对应在PlayerLoop函数中调用。GetTimeManager().StepFixedTime()函数会判断是否需要处理固定时间的相关操作,例如用户定义的FixedUpdate函数更新逻辑,所有的组件Update调用之后才调用LateUpdate函数。
(2)、基于引擎消息事件的调用机制
Unity引擎提供给用户感知某些消息类事件的感知,例如某游戏的EntityCollider.cs文件中实现了EntityCollider::OntriggerEnter方法。
其中OnTriggerEnter为用户感知碰撞探测相关事件,OnTriggerEnter函数绑定了kEnterTrigger函数,在PhysicsManager::ProcesssRecordedreports函数内部调用,在判断当前MessageID为kEnterTrigger之后,已消息方式调用SendMessage函数让引擎调用用户实现的OnTriggerEnter接口。其中PhysicsManager::ProcesssRecordedreports函数由PhysicsManager::FixedUpdate调用,消息处理机制是引擎以固定时间的方式在PhysicsManager类的FixedUpdate方法中不断调用实现。
(3)、引擎的延迟调用机制
Unity为用户提供了延迟调用某方法,对应通过Invoke和InvokeRepeating 函数实现,其中Invoke和InvokeRepeat接口定义如下:
function Invoke (methodName : string, time : float) : void
function InvokeRepeating (methodName : string, time : float, repeatRate : float) : void
Invoke为延迟调用一次,InvokeRepeating 为延迟调用多次,延迟调用通过引擎的InvokeDelayed函数实现,延迟调用机制较为简单,延迟的时间、重复调用的次数都由用户控制。
(4)、其他机制
其他机制包括用户定义的C#函数之间调用,该类调用方式与C++函数调用方式类似,不在进行详细阐述。
三、Unity整理框架及帧更新机制
Unity引擎将所有重要事件于主线程中实现,如帧更新逻辑、渲染逻辑、碰撞检测逻辑等,Unity引擎初始化及运行帧更新相关执行流程如下图所示:
其中UpdateScene负责更新游戏场景中所有帧更新时间。PlayerLoop函数属于每帧更细都会调用,该函数负责处理游戏中所有组件的Update、及消息事件相应处理。
Unity引擎会以一定间隔时间不断更新游戏场景中所有相关事件,每次需要计算下一次帧更新所需的间隔时间,对应时间计算在MainMessageLoop函数实现,通过Sleep方式将执行权切换给其他线程,防止出现CPU 100%的情况。
四、Unity引擎对象链表组织方式
每个游戏都会通过某种方式存储场景中的所有对象,Unity引擎的场景对象通过GameObjectManager进行管理,GameObjectManager通过STL的List结构存储了游戏场景中所有对象,通过遍历List链表便可获取到游戏场景中所有对象,Unity通过GameObject::FindGameObjectWithTag函数可通过传入对象的标识从而获取到对象指针,函数通过遍历List链表的所有信息查找所需的对象。每个GameObject对象都包含了Tag字段,该字段用于区分不同的对象