前篇链接:Unity之C#学习笔记(4):Unity中旋转的表示——四元数 Quaternion(上)

在上节中,我们学习了代表无旋转的静态量Quaternion.identity和将欧拉角转换为四元数的方法Quaternion.Euler,它们在初始化中很常用。在这节,我们讲解另外两个方法Quaternion.LookRotation和Quaternion.Slerp,它们能为你实现实时追踪目标的效果。

假设我们现在要设计一个能自动追踪玩家的敌方AI。除了在位置上能向玩家的方向移动,我们还希望这个AI能一直“面向”玩家,便于后续添加一些如发射子弹之类的功能。想搞清楚物体具体的旋转参数是相当麻烦的,但Unity为我们提供了一个非常好用的方法Quaternion.LookRotation:

unity单指旋转物体 unity旋转物体代码_System


我们暂时不用管第二个参数。第一个参数是一个Vector3变量,它的意思就是“看向”的方向。这个方向其实就是目标和自身position相减所得的值。调用这个方法,我们不用管物体的旋转参数怎么计算,只需要传一个目标减去自身的Vector3值即可。让我们实际试一试。

创建一个Player和一个Enemy,然后创建一个Aim脚本,挂在Enemy上,并将_playerTransform引用到Player:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Aim : MonoBehaviour
{
    [SerializeField]
    private Transform _playerTransform;
    // Update is called once per frame
    void Update()
    {
        Vector3 direction = _playerTransform.position - transform.position;
        transform.rotation = Quaternion.LookRotation(direction);
        Debug.DrawRay(transform.position, direction, Color.green);
    }
}

Tips 1: SerializeField的用法我们在第(2)节中已经讲过了
Tips 2: 你会发现这里我们声明的是一个Transform变量,而非一个GameObject。当我们只需要用到物体中的部分属性时,这样做是合理且清晰的
Tips 3: Debug.DrawRay(Vector3 start, Vector3 dir)可以画出一条起始于start,指向dir的射线,第三个可选参数可以指定射线的颜色。这种方法在检查旋转相关的实际效果时非常实用。

完成后运行游戏,在编辑窗口拖动Player,你会看到Enemy始终面向着Player的方向。

unity单指旋转物体 unity旋转物体代码_System_02


由于上面的代码写在了Update函数中,所以在每一帧,Enemy的旋转都会被调整为面向Player的方向,效果实际上就是Enemy“吸住”了玩家。即使Player运动得很快,每秒几十上百次的刷新也能让Enemy随时锁定玩家。

但有些时候,我们想要的效果是Enemy平滑地从原方向转到Player所在的方向。这就要用到接下来这个函数:Quaternion.slerp(Quaternion a, Quaternion b, float t)。Slerp是Spherical Linear Interpolation的简写,即球面线性插值。“线性插值”,就是给定a和b两个值和一个系数t,计算出当t为0到1之间的某个值时对应的a到b的中间值。其中a和b可以是float,可以是Vector3,也可以是这里的Quaternion。这里,它可以计算出从旋转a到旋转b的平滑效果。让我们来实际试一试。

还是之前的物体不变,我们对Aim脚本进行修改:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Aim : MonoBehaviour
{
    [SerializeField]
    private Transform _playerTransform;
    public float rotateSpeed;
    // Update is called once per frame
    void Update()
    {
        Vector3 direction = _playerTransform.position - transform.position;
        Quaternion targetRotation = Quaternion.LookRotation(direction);
        transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation
        									, Time.deltaTime * rotateSpeed);
        Debug.DrawRay(transform.position, direction, Color.green);
    }
}

旋转起点a显然是物体当前的旋转,而目标旋转b其实就是刚才通过LookRotation通过目标和自身的方位差算出的新旋转。时间t是所用时间,我们可以用老朋友Time.deltaTime乘以一个自由控制的系数rotateSpeed获得。在控制面板上将rotateSpeed调成2试试:

unity单指旋转物体 unity旋转物体代码_Time_03


可以看到,Enemy的追踪已经变得平滑了。你可以调试rotateSpeed,找到最合适的取值。

在这几节中,我们主要讲解了一些与Unity系统有关的编程内容,让大家快速熟悉Unity的基础编程。从下节开始,我们将回归C#核心。这个系列不算零基础入门内容,我们将跳过基本变量类型、程序的流程控制(if, switch, for, while)以及面向对象的基本概念,但总体难度是比较基础的(毕竟博主也是初学者)。后续的内容有

数组(Array)
类的继承(Inheritance)
虚函数,方法隐藏和方法重写(Virtual Methods, New and Override)
抽象类和抽象函数(Abstract Classes and Methods)
接口和多态(Interfaces and Polymorphism)
静态类型(Static)
属性(Properties)
列表(Lists)
枚举(Enums)
字典(Dictionary)
委托和事件(Delegates and Events)
单例模式(Singleton)
对象池(Object Pools)
命令模式(Command Patterns)

在每一节中,我们也会展示一些例子便于大家理解和上手操作。我们下节数组(Array)见。