Unityでオブジェクトを中心にカメラを移動させるスクリプト


スワイプでカメラ移動させたい!!頼む!!

最近Unityの楽しさを知ってからというものの、業務でPHPを使いつつ、家ではがっつりゲーム開発というワケワカメな状況を送っているBuzzです。

今回はそんな初心者ゲーム開発者(仮)の私がボードゲーム(仮)を作成する際、盤面状況の把握方法として盤面を中心としたカメラ移動を画面スワイプで実現したやり方を備忘録として投稿します。そしてあわよくば猛者様の意見を頂戴できればと。

どんなものができたのか

まずは完成形をお見せします。
今回はチェスや将棋、オセロなどの盤面を想定しているのでY方向の回転は無しにしています。(Y方向回転についても後述してます)
なぜ宇宙にタイルが浮いているかは置いといて、この処理をコーディングしていきます。

Let's コーディング

とりあえずこんなコードになりました。今回はカメラに関する処理なのでファイル名をCameraControllerとして、カメラオブジェクトにアタッチしています。

CameraController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    //① START
    GameObject mainCamera;  //カメラ
    GameObject fieldObject; //フィールド(中心としたいオブジェクト、今回は盤面を中心とするためfield)
    public float rotateSpeed = 1.0f; //回転スピード
    //① END

    void Start()
    {
        //② START
        this.mainCamera = Camera.main.gameObject; //カメラオブジェクトを代入
        this.fieldObject = GameObject.Find("Field"); //"Field"に中心としたいオブジェクトをHierarchyの名前から指定
        //② END
    }
    void Update()
    {
        //③ START
        if(Input.GetMouseButton(0))
        {
            rotateCamera();
        }
        //③ END
    }
    //④ START
    private void rotateCamera()
    {
        Vector3 angle = new Vector3(
                Input.GetAxis("Mouse X") * this.rotateSpeed,
                0,
                0
            );
        this.mainCamera.transform.RotateAround(this.fieldObject.transform.position, Vector3.up, angle.x);
    }
    //④ END
}

①カメラ、中心としたいオブジェクト(以降「中心オブジェ」)、回転のスピードをそれぞれ定義し、この時点ではカメラと中心オブジェは空の状態です。

②Start()の中でカメラ(mainCamera)にカメラオブジェクト、中心オブジェ(fieldObject)にFieldオブジェクトを代入しています。

③Update()の中で「もし左クリックを押している間」という条件のもと、rotateCamera()を実行しています。実際これくらいの記述量であればこの中にrotateCameraの中身を書いてもいいかもしれません。

④rotateCamera()を定義しています。Vector3引数にGetAxis関数でX方向に生じた値を取って回転スピード(rotateSpeed)と掛けた値、YとZに関しては今回用はないので0を渡しています。最後にカメラオブジェクトのRotateAround関数でfieldObjectの中心、回転の軸(今回はY)、回転方向(今回はX)とすることで回転を実現しています。

実機で動かす

これで実際にビルドしてスマートフォンで確認してみました。

、、、って、あれ?
なんか挙動がおかしいです。

すこし見辛いですが、画面をタップするたびに別のアングルにワープするようになってしまいました。
これでは盤面状況の確認時にかなりストレスですよね。。
こんな時はGoogle先生の出番です(最初から出番しかありませんが)

検索すると直前の回転を保持しておくことで解決できそうです。

自然にスクロール編

いろいろ調べた結果、以下に落ち着きました。

CameraController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    GameObject mainCamera;
    GameObject fieldObject;
    public float rotateSpeed = 1.0f;
    private Vector3 lastMousePosition; //①追記
    private Vector3 newAngle = new Vector3(0, 0, 0); //②追記
    void Start()
    {
        this.mainCamera = Camera.main.gameObject;
        this.fieldObject = GameObject.Find("Field");
    }
    void Update()
    {   
        //追記 START
        if(Input.GetMouseButtonDown(0)) //③
        {
            newAngle = mainCamera.transform.localEulerAngles; //④
            lastMousePosition = Input.mousePosition; //⑤
        }
        //追記 END
        else if(Input.GetMouseButton(0))
        {
            rotateCamera();
        }
    }

    private void rotateCamera()
    {
        Vector3 angle = new Vector3(
                Input.GetAxis("Mouse X") * this.rotateSpeed,
                0,
                0
            );
        this.mainCamera.transform.RotateAround(this.fieldObject.transform.position, Vector3.up, angle.x);
    }
}

①直前の回転を保持しておくためのVector変数lastMousePositionを定義、最初の時点では空です。

②Vector型変数newAngleにX:0,Y:0,Z:0のVector3インスタンスを代入しています。

③左クリックが押下された際、一回だけ実行されます。

④newAngle変数に相対的な回転地を代入しています。

⑤lastMousePosition変数に左クリックされた際のマウス位置を代入しています。

今度こそ!

これで直前までの回転を保持してくれて、想定通りの動きをしてくれそうです!
ビルドしてみましたー

いい感じ・・・圧倒的いい感じ・・・っ!!!
そんなこんなで初心者が無事、カメラ回転を実装できました。
もっと簡単に実装できる方法などあればコメントお願いします。

おまけ

最後にY方向の回転も追加して終わろうと思います。
これは今あるコードに以下を追加することでチョチョイのチョイのお茶の子さいさいのサイって感じで実装できました。

CameraController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    GameObject mainCamera;
    GameObject fieldObject;
    public float rotateSpeed = 1.0f;
    private Vector3 lastMousePosition;
    private Vector3 newAngle = new Vector3(0, 0, 0);
    void Start()
    {
        this.mainCamera = Camera.main.gameObject;
        this.fieldObject = GameObject.Find("Field");
    }
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            newAngle = mainCamera.transform.localEulerAngles;
            lastMousePosition = Input.mousePosition;
        }
        else if(Input.GetMouseButton(0))
        {
            rotateCamera();
        }
    }

    private void rotateCamera()
    {
        Vector3 angle = new Vector3(
                Input.GetAxis("Mouse X") * this.rotateSpeed,
                Input.GetAxis("Mouse Y") * this.rotateSpeed, //追記
                0
            );
        this.mainCamera.transform.RotateAround(this.fieldObject.transform.position, Vector3.up, angle.x);
        this.mainCamera.transform.RotateAround(this.fieldObject.transform.position, transform.right, angle.y); //追記
    }
}

説明は前述しているので省きますが、以下が実現します。

盤面確認などで使うには裏面まで見る必要はないので、Y方向が一定値以下になったら固定させる処理などを追加する必要がありますね。ですが例えば入手したアイテムなどを眺めるシーンなんかにはそのまま使えそうです。まんもすうれぴー。

参考サイト

非常にお世話になりました。

https://xr-hub.com/archives/6272
https://tech.pjin.jp/blog/2018/09/05/unity_material_color-change/
https://mogi0506.com/uinty-camera-rotation/

少しでもお役に立てればと思います。