Unityでの開発において、マウス入力でオブジェクトを回転させる

マウスで3Dオブジェクトを回転させることは、3Dオブジェクトビューアやゲームなどの3Dソフトでよくある機能です。例えば、ゲームを進めるためには、プレイヤーがオブジェクトを詳細に調査する必要があるかもしれません。このような機能は、社内で開発しているUnityプロジェクトで必要とされていました。オンラインで検索すると、いくつかのチュートリアルや記事を見つけましたが、それらはすべて一定のカメラビューを前提としていました。利用者がオブジェクトを回転する時に、オブジェクトに対して相対的に位置やビューを移動したかどうかを考慮していませんでした。そのため、カメラビューが正確に期待した通りではない場合には、正しく回転しませんでした。

その次の問題は、プロジェクトがWebGLと互換性があり、ウェブブラウザ内で実行されなければならないことでした。ブラウザではマウスカーソルの制限があるため、カスタムのマウス入力ハンドラを作成しなければなりませんでした。これについては、今後の記事で取り上げます。

今回紹介したいのは、Unityでマウスを使ってオブジェクトを回転させる方法と、回転しているオブジェクトを見る角度を考慮した方法です。この方法は、一人称視点のコントローラーや、3人称視点のコントローラーにも使えます。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626158/a5647a51-f1e6-3f27-a279-50c068287272.png)

**回転可能なオブジェクトを作成**

まず、オブジェクトを回転させるためのスクリプトを作成します。FirstPersonControllerスクリプトはプレイオブジェクトに付いているコンポーネントで、一人称視点で動き回れるようにしています。この文脈で注意すべき唯一のことは、"IsLookingWithMouse "というブール変数です。オブジェクトを回転させているときはfalseに設定され、回転が終わったら再びtrueに設定されます。これでオブジェクトの回転中時に、カメラの動きを簡単に無効にできます。MonoBehaviourメソッド OnMouseDown() と OnMouseUp() で設定します。

最も重要なメソッドは OnCursorDrag() で、実際の回転を処理します。オブジェクトをクリックしたままに、ドラッグするフレームごとに呼び出されます。まず、"Mouse X" と "Mouse Y" の入力をそれぞれ取得して、オブジェクトの回転に適用する X と Y の回転を計算します。入力値は両方とも、自由に設定できる速度値とラジアン変換のための Mathf.Deg2Rad を掛け合わされます。 そして、オブジェクトに X と Y の回転を適用するために、transform.Rotate() メソッドを個別に呼び出します。カメラの位置を基準にすると、X の軸は上、Y の軸は右になります。相対空間を「ワールド」とします。

```C#:RotatableObject.cs
using UnityEngine;

public class RotatableObject : MonoBehaviour
{
   [SerializeField] private float userRotationSpeed = 100f;
   [SerializeField] private FirstPersonController characterController;

   private void Awake()
   {
       characterController = FindObjectOfType<FirstPersonController>();
   }

   public void OnMouseDown()
   {
       if (characterController == null)    return;
       characterController.IsLookingWithMouse = false;
   }

   public void OnMouseUp()
   {
       if (characterController == null)    return;
       characterController.IsLookingWithMouse = true;
   }
  
   public void OnMouseDrag()
   {
       if (characterController == null || Camera.main == null)   return;

       float rotX = Input.GetAxis("Mouse X") * userRotationSpeed * Mathf.Deg2Rad;
       float rotY = Input.GetAxis("Mouse Y") * userRotationSpeed * Mathf.Deg2Rad;

       transform.Rotate(Camera.main.transform.up, -rotX, Space.World);
       transform.Rotate(Camera.main.transform.right, rotY, Space.World);
   }
}
```

あとは Unity シーンにボックスコライダーと RotatableObject スクリプトが付いているオブジェクトを作成するだけです。内蔵のキューブで十分です。これで、何らかの移動コントローラスクリプトがあれば、シーンの周りを移動したり、好きな角度でキューブを回転させることができます。これが私の実装方法です。

```C#:FirstPersonController.cs
using UnityEngine;

public class FirstPersonController : MonoBehaviour
{
    [SerializeField] private Transform cam = null;

    public float camRotationSpeed = 40f;
    public float camMinimumY = -60f;
    public float camMaximumY = 75f;
    public float rotationSmoothSpeed = 10f;
    public float groundDistance = 0.4f;

    public float walkSpeed = 9f;
    public float runSpeed = 14f;
    public float maxSpeed = 20f;
    public float jumpPower = 1f;
    public float gravity = 9.81f;
    
    public bool IsLookingWithMouse { get; set; }

    private CharacterController characterController;
    private float rotationX;
    private float rotationY;
    private float currentSpeed;
    
    private void Awake()
    {
        characterController = GetComponent<CharacterController>();
        IsLookingWithMouse = true;
        Cursor.lockState = CursorLockMode.Locked;
    }

    private void Update()
    {
        LookRotation();
        Movement();
    }

    private void LookRotation()
    {
        if (!IsLookingWithMouse) return;
        
        rotationX += Input.GetAxis("Mouse X") * camRotationSpeed;
        rotationY += Input.GetAxis("Mouse Y") * camRotationSpeed;
        rotationY = Mathf.Clamp(rotationY, camMinimumY, camMaximumY);
        
        Quaternion bodyTargetRotation = Quaternion.Euler(0, rotationX, 0);
        Quaternion camTargetRotation = Quaternion.Euler(-rotationY, 0, 0);
        float interpolant = Time.deltaTime * rotationSmoothSpeed;
        transform.rotation = Quaternion.Lerp(transform.rotation, bodyTargetRotation, interpolant);
        cam.localRotation = Quaternion.Lerp(cam.localRotation, camTargetRotation, interpolant);
    }

    private void Movement()
    {
        currentSpeed = Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed;

        Vector3 sidewaysMovement = transform.right * Input.GetAxis("Horizontal");
        Vector3 forwardsMovement = transform.forward * Input.GetAxis("Vertical");
        Vector3 movement = sidewaysMovement + forwardsMovement;
        characterController.Move(Time.deltaTime * currentSpeed * movement);
    }
}
```
必ずAwakeかStartメソッドに下記のコードを追加してください。
Cursor.lockState = CursorLockMode.Locked;
これで、フォーカスが合っているときに、マウスがアプリケーションの中心にロックされていることを保証します。


また、画面の中央にシンプルなターゲットアイコンを作成すれば選択しやすくなります。それは、階層ビューで右クリックしてUIイメージを作成することで簡単にできます。

![Capture.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626158/d51e3241-5a38-1559-d067-94e77bcc9d00.jpeg)

以上です。後日、この機能をWebGLで動作するように拡張する方法を説明します。