のりまき日記

Unityなどの活用リファレンスブログ。「こうしたい時どうする」をまとめたい

unity)キャラクターの周りをぐるぐるするカメラを作りたい

プレーヤーを注視するカメラを作る時などに極座標が使えると便利です

極座標とは

いつもの移動と極座標的な移動

詳しくは極座標系 - Wikipedia

Polarを実装

Wikipediaを参考に実装しました

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

[System.Serializable]
public struct Polar : System.IEquatable<Polar>
{
    public float radius;

    [Range(0f, 360f)]
    public float theta;

    [Range(0f, 360f)]
    public float phi;

    public Polar(float radius, float theta, float phi)
    {
        this.radius = radius;
        this.theta = theta;
        this.phi = phi;
    }

    public Vector3 ToCartesian()
    {
        var t = theta * Mathf.Deg2Rad;
        var p = phi * Mathf.Deg2Rad;
        return new Vector3(
            radius * Mathf.Sin(t) * Mathf.Sin(p),
            radius * Mathf.Cos(t),
            radius * Mathf.Sin(t) * Mathf.Cos(p)
        );
    }

    public override bool Equals(object obj) => obj is Polar other && this.Equals(other);

    public bool Equals(Polar p) => radius == p.radius && theta == p.theta && phi == p.phi;

    public override int GetHashCode() => (radius, theta, phi).GetHashCode();

    public static bool operator ==(Polar lhs, Polar rhs) => lhs.Equals(rhs);

    public static bool operator !=(Polar lhs, Polar rhs) => !(lhs == rhs);
}

tips!

構造体を定義するときにUnityっぽく実装したい時があります
そういう時はUnityがGithubで公開しているソースを読むと参考になります
今回はVector2のソースを参考に実装しました

github.com

PolarTransfromを実装

上記Polarの座標をtransformのpositionに変換して設定します
またカメラで使う場合は対象を注視したいことがほとんどのため、LooAtフラグをつけました

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

[RequireComponent(typeof(Transform))]
public class PolarTransfrom : MonoBehaviour
{
    [SerializeField]
    Polar m_Polar;
    public Polar polar { get { return m_Polar; } set { m_Polar = value; UpdatePolar(); } }

    [SerializeField]
    bool m_LookAt;

    void UpdatePolar()
    {
        transform.localPosition = m_Polar.ToCartesian();
        if (m_LookAt)
        {
            transform.localRotation = Quaternion.LookRotation(-transform.localPosition);
        }
    }

#if UNITY_EDITOR

    private void OnDrawGizmos()
    {
        var matrix = transform.parent?.localToWorldMatrix ?? Matrix4x4.identity;

        UnityEditor.Handles.color = Color.red;
        UnityEditor.Handles.matrix = matrix * Matrix4x4.Rotate(Quaternion.Euler(0, m_Polar.phi, 0));
        UnityEditor.Handles.DrawWireDisc(Vector3.zero, Vector3.left, m_Polar.radius);
        UnityEditor.Handles.DrawLine(Vector3.zero, Vector3.up * m_Polar.radius);
        UnityEditor.Handles.DrawLine(Vector3.zero, Quaternion.Euler(m_Polar.theta, 0, 0) * Vector3.up * m_Polar.radius, 2f);

        UnityEditor.Handles.color = Color.blue;
        UnityEditor.Handles.matrix = matrix;
        UnityEditor.Handles.DrawWireDisc(Vector3.zero, Vector3.up, m_Polar.radius);
        UnityEditor.Handles.DrawLine(Vector3.zero, Vector3.forward * m_Polar.radius);
        UnityEditor.Handles.DrawLine(Vector3.zero, Quaternion.Euler(0, m_Polar.phi, 0) * Vector3.forward * m_Polar.radius, 2f);

        UnityEditor.Handles.color = Color.white;
        UnityEditor.Handles.matrix = Matrix4x4.identity;
    }

    private void OnValidate()
    {
        UpdatePolar();
    }
#endif
}

tips!

Unityはインスペクタで値を変更してもスクリプトから値を変更しても即座にビューに反映されます
自分で作るコンポーネントもその思想に乗っかりたいのでOnValidateとSetterを使って上記のように書くことが多いです
Unityの公開しているUIコンポーネントの実装を見るとだいたい同じことしているようです

github.com

プレビュー

カメラ操作がラクラク

このままだとカメラの中心が足元になってしまいます
カメラを上げたり回転させて調整することもできますが傾斜錐台で調整するようにします

LensShiftの実装

UnityのマニュアルにもありますがPhysical Cameraの登場で標準でレンズシフト機能がつきました
今回は通常のCameraコンポーネントをレンズシフトに対応させます

docs.unity3d.com

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

[RequireComponent(typeof(Camera))]
public class LensShift : MonoBehaviour
{
    [SerializeField, Range(-1f, 1f)]
    float m_ShiftX;
    public float shiftX { get { return m_ShiftX; } set { m_ShiftX = value; SetObliqueness(); } }

    [SerializeField, Range(-1f, 1f)]
    float m_ShiftY;
    public float shiftY { get { return m_ShiftY; } set { m_ShiftY = value; SetObliqueness(); } }

    void SetObliqueness()
    {
        Matrix4x4 mat = Camera.main.projectionMatrix;
        mat[0, 2] = m_ShiftX;
        mat[1, 2] = m_ShiftY;
        Camera.main.projectionMatrix = mat;
    }

#if UNITY_EDITOR
    private void OnValidate()
    {
        SetObliqueness();
    }
#endif
}

tips!

傾斜錐台を使う利点はいろいろありますが、カメラの平面を維持したままシフトできる点が役立つ場面が多いと思います

イメージ

おわり

レンズシフト