• 事件函数
    • 定期更新事件
    • 初始化事件
    • GUI 事件
    • 物理事件

    事件函数

    传统编程的思路是,在一个循环结构中连续运行代码,直到完成任务。Unity 中的脚本则不同,Unity 通过间歇地调用脚本中声明的特定函数来控制脚本。一旦某个函数执行完成,控制权被交会 Unity。这些函数被成为事件函数,因为 Unity 通过激活它们来响应游戏过程中发生的各种事件。Unity 使用一套命名方案来唯一标识特定事件对应(调用)的函数。例如,你已经看到的 Update 函数(每桢更新之前被调用)和 Start 函数(游戏对象的第一桢更新前被调用)。Unity 提供了许多事件函数,可以在 MonoBehaviour 类的脚本参考页找到完整列表和用法的详细信息。下面是一些最常用和最重要的事件。

    定期更新事件

    一个游戏更像一段动画,因为动画桢是实时生成的。游戏编程中的一个关键概念是,在每一桢渲染之前,更改游戏中对象的位置、状态和行为。在 Unity 中,Update 函数是放置这类代码的主要地方。Update 在桢渲染和动画计算之前被调用。

    1. void Update() {
    2. float distance = speed * Time.deltaTime * Input.GetAxis("Horizontal");
    3. transform.Translate(Vector3.right * distance);
    4. }

    物理引擎也以离散的时间步长进行更新,类似于桢渲染。每次物理更新之前,函数 FixedUpdate 被调用。因为物理更新和桢更新以不同的频率发生,所以,如果你把物理代码放入 FixedUpdate 而不是 Update,可以获得更准确的结果。

    1. void FixedUpdate() {
    2. Vector3 force = transform.forward * driveForce * Input.GetAxis("Vertical");
    3. rigidbody.AddForce(force);
    4. }

    当场景中所有对象的 Update 和 FixedUpdate 函数调用完,并且所有动画计算完成之后,执行一些额外的更改可能会很有用。一个例子是摄像机跟踪某个目标对象时,对摄像机方位的调整必须发生在目标对象移动之后。另一个例子是脚本代码想要覆盖动画效果(例如,让角色头部面向场景中的某个目标)。LateUpdate 函数可以用于这类情况。

    译注:既然 Update 和 FixedUpdate 的执行频率不同,那么怎么保证 LateUpdate 的顺序呢?看了 [事件函数执行顺序] 里的流程图还是不确定。

    1. void LateUpdate() {
    2. Camera.main.transform.LookAt(target.transform);
    3. }

    初始化事件

    在游戏期间发生任何更新之前,能够调用初始化代码通常很有用。在对象的第一桢或第一次物理更新之前,Start 函数被调用。在场景加载时,为场景中的每个对象调用 Awake 函数。请注意,尽管各种对象的 Start 和 Awake 函数以任意顺序被调用,但是在第一个 Start 函数被调用之前,所有的 Awake 函数都应该已经完成。这意味着,Start 函数中的代码可以使用之前 Awake 阶段执行的初始化(赋值)。

    GUI 事件

    Unity 提供了一套 GUI 控件渲染系统,用于控制场景中的主要操作和响应对控件的点击。这种代码与正常的桢更新有些不同,因为应该放入 OnGUI 函数,该函数会被周期性调用。

    1. void OnGUI() {
    2. GUI.Label(labelRect, "Game Over");
    3. }

    你还可以检测场景中显示的游戏对象上发生的鼠标事件。这可以用于武器瞄准,或显示当前鼠标指针所指角色的信息。脚本使用一组 OnMouseXXX 事件函数(例如 OnMouseOver、OnMouseDown)响应用户鼠标的行为。例如,如果按下鼠标按钮,并且指针位于一个特定对象之上时,那么该对象上脚本的 OnMouseDown 函数将被调用(如果存在的话)。

    物理事件

    物理引擎将通过调用脚本的事件函数,来报告与对象的碰撞。当建立、保持和断开连接时(碰撞时两个物体连接在一起),OnCollisionEnter、OnCollisionStay 和 OnCollisionExit 函数将被调用。如果对象的碰撞器被配置为触发器(例如,简单地检测某个对象是否进入了碰撞器,而不是响应物理行为),相应的 OnTriggerEnter、OnTriggerStay 和 OnTriggerExit 函数将被调用。在物理更新期间,如果检测到多个连接,那么这些函数可能会连续多次被调用,因此,会传给函数一个含有碰撞信息的参数(位置、相关对象的标识等)。

    1. void OnCollisionEnter(otherObj: Collision) {
    2. if (otherObj.tag == "Arrow") {
    3. ApplyDamage(10);
    4. }
    5. }