Unityで「三人称視点操作(TPP)」を作ってみよう！

# はじめに
受講生からもよく「三人称視点のゲーム作りたい！」という声を聞くので、この記事を読んだら、一発で実装できる記事を書こうと思います！(Unityの使い方は知ってる前提で書くよ～)


Unityには、便利な「Cinemachine」ってのがありますが、折角なら自分でプログラミング書いた方が楽しいだろ！！ということで、使いません🙅‍♂️
気軽に実装って人は、「Cinemachine」使ってみるのもありかと！


今回、qiitaの記事を初めて書くので、みにくかったら、ごめぇんねっ☆

# 完成
完成はこんな感じです！

![ezgif.com-video-to-gif-converted.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/910650aa-f43e-5faa-d5d8-bad6c3a1874a.gif)


今回の仕様は、
- WASD入力でのカメラを基点とした移動
- マウス入力で、プレイヤーを中心としてカメラが上下左右に回転する

といったものになっております。



__それでは作っていくぅぅぅぅぅぅぅ！__


# 1.事前準備
まず、なんでもOKなんですけど、
- 動かすキャラクター(前後左右が分かるもの)
- ステージ

の2点を用意しましょう

キャラクターはcapsuleにcubeを付けたり、ステージはcube引き伸ばしたりして適当に作れればOK！！


これは、capsuleとかcubeとかを組み合わせた"**ポチタ**"です。
今回は、こいつを動かしていきます！
![image (55).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/37e5aa2c-f631-f69c-37f9-96aecff04c33.png)



# 2.プレイヤーの設定をする
プレイヤーにRigidBodyという物理演算をしてくれるコンポネントを追加しましょう！
![image (56).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/491aebdf-69de-7d73-a1f1-9b716785b798.png)

画像の「Add Component」ってところ押してください。

![image (57).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/53ecbe98-4d01-d229-3517-9be0f0d1c77a.png)

検索欄に「ri」と打ったら、「Rigidbody」がでてくるので、左クリックで追加してあげてください。

![image (58).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/4e5b9ad0-384e-f56e-cdd1-cc691741d663.png)

追加ができたら
- Interpolate　→　Interpolateに変更
- Freeze Rotarion　→　x,y,zにチェックを入れる

と変更してください

# 3.カメラを移動させとく

最初のカメラの位置を常に保った状態で、カメラが動くので、良い感じの位置にしときましょう。

位置はお好みでOKですが、まあ、大体こんくらいかなって画像載せときます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/187666cd-2813-fcfe-5a5e-951e874f83cc.png)


# 4.メインカメラの制御プログラム作る

まず、「CameraController」って名前でScriptを作成します。

作成できたら、開いて以下のプログラムをコピペ！！

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    [SerializeField] GameObject player;

    Vector3 currentPos;//現在のカメラ位置
    Vector3 pastPos;//過去のカメラ位置

    Vector3 diff;//移動距離

    private void Start()
    {
        //最初のプレイヤーの位置の取得
        pastPos = player.transform.position;
    }
    void Update()
    {
        //------カメラの移動------

        //プレイヤーの現在地の取得
        currentPos = player.transform.position;

        diff = currentPos - pastPos;

        transform.position = Vector3.Lerp(transform.position, transform.position + diff, 1.0f);//カメラをプレイヤーの移動差分だけうごかすよ

        pastPos = currentPos;


        //------カメラの回転------

        // マウスの移動量を取得
        float mx = Input.GetAxis("Mouse X");
        float my = Input.GetAxis("Mouse Y");

        // X方向に一定量移動していれば横回転
        if (Mathf.Abs(mx) > 0.01f)
        {
            // 回転軸はワールド座標のY軸
            transform.RotateAround(player.transform.position, Vector3.up, mx);
        }

        // Y方向に一定量移動していれば縦回転
        if (Mathf.Abs(my) > 0.01f)
        {
            // 回転軸はカメラ自身のX軸
            transform.RotateAround(player.transform.position, transform.right, -my);
        }
    }
}
```

ちょい解説。
やってることは2つ！
- カメラ自身の移動
- プレイヤーを中心としたカメラの回転


『カメラ自身の移動』では、
プレイヤーが移動する前の位置、後の位置の差を取って、その分だけカメラも移動させてるだけです！

『カメラの回転』では、
マウスのX方向の入力、Y方向の入力を取得してきて、入力された分だけプレイヤーを中心に回転させてるだけです！
オブジェクトを中心に回転は、transform.RotateAround()で実装できます。

---------------------------

コピペができたら、「MainCamera」にアタッチ！！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/5d9de1f5-3cbe-be73-ab36-fc8b347bc6ff.png)


そして、Inspecterのほうで、「Player」のオブジェクトをドラック＆ドロップで入れてあげましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/a2b0d158-755d-05e6-1d68-c0137acdbc41.png)


# 5.プレイヤーの制御プログラム作る

まず、「PlayerController」って名前でScriptを作成します。

作成できたら、開いて以下のプログラムをコピペ！！

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    float moveSpeedIn;//プレイヤーの移動速度を入力


    Rigidbody playerRb;//プレイヤーのRigidbody

    Vector3 moveSpeed;//プレイヤーの移動速度

    Vector3 currentPos;//プレイヤーの現在の位置
    Vector3 pastPos;//プレイヤーの過去の位置

    Vector3 delta;//プレイヤーの移動量

    Quaternion playerRot;//プレイヤーの進行方向を向くクォータニオン

    float currentAngularVelocity;//現在の回転各速度

    [SerializeField]
    float maxAngularVelocity = Mathf.Infinity;//最大の回転角速度[deg/s]

    [SerializeField]
    float smoothTime = 0.1f;//進行方向にかかるおおよその時間[s]

    float diffAngle;//現在の向きと進行方向の角度

    float rotAngle;//現在の回転する角度

    Quaternion nextRot;//どんくらい回転するか
    void Start()
    {
        playerRb = GetComponent<Rigidbody>();

        pastPos = transform.position;
    }

    void Update()
    {
        //------プレイヤーの移動------

        //カメラに対して前を取得
        Vector3 cameraForward = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized;
        //カメラに対して右を取得
        Vector3 cameraRight = Vector3.Scale(Camera.main.transform.right, new Vector3(1, 0, 1)).normalized;

        //moveVelocityを0で初期化する
        moveSpeed = Vector3.zero;

        //移動入力
        if (Input.GetKey(KeyCode.W))
        {
            moveSpeed = moveSpeedIn * cameraForward;
        }

        if (Input.GetKey(KeyCode.A))
        {
            moveSpeed = -moveSpeedIn * cameraRight;
        }

        if (Input.GetKey(KeyCode.S))
        {
            moveSpeed = -moveSpeedIn * cameraForward;
        }

        if (Input.GetKey(KeyCode.D))
        {
            moveSpeed = moveSpeedIn * cameraRight;
        }

        //Moveメソッドで、力加えてもらう
        Move();

        //慣性を消す
        if(Input.GetKeyUp(KeyCode.W) || Input.GetKeyUp(KeyCode.A) || Input.GetKeyUp(KeyCode.S) || Input.GetKeyUp(KeyCode.D))
        {
            //playerRb.velocity = Vector3.zero;
           // playerRb.angularVelocity = Vector3.zero;
        }

        //------プレイヤーの回転------

        //現在の位置
        currentPos = transform.position;

        //移動量計算
        delta = currentPos - pastPos;
        delta.y = 0;

        //過去の位置の更新
        pastPos = currentPos;

        if (delta == Vector3.zero)
            return;

        playerRot = Quaternion.LookRotation(delta, Vector3.up);

        diffAngle = Vector3.Angle(transform.forward, delta);

        //Vector3.SmoothDampはVector3型の値を徐々に変化させる
        //Vector3.SmoothDamp (現在地, 目的地, ref 現在の速度, 遷移時間, 最高速度);
        rotAngle = Mathf.SmoothDampAngle(0, diffAngle, ref currentAngularVelocity, smoothTime, maxAngularVelocity);

        nextRot = Quaternion.RotateTowards(transform.rotation, playerRot, rotAngle);

        transform.rotation = nextRot;

    }

    /// <summary>
    /// 移動方向に力を加える
    /// </summary>
    private void Move()
    {
        //playerRb.AddForce(moveSpeed, ForceMode.Force);

        playerRb.velocity = moveSpeed;
    }
}

```
ちょい解説。
やってることは2つ！
- カメラを基点とした、プレイヤーの移動
- 移動方向にプレイヤーを回転

『プレイヤーの移動』では、
「MainCamera」の正面、右方向を取得することによって、カメラに対して前後左右に進むように実装してます

『プレイヤーの回転』では、
プレイヤーの進む前の位置、進んだ後の位置から、移動量を計算し、Quaternion.LookRotation()で回転をさせています。
後半にわちゃわちゃ書いてるのは、滑らかに回転させるためのものです。

----

コピペができたら、「自分が作ったプレイヤー」にアタッチ！！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/e4ae0c29-4a61-d6dc-9b79-8a41e7ed319b.png)

Inspecterから、数値を変更できるようにしてるのですが、好きに触ってみてください。
個人的に画像の設定くらいがちょうどよかったです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3555729/19f31688-4ce7-ee89-e179-f4c1abafa729.png)


# おわり！！
これで、三人称視点移動のプレイヤーが完成です！
ただ、ゲームの仕様によっては、移動方法の実装を変える必要があるので、そこはがんばってね♡