【Unity】カメラ移動を制御するスクリプト

## 概要

Unityでデバックとかする時に使ってる移動・回転をするためのカメラのスクリプトです。
ふと忘れたり、スクリプトが色々な所にあったりするので備忘録を兼ねてQiitaに公開したい。

## CameraMover.cs

```c#:CameraMover.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SocialPlatforms;

public class CameraMover : MonoBehaviour
{
    // WASD：前後左右の移動
    // QE：上昇・降下
    // 右ドラッグ：カメラの回転
    // 左ドラッグ：前後左右の移動
    // スペース：カメラ操作の有効・無効の切り替え
    // P：回転を実行時の状態に初期化する

	//カメラの移動量
	[SerializeField, Range(0.1f, 10.0f)]
	private float _positionStep = 2.0f;

    //マウス感度
    [SerializeField, Range(30.0f, 150.0f)]
    private float _mouseSensitive = 90.0f;

    //カメラ操作の有効無効
	private bool _cameraMoveActive = true;
    //カメラのtransform  
    private Transform _camTransform;
    //マウスの始点 
    private Vector3 _startMousePos;
    //カメラ回転の始点情報
    private Vector3 _presentCamRotation;
    private Vector3 _presentCamPos;
    //初期状態 Rotation
    private Quaternion _initialCamRotation;
    //UIメッセージの表示
    private bool _uiMessageActiv;

    void Start ()
	{
		_camTransform = this.gameObject.transform;
		
		//初期回転の保存
		_initialCamRotation = this.gameObject.transform.rotation;
	}
	
	void Update () {
		
		CamControlIsActive(); //カメラ操作の有効無効

        if (_cameraMoveActive)
		{
			ResetCameraRotation(); //回転角度のみリセット
            CameraRotationMouseControl(); //カメラの回転 マウス
            CameraSlideMouseControl(); //カメラの縦横移動 マウス
            CameraPositionKeyControl(); //カメラのローカル移動 キー
        }
	}
	
	//カメラ操作の有効無効
	public void CamControlIsActive()
	{
		if (Input.GetKeyDown(KeyCode.Space))
		{
			_cameraMoveActive = !_cameraMoveActive;

            if (_uiMessageActiv == false)
            {
                StartCoroutine(DisplayUiMessage());
            }            
			Debug.Log("CamControl : " + _cameraMoveActive);
		}
	}
	
	//回転を初期状態にする
	private void ResetCameraRotation()
	{
		if(Input.GetKeyDown(KeyCode.P))
		{
			this.gameObject.transform.rotation = _initialCamRotation;
			Debug.Log("Cam Rotate : " + _initialCamRotation.ToString());	
		}
	}
	
	//カメラの回転 マウス
	private void CameraRotationMouseControl()
	{
		if (Input.GetMouseButtonDown(0))
		{
			_startMousePos = Input.mousePosition;
			_presentCamRotation.x = _camTransform.transform.eulerAngles.x;
			_presentCamRotation.y = _camTransform.transform.eulerAngles.y;
		}
		
		if (Input.GetMouseButton(0))
		{
			//(移動開始座標 - マウスの現在座標) / 解像度 で正規化
			float x = (_startMousePos.x - Input.mousePosition.x) / Screen.width;
			float y = (_startMousePos.y - Input.mousePosition.y) / Screen.height;

			//回転開始角度 ＋ マウスの変化量 * マウス感度
			float eulerX = _presentCamRotation.x + y * _mouseSensitive;
			float eulerY = _presentCamRotation.y + x * _mouseSensitive;

			_camTransform.rotation = Quaternion.Euler(eulerX, eulerY, 0);
		}
	}
	
	//カメラの移動 マウス
	private void CameraSlideMouseControl()
	{
		if (Input.GetMouseButtonDown(1))
		{
			_startMousePos = Input.mousePosition;
			_presentCamPos = _camTransform.position;
		}

		if (Input.GetMouseButton(1))
		{
			//(移動開始座標 - マウスの現在座標) / 解像度 で正規化
			float x = (_startMousePos.x - Input.mousePosition.x) / Screen.width;
			float y = (_startMousePos.y - Input.mousePosition.y) / Screen.height;

            x = x * _positionStep;
            y = y * _positionStep;

            Vector3 velocity = _camTransform.rotation * new Vector3(x, y, 0);
            velocity = velocity + _presentCamPos;
            _camTransform.position = velocity;
		}
	}
		
	//カメラのローカル移動 キー
	private void CameraPositionKeyControl()
	{
        Vector3 campos = _camTransform.position;
		
		if (Input.GetKey(KeyCode.D)) { campos += _camTransform.right * Time.deltaTime * _positionStep; }
		if (Input.GetKey(KeyCode.A)) { campos -= _camTransform.right * Time.deltaTime * _positionStep; }
		if (Input.GetKey(KeyCode.E)) { campos += _camTransform.up * Time.deltaTime * _positionStep; }
		if (Input.GetKey(KeyCode.Q)) { campos -= _camTransform.up * Time.deltaTime * _positionStep; }
		if (Input.GetKey(KeyCode.W)) { campos += _camTransform.forward * Time.deltaTime * _positionStep; }
		if (Input.GetKey(KeyCode.S)) { campos -= _camTransform.forward * Time.deltaTime * _positionStep; }

		_camTransform.position = campos;
	}

    //UIメッセージの表示
    private IEnumerator DisplayUiMessage()
    {
        _uiMessageActiv = true;
        float time = 0;
        while (time < 2)
        {
            time = time + Time.deltaTime;
            yield return null;
        }
        _uiMessageActiv = false;
    }

    void OnGUI()
    {
        if (_uiMessageActiv == false) { return; }
        GUI.color = Color.black;
        if (_cameraMoveActive == true)
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height - 30, 100, 20), "カメラ操作 有効");
        }

        if (_cameraMoveActive == false)
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height - 30, 100, 20), "カメラ操作 無効");
        }
    }

}

```

## 使い方

1.CameraMover という名前でC#のスクリプトを作成。
2.初期のテンプレートを消して、上記のスクリプトをコピペする。
3.CameraMover をカメラにアタッチする。

※自分の場合は、空のゲームオブジェクトを作って「CameraRig」とか適当な名前つけて「CameraRig」にCameraMoverをアタッチします。
その後、CameraRigの子にカメラのゲームオブジェクトを設定して、カメラのトランスフォームを初期化しています。

## 操作方法

WASD：前後左右の移動
QE：上昇・降下
右ドラッグ：カメラの回転
左ドラッグ：前後左右の移動
スペース：カメラ操作の有効・無効の切り替え
P：回転を実行時の状態に初期化する。

## スクリプト解説

### CameraRotationMouseControl()

```.cs
	private void CameraRotationMouseControl()
	{
		if (Input.GetMouseButtonDown(0))
		{
			_startMousePos = Input.mousePosition;
			_presentCamRotation.x = _camTransform.transform.eulerAngles.x;
			_presentCamRotation.y = _camTransform.transform.eulerAngles.y;
		}
		
		if (Input.GetMouseButton(0))
		{
			//(移動開始座標 - マウスの現在座標) / 解像度 で正規化
			float x = (_startMousePos.x - Input.mousePosition.x) / Screen.width;
			float y = (_startMousePos.y - Input.mousePosition.y) / Screen.height;

			//回転開始角度 ＋ マウスの変化量 * マウス感度
			float eulerX = _presentCamRotation.x + y * _mouseSensitive;
			float eulerY = _presentCamRotation.y + x * _mouseSensitive;

			_camTransform.rotation = Quaternion.Euler(eulerX, eulerY, 0);
		}
	}
```

やってる事はクリックした時に「現在の回転の姿勢」と「クリックをした場所」を保存して…
その値を元にクリックした場所からの変化量をつかって、現在の姿勢に対して追加の回転を計算。
それを新規に現在の回転に適用しています。
やってる事はシンプルなんですが…ここにたどり着くまで自分はけっこうかかりました…

例えば「マウスの現在地を常に現在の回転」にすると、回転できる角度が制限されたり、UIが操作しづらかったり結構不便だったりします。
なので「クリックしてる時だけ変化して欲しい」という事になるのですが「現在の姿勢」と「マウスの変化量を足した次の姿勢」を表現するのが結構厄介です。
適当にやっても回る事は回るのですが、雑にやると**Z成分がまざって傾いたカメラ**になったりします。
Z成分が入ると非常に不便なので…　**_camTransform.rotation = Quaternion.Euler(eulerX, eulerY, 0);**　この行が重要だったりします。


### CameraLocalposKeyControl()

```.cs
    //カメラのローカル移動 キー
    private void CameraLocalposKeyControl()
    {
        Vector3 campos = _camTransform.position;

        if (Input.GetKey(KeyCode.D)) { campos += _camTransform.right * Time.deltaTime * _positionStep; }
        if (Input.GetKey(KeyCode.A)) { campos -= _camTransform.right * Time.deltaTime * _positionStep; }
        if (Input.GetKey(KeyCode.E)) { campos += _camTransform.up * Time.deltaTime * _positionStep; }
        if (Input.GetKey(KeyCode.Q)) { campos -= _camTransform.up * Time.deltaTime * _positionStep; }
        if (Input.GetKey(KeyCode.W)) { campos += _camTransform.forward * Time.deltaTime * _positionStep; }
        if (Input.GetKey(KeyCode.S)) { campos -= _camTransform.forward * Time.deltaTime * _positionStep; }

        _camTransform.position = campos;
    }
```

基本的には**「this.Gameobject.transform」**の forward up right を足してるだけです。
Time.deltaTimeを掛けて１フレームあたりの変化量に調整して、変化の倍率をかけてるだけです。
これだけで**「現在の姿勢に対する前後左右上下に移動」**できます。

## カメラを移動しながら撮影したい。

<a target="blank" href="https://qiita.com/Nekomasu/items/dcdf73f221fd64875ef0">【Unity】Unityでスクリーンショットを撮る時に必要なTipsまとめ</a>
上記を参考にしてみてください。キャラクターを撮影するプロジェクトが作れます。
※キー設定が被ってるのでよしなに。