UnityCameraにAddComponentするだけで2DViwerを作る

Unityに使い慣れる為に、先日、[3DViwer](https://qiita.com/_akoto_/items/19366d70518641738407)を作ったのですが、今回は2DViewerの話です。
内容的には、一つのファイルにまとめて、CameraにAddComponentするだけで実現可能なようにしています。

## 変更点

- 回転軸が2軸から1軸に変更
- 拡大はそのままスケールとして使用
- 回転するオブジェクトをカメラからターゲットに変更
- 平行移動時の加算するベクトル変更


## 変更点の補足
**回転軸が2軸から1軸に変更**
3Dの時は、縦横の移動距離をそのまま回転角度に割り当てていましたが、2Dでは1軸になる為、縦横の移動を1軸の回転へと変換しないといけません。
回転力は出せたとしても、右回り、左回りどちらに回転させるべきか、迷うかもしれませんが、その時は、2つのベクトルを渡して、内積からcosθを求めてやれば、正負を判定できます。コード中も、その為、mouseTouchStartPos を常に更新しています。

**拡大はそのままスケールとして使用**
書いてある通り

**回転するオブジェクトをカメラからターゲットに変更**
一度、試してもらえると分かると思いますが、カメラが回転すると背景も回転する為、目が回ります。
背景が一色かつ装飾の無ければ、カメラを回転させても問題無いかも知れませんが、通常のゲームは無意味な空間は無い為、リッチな装飾を施してあるものです。

**平行移動時の加算するベクトル変更**
3Dでは、 [transform.up](https://docs.unity3d.com/ja/2017.4/ScriptReference/Transform-up.html)と [transform.right](https://docs.unity3d.com/ja/2017.4/ScriptReference/Transform-right.html)を使用していましたが、2Dでは、縦横の移動距離をそのままx軸とy軸に当てはめる事になるかと思います。

## コード
実際に書いてみたコードです。検証用に書いた為、品質は保証ありません。
変数名やコードのフォーマットは、3DViewerに書いたコードに意味や使用用途をなるべく合わせるようにしました。
Inputで入力を取っていますが、SerializeFieldで、回転させるターゲットを外部から参照する為、渡す必要があります。

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


namespace App
{
    public class PolarCoordinatesCameraController : MonoBehaviour
    { 
         enum MouseClickType
        {
            None,
            Left,
            Right,
        }

        [SerializeField] float wheelLength = 2f;
        [SerializeField] float wheelSpeed = 0.1f;
        [SerializeField] float mouseLeftSpeed = 1f;
        [SerializeField] float mouseRightSpeed = 1f;
        [SerializeField] Image image;

        Vector2 mouseTouchStartPos;
        MouseClickType mouseClickType = MouseClickType.None;
        bool isMouseClick = false;

        void Update()
        {
            if (image == null)
            {
                return;
            }
            
            var wheel =  Input.GetAxis("Mouse ScrollWheel");

            var cameraPostion = new Vector2(0f, 0f);
            var radius = 0f;
            var cosAngle = 0f;
            if (wheel == 0f)
            {
                if (Input.GetMouseButtonDown(0))
                {
                    mouseTouchStartPos = Input.mousePosition;
                    mouseClickType = MouseClickType.Left;
                    isMouseClick = true;
                }
                else if (Input.GetMouseButtonDown(1))
                {
                    mouseTouchStartPos = Input.mousePosition;
                    mouseClickType = MouseClickType.Right;
                    isMouseClick = true;
                }
                else if (Input.GetMouseButton(0))
                {
                    var mousePosition = Input.mousePosition;
                    var targetRotation = new Vector2((mousePosition.x - mouseTouchStartPos.x) * mouseLeftSpeed,
                                                     (mousePosition.y - mouseTouchStartPos.y) * mouseLeftSpeed);
                    cosAngle = Vector2.Dot(mousePosition, targetRotation) / mousePosition.magnitude * targetRotation.magnitude;
                    mouseTouchStartPos = Input.mousePosition;
                }
                else if (Input.GetMouseButton(1))
                {
                    cameraPostion.x = (Input.mousePosition.x - mouseTouchStartPos.x) * mouseRightSpeed;
                    cameraPostion.y = (Input.mousePosition.y - mouseTouchStartPos.y) * mouseRightSpeed;
                    mouseTouchStartPos = Input.mousePosition;
                }
                else if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1))
                {
                    mouseTouchStartPos = Vector2.zero;
                    isMouseClick = false;
                }
            }
            else
            {
                if (Input.GetAxis("Mouse ScrollWheel") < 0)
                {
                    radius = wheelSpeed;
                }
                else
                {
                    radius = -wheelSpeed;
                }
            }

            // Scale
            var scale = image.transform.localScale;
            var newScaleX = (scale.x + radius < 1f) ? 1f : scale.x + radius;
            var newScaleY = (scale.y + radius < 1f) ? 1f : scale.y + radius;
            image.transform.localScale =  new Vector3(newScaleX, newScaleY, scale.z);
            // Rotate
            var angle = (cosAngle > 0) ? -mouseLeftSpeed : (cosAngle < 0) ? mouseLeftSpeed : 0;
            image.transform.Rotate(new Vector3(0, 0, 1), angle);
            // Translate
            var position = image.transform.position;
            image.transform.position =  new Vector3(position.x + cameraPostion.x, position.y + cameraPostion.y, position.z);
        }
    }
}
```

Hierarchy と CameraのInspectorのスクショを載せておきます。
![shot1.jpg](https://qiita-image-store.s3.amazonaws.com/0/153021/94f4a8ae-6a78-61bb-bbf5-d67b2017124e.jpeg)
![shot2.jpg](https://qiita-image-store.s3.amazonaws.com/0/153021/830cdbf0-b4e7-7af2-571d-8cd98765ef19.jpeg)


## まとめ
2DViewerの場合、回転を落とし込むのが面倒だと思いますが、それ以外の変換は難しくありません。
回転について、xとyの2軸から回転する角度を出していますが、xとyのどちらか、移動距離が大きい方から算出してしまった方が、良いかもしれません。
また、実際にゲームに導入する際は、ばねを入れる必要があるかと思います。