読者です 読者をやめる 読者になる 読者になる

Unityでカメラをメタセコイア風に動かす

ゴールデンウィーク堪能中。落ち着いて勉強できる時間があるのはやっぱりいいですね。

Unityをしっかり使いこなそうと思いつつ、なかなか使えていなかったのですが、連休のおかげでようやく手が出せました。今回はメタセコイア風のカメラ操作を実装してみたいと思います。

メタセコイアのカメラ操作

メタセコイアのカメラの操作方法は

  • 右ボタンドラッグ:注視点を中心に回転
  • 中ボタンドラッグ:平行移動
  • マウスホイール :注視点との距離を変更

となっています。

Unityのシーンビューのカメラ操作と似ていますが、Unityの場合は右ボタンドラッグ時の動きがカメラの旋回(パン)になっており、注視点を中心とした回転は、フォーカス中にAlt+左ボタンドラッグになります。

この操作をゲーム内でも使えるようにしてみます。

実際のコード

実際のコードはこちら。

対象のカメラにアタッチすることで操作ができるようになります。

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

public class CameraController : MonoBehaviour
{
    public Vector3 cameraSpeed = new Vector3(0.6f, 0.6f, 10.0f);
    public Vector3 cameraTarget = Vector3.zero;
    public Vector3 cameraAngle = new Vector3(-40.0f, 0.0f, 0.0f);
    public float cameraDistance = 10.0f;

    private Vector3 lastMousePosition;
  
    void Start()
    {
        UpdateCamera();
    }

    void Update()
    {
        //
        // rotates the camera while the mouse left button is pressed
        //
        if (Input.GetMouseButton(1))
        {
            Vector3 dragOffset = Input.mousePosition - lastMousePosition;

            if (!Input.GetMouseButtonDown(1))
            {
                cameraAngle.x = (cameraAngle.x + dragOffset.y * cameraSpeed.x) % 360.0f;
                cameraAngle.y = (cameraAngle.y - dragOffset.x * cameraSpeed.y) % 360.0f;
            }
        }

        //
        // dollies the camera while the mouse middle button is pressed
        //
        if (Input.GetMouseButton(2))
        {
            Camera camera = GetComponent<Camera>();
            Vector3 mousePositionInWorld = camera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, cameraDistance));
            Vector3 lastMousePositionInWorld = camera.ScreenToWorldPoint(new Vector3(lastMousePosition.x, lastMousePosition.y, cameraDistance));
            Vector3 dragOffset = mousePositionInWorld - lastMousePositionInWorld;

            if (!Input.GetMouseButtonDown(2))
            {
                cameraTarget -= dragOffset;
            }
        }

        lastMousePosition = Input.mousePosition;

        //
        // moves the camera forward or backward when the mouse wheel is rotated
        //
        float mouseScrollWheel = Input.GetAxis("Mouse ScrollWheel");
        if (mouseScrollWheel != 0.0f)
        {
            cameraDistance = Mathf.Max(cameraDistance - mouseScrollWheel * cameraSpeed.z, 0.0f);
        }

        UpdateCamera();
    }

    void UpdateCamera()
    {
        transform.rotation = Quaternion.Euler(-cameraAngle);
        transform.position = cameraTarget + transform.rotation * Vector3.back * cameraDistance;
    }
}

扱いやすさと誤差の蓄積防止の観点から、カメラの回転はcameraAngleに保持したオイラー角から毎回作り直しています。

publicプロパティ

publicプロパティの意味は以下の通りです。

  • Vector3 cameraSpeed :カメラの回転と前後移動の速度
  • Vector3 cameraTarget:カメラの回転中心位置
  • Vector3 cameraAngle :カメラの初期角度
  • float cameraDistance:カメラと回転中心との初期の距離

実際の動作画面

実際の動作画面です。

右ボタンドラッグ→中ボタンドラッグ→マウスホイールの順に操作しています。

f:id:tkitao:20170505175926g:plain

これまでは、ベクトル・行列・クォータニオンは自前のライブラリを使うことがほとんどだったのですが、ようやくUnityとの対応付けができました。慣れてくると楽しいですね。