【Unity学习笔记】使用Cinemachine快速实现具有锁定敌人功能的第三人称相机方案

#Unity官方的Cinemachine工具，功能非常强大并且方便使用，可以用其轻松实现各种相机的功能。本文介绍一种简单快速实现锁定敌人功能的第三人相机方案。效果如下图所示
![1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/0a274c13-c015-50e9-4f2d-870359037f86.gif)

# 实现步骤介绍
### 1.准备Unity2018.3以上的版本，新建一个空白的3d项目，在Pakage Manager中下载安装Cinemachine（本文使用的是Unity2019.3.0f3 + Cinemachine 2.3.4）
![コメント 2019-12-16 221943.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/49890923-c0e6-a797-39ff-4011426c7c4c.png)

### 2.新建一个第三人称虚拟相机Cinemachine FreeLook，一个名为Follow的空物体，一个名为LookAt的空物体。为使Hierarchy保持整洁，新建一个名为CameraRoot的空物体，将上述三者和Main Camera都纳入其下。
![コメント 2019-12-16 222236.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/70eb931a-5bdf-9637-6b3b-a05cf76ffa32.png) ![コメント 2019-12-15 224719.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/97b562a5-0178-e2e0-91a0-1848d114ae70.png)

### 3.设置CM FreeLook的基本属性
![コメント 2019-12-15 231404.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/44bd4a49-10a5-3dcf-824d-2da13dd86e8f.png)
将CM FreeLook的Follow设置为Follow空物体，LookAt设置为LookAt空物体（这里为何不将Follow直接设置为玩家本身，不将LookAt直接设置为要锁定的敌人呢？放到后面具体实现的时候来说明）。其余项目可以保持默认值。

### 4.设置CM FreeLook两条轴的属性
![コメント 2019-12-15 233053.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/321249a1-7aad-b227-84bb-fabd30d4a000.png) ![コメント 2019-12-15 233120.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/49c7c2db-48a8-97d4-6ced-264e7d6a2e1b.png) ![コメント 2019-12-16 233542.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/4b7e8cd3-9679-2110-6e1d-0c10a8fe6956.png) 

- Y Aixs的Value设置成1，并且始终保持不变。本方案始终使用TopRig的设置，不会用到MiddleRig和BottomRig。
- 将X Aixs和Y Aixs的InputAxisName中的MouseX和MouseY去掉。本方案不使用鼠标手动操作XY轴的运动。
- 将Orbits下的Binding Mode设置为Lock To Target With World Up。无论Follow对象如何旋转，围绕Follow的旋转轴始终朝向世界坐标的Up方向，即旋转轨迹面始终平行于世界坐标的水平平面。

### 5.设置CM FreeLook的运动属性
![コメント 2019-12-16 234942.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/d46a62f9-a6be-609a-b64d-6e607245a33c.png)

- TopRig中的Body属性下的XYZ Damping是在三个轴的方向上虚拟相机位置跟随Follow物体的缓冲值，可根据实际需求设置。0为不缓冲，保持相机旋转轴中心的位置坐标和Follow对象始终保持一致。本方案不需要位置跟随上的缓冲，因此均设置为0。
- YawDamping是相机Yaw轴（可以简单理解成y轴方向）旋转值跟随Follow的旋转值的缓冲值，0表示不缓冲，可根据实际需求设置。本方案中将其设置为0.2。

![コメント 2019-12-17 235925.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/416c532b-2c8d-4ae4-f8fd-fa4098bba9d1.png)

- Aim属性下的各项属性基本保持默认即可。本方案的设置如图所示。


### 6.建立一个Player物体和若干个Enemy物体。
![コメント 2019-12-17 220520.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/fcdecc9f-e0de-7c8e-632e-4e6bc9ed0e1d.png)
物体的形状可以随意设定，本文为了方便就设置为胶囊形，并添加一个小球放在Z轴正方向表示物体的前方。
![コメント 2019-12-17 221347.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/0ce194fd-afe1-69b7-48f0-65d48ca02fd8.png)

### 7.编写相机控制脚本
新建一个CameraController用于控制相机跟随。将其添加到CameraRoot上，并设置好各项属性。
![コメント 2019-12-17 220325.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/59a93446-498a-6611-3b75-76df2b16177d.png)

```csharp:CameraController.cs
using UnityEngine;
using Cinemachine;
public class CameraController : MonoBehaviour
{
    [SerializeField]
    private CinemachineFreeLook CmFreeLook;
    [SerializeField]
    private Transform follow;
    [SerializeField]
    private Transform lookAt;
    [SerializeField]
    private Transform player;
    [SerializeField]
    private Transform[] enemies;
    [SerializeField]
    private AimMark aimMark;

    // 当前锁定的敌人的Index
    private int lockedEnemyIndex;

    private void Start()
    {
        // 给FreeLook虚拟相机设置Follow和LookAt的物体
        CmFreeLook.Follow = follow;
        CmFreeLook.LookAt = lookAt;

        // 设置锁定标记的跟随对象
        aimMark.SetAimTarget(lookAt);
    }

    private void Update()
    {
        // 按下空格时切换锁定的敌人
        if(Input.GetKeyDown(KeyCode.Space))
        {
            lockedEnemyIndex = (lockedEnemyIndex + 1) % enemies.Length;
            aimMark.PlayAimAnimation();
        }

        // 使lookAt的位置和旋转都和锁定的目标保持一致
        lookAt.position = enemies[lockedEnemyIndex].position;
        lookAt.rotation = enemies[lockedEnemyIndex].rotation;

        // 使follow的位置和玩家一致
        follow.position = player.position;
        // 使follow的z轴正方向指向lookAt
        follow.LookAt(lookAt);
    }
}
```
其中的```AimMark```是锁定的时候出现的UI，和相机功能无关，这里可以先不看。

这里将LookAt空物体的位置和旋转始终保持和锁定中的敌人一致，Follow则是位置保持和Player一致同时使Z轴正方向指向LookAt。
上文提到，为何不直接将CM FreeLook的Follow和LookAt直接设置为Player和锁定中Enemy，而是需要用两个间接物体呢？
 
#### 建立一个Follow空物体，位置跟随Player，并看向LookAt物体的目的是为了使相机，Player，Enemy处于一条直线上
![2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265897/19757200-5bf7-6664-a0e0-65f55e6c258b.gif)

Orbits中的Binding Mode设置为Lock To Tartget后，相机的旋转轴的位置跟随Follow对象的位置，旋转值也会Follow对象的旋转值。
而Lock To Target的三种变体，相机的旋转轴的位置都是跟随Follow对象的位置，但旋转值的跟随方式则有所不同

```
Lock To Tartget：                旋转值跟随Follow对象的旋转值
Lock To Target On Assign：       Start时或Follow对象更变时，旋转值设置为Follow对象的旋转值一次，之后则不再变化。
Lock To Target With World Up：   只有Yaw轴的旋转值跟随Follow对象的旋转值。
Lock To Target No Roll：         Pitch轴和Yaw轴跟随Follow对象的旋转值，Roll轴不跟随
```
#### 建立一个LookAt空物体的目的，是为了让切换锁定敌人时，镜头能平滑转向。
直接更换CM FreeLook的LookAt对象的话，镜头会瞬间指向对象，不会对其进行补间。而让LookAt对象始终为一个空物体，切换锁定时让空物体的位置更变为锁定敌人的位置，相机的转向就会进行补间。

### 8.要点总结
1. 使用Cinemachine中的FreeLook相机，Follow和LookAt属性分别设置为Follow空物体和LookAt空物体。
2. 设置Orbits下的Binding Mode为Lock To Target With World Up。
3. 编写脚本，使Follow空物体位置跟随Player，旋转指向LookAt空物体。LookAt空物体位置和旋转都跟随要锁定的Enemy。

以上就是使用Cinemachine快速实现具有锁定敌人功能的第三人称相机方案。