プレーヤーを注視するカメラを作る時などに極座標が使えると便利です
極座標とは
詳しくは極座標系 - 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のソースを参考に実装しました
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コンポーネントの実装を見るとだいたい同じことしているようです
プレビュー
このままだとカメラの中心が足元になってしまいます
カメラを上げたり回転させて調整することもできますが傾斜錐台で調整するようにします
LensShiftの実装
UnityのマニュアルにもありますがPhysical Cameraの登場で標準でレンズシフト機能がつきました
今回は通常のCameraコンポーネントをレンズシフトに対応させます
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!
傾斜錐台を使う利点はいろいろありますが、カメラの平面を維持したままシフトできる点が役立つ場面が多いと思います