UnityでTPS視点のカメラを実装する！

<h1>はじめに

みなさんこんにちは！
今回はUnityでTPS視点のカメラを制作してみました！

イメージはこんな感じです。
![無題の動画 ‐ Clipchampで作成 (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290562/387fb7e9-36ca-91cf-73ef-bed12e65f720.gif)

マウスの動かした方向をキャラクターの正面にして、移動する感じにしました。
ARKに近い感じですかね。
<h1>1.準備</h1>

1.キャラクターオブジェクト
2.キャラクターとカメラのスクリプト（今回私はMan.csとCameraScript.csの2つ用意しました。）
3.カメラの中心位置とする空のオブジェクト（キャラクターの子要素にする）
（キャラクター・アニメーションの導入方法等は割愛します）

<h1>2.コード</h1>
まずキャラクターのスクリプトから。

```C#:Man.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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

    public GameObject Cam;
    private Animator animator; // キャラクターオブジェクトのAnimator
    public RuntimeAnimatorController walking;
    public RuntimeAnimatorController running;
    public RuntimeAnimatorController standing;

    public float moveSpeed = 5.0f; // キャラクターの移動速度

    public bool damaged;

    private void Start()
    {
        animator = GetComponent<Animator>();
        damaged = false;
    }

    void Screen_movement(float mx){
        // X方向に一定量移動していれば横回転
        //0.0000001fは滑らかさ
        if (Mathf.Abs(mx) > 0.0000001f)
        {
            mx = mx * 5;

            // 回転軸はワールド座標のY軸
            player.transform.RotateAround(player.transform.position, Vector3.up, mx);
        }
    }

    void Update()
    {   
        float mx = Input.GetAxis("Mouse X");
        Screen_movement(mx);

        if (Input.GetKey(KeyCode.W))
        {
            // "W"キーが押されたときの処理をここに記述
            print("歩くぞお");
            animator.runtimeAnimatorController = walking;                   

            //プレイヤーの正面に向かって移動する
            transform.position += transform.forward * moveSpeed * Time.deltaTime;

        }
        else if(Input.GetKeyUp(KeyCode.W))
        {
            animator.runtimeAnimatorController = standing;
        }
    }
}
```
Screen_movement()でキャラクターの正面をマウスに依存して移動させています。
このプログラムは下のブログを参考にさせて頂きました！

https://tech.pjin.jp/blog/2022/09/26/unity-move-camera

Wキーを押すと、その時にキャラクターの向いている方向へ進みます。
```C#
if (Input.GetKey(KeyCode.W))
{
    // "W"キーが押されたときの処理をここに記述
    animator.runtimeAnimatorController = walking;                   

    //プレイヤーの正面に向かって移動する
    transform.position += transform.forward * moveSpeed * Time.deltaTime;

}
```
Wキーを離すと立っているアニメーションに切り替わります。
```C#
if (Input.GetKeyUp(KeyCode.W))
{
    animator.runtimeAnimatorController = standing;
}
```



キャラクターのスクリプトはこんな感じです。
これでマウスを向けた方向がキャラクターの正面となり、Wキーを押せばその方向に進みます。

次はカメラのクリプトです。

```C#:CameraScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraScript : MonoBehaviour
{
    [SerializeField] GameObject player;
    public float distance; // カメラとプレイヤー間の距離
    public float height; // カメラの高さ
    public float smoothSpeed; // カメラの回転速度
	
    void Update()
    {
        // マウスの移動量を取得
        float my = Input.GetAxis("Mouse Y");
        float mx = Input.GetAxis("Mouse X");
        
         // X方向に一定量移動していれば横回転
        //0.0000001fは滑らかさ
        if (Mathf.Abs(mx) > 0.0000001f)
        {
            mx = mx * 5;
            
            // 回転軸はワールド座標のY軸
            transform.RotateAround(player.transform.position, Vector3.up, mx);
            
        }  

        // Y方向に一定量移動していれば縦回転
        if (Mathf.Abs(my) > 0.0000001f)
        {
            //高さの制限
            if ((height - my) < -2 || (height - my) > 4)
            {
                my = 0;
            }
            height -= my / 10;            
        }  
    }

    void LateUpdate()
    {
        // プレイヤーの中心位置を計算
        Vector3 playerCenter = player.transform.position + Vector3.up * height;

        // プレイヤーの後ろに位置するターゲット位置を計算
        Vector3 targetPosition = playerCenter - player.transform.forward * distance;

        
        // カメラの位置を滑らかに更新
        Vector3 smoothedPosition = Vector3.Lerp(transform.position, targetPosition, smoothSpeed * Time.deltaTime);
        transform.position = smoothedPosition;

        // カメラは常にプレイヤーを向く
        transform.LookAt(player.transform);
    }
}
```
プレイヤーの中心位置と、その位置とカメラの距離を設定し、その距離を常に保つようにカメラの位置を更新します。

```C#
void LateUpdate()
{
    // プレイヤーの中心位置を計算
    Vector3 playerCenter = player.transform.position + Vector3.up * height;

    // プレイヤーの後ろに位置するターゲット位置を計算
    Vector3 targetPosition = playerCenter - player.transform.forward * distance;

        
    // カメラの位置を滑らかに更新
    Vector3 smoothedPosition = Vector3.Lerp(transform.position, targetPosition, smoothSpeed * Time.deltaTime);
    transform.position = smoothedPosition;

    // カメラは常にプレイヤーを向く
    transform.LookAt(player.transform);
}
```
この時、中心位置を本当にプレイヤーオブジェクトの中心にしてしまうと、カメラが下過ぎてちょっと見づらいです。そこでプレイヤーオブジェクトよりも高い位置に空のオブジェクトを設置し、それをplayerに設定すると、いい感じになります。
![スクリーンショット (19).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290562/906094f6-e252-1d92-a9c0-cba1e2b062f9.png)

Y方向の視点移動は制限がないと一回転してしまうので、一定の高さで制限します。
```C#
//高さの制限
if ((height - my) < -2 || (height - my) > 4)
{
    my = 0;
}
```
後は、[SerializeField]とかを設定すれば同じ挙動になるかと思います！

<h1>3.めちゃくちゃ悩んだけど実は単純な問題だった事

（上のコードでは解決済みです！）
実はこの実装をする際、とある事に悩んでいました。
![無題の動画 ‐ Clipchampで作成.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290562/d4c4f419-9bc2-1ca2-b789-15611aae9759.gif)
分かりづらいかもしれませんが、マウスを移動していないのに画面が左右に揺れています。
これはプレイヤーに設定されたアニメーションによってプレイヤーオブジェクトの向きが若干変わってしまっているからです。
これを何とかするために色々調べましたが、めっちゃ単純でした。
![3Dゲーム - Player_Move - Windows, Mac, Linux - Unity 2021.3.15f1 Personal_ _DX11_ 2023_10_25 21_22_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290562/103c39ae-023c-8d34-ef28-7b854adff9dd.png)
<h3>このチェックを外すだけ!!<h3>

ChatGPT君が教えてくれました( ´∀｀ )
エラーの原因が全角スペースだった時ぐらいの衝撃でした。

これを解除することで、アニメーションの動きをプレイヤーオブジェクトの位置や向きに反映させないようにできます。
逆に、これにチェックを付ければ、アニメーションに動き通りにオブジェクトが位置や向きを変えるようになります。

<h1>おわりに

以上、UnityでTPS視点のカメラを実装しました。
コードのアニメーションの所ですが、実はもっと簡単なやり方があるっぽいです。
```C#
if (Input.GetKeyUp(KeyCode.W))
{
    //わざわざstandingのAnimatorControllerを作らなくっても良かったらしい。勉強不足でごめんなさい・・・。
    animator.runtimeAnimatorController = standing;
}
```
ちなみに、X(Twitter)始めたので、良かったらフォローお願いします！
改善ポイント・質問等、よろしくお願いします！

X(Twitter):https://twitter.com/ITTON2001
Github:https://github.com/ITTON2001