// Reference: https://www.behance.net/gallery/44375157/Free-Motion-Camera-Rig using UnityEngine; using System.Collections.Generic; using System.Linq; using System; #if UNITY_EDITOR using UnityEditor; #endif namespace UltraCombos { [ExecuteAlways] public class FreeMotionCameraRig : MonoBehaviour { [SerializeField] private int trailLength = 100; [Header("Rotations")] public float distance; public float horizontal; public float vertical; [Header("Head Extra")] [Range(-90, 90)] public float roll; [Range(-90, 90)] public float tilt; [Range(-90, 90)] public float pan; public float slide; [Header("Controllers")] [SerializeField] private Transform tiltPivot; [SerializeField] private Transform panPivot; [SerializeField] private Transform rollPivot; [Header("Automatic")] [SerializeField] private float horizontalSpeed = 0; private Queue trail = new Queue(); private void Start() { } private void Update() { distance = Mathf.Max(distance, 0); if (Mathf.Abs(horizontalSpeed) > 0) { horizontal = Mathf.Repeat(horizontal + horizontalSpeed * Time.deltaTime, 360); } transform.localEulerAngles = new Vector3(vertical, horizontal, 0); if (tiltPivot) { tiltPivot.localPosition = new Vector3(0, 0, -distance); tiltPivot.localEulerAngles = new Vector3(tilt, 0, 0); } if (panPivot) { panPivot.localEulerAngles = new Vector3(0, pan, 0); } if (rollPivot) { rollPivot.localPosition = new Vector3(0, 0, -slide); rollPivot.localEulerAngles = new Vector3(0, 0, -roll); } trail.Enqueue(rollPivot.position); if (trail.Count > trailLength) { trail.Dequeue(); } } private void OnDrawGizmosSelected() { #if UNITY_EDITOR var rot = Quaternion.Euler(0, transform.localEulerAngles.y, 0); var trs = Matrix4x4.TRS(transform.position, rot, transform.localScale); using (new Handles.DrawingScope(new Color32(5, 169, 245, 255), trs)) { Handles.DrawWireDisc(Vector3.zero, Vector3.up, distance); var d = distance * 1.1f; var r = distance * 0.08f; var triangle = GetPoints(3, true) .Select(p => p * r * 2 + new Vector3(0, 0, d)) .ToArray(); Handles.DrawPolyLine(triangle); Handles.DrawWireDisc(new Vector3(+d, 0, 0), Vector3.up, r); Handles.DrawWireDisc(new Vector3(-d, 0, 0), Vector3.up, r); var circle = GetPoints(32, false); var lines = new List(); var cr = distance * 0.9f; for (var i = 0; i < circle.Count; ++i) { var i2 = (i + 1) % circle.Count; lines.Add(circle[i] * cr); lines.Add(circle[i2] * cr); } Handles.DrawDottedLines(lines.ToArray(), 4); } var size = distance * 0.1f; if (tiltPivot) { } if (panPivot) { using var scp = new Handles.DrawingScope(new Color32(194, 153, 0, 255), panPivot.localToWorldMatrix); var w = size; var h = size * 2; var points = new Vector3[] { new Vector3(+w, 0, 0), new Vector3(0, 0, +h), new Vector3(-w, 0, 0), new Vector3(0, 0, -h), new Vector3(w, 0, 0) }; Handles.DrawPolyLine(points); } if (rollPivot) { using var scp = new Handles.DrawingScope(new Color32(56, 186, 110, 255), rollPivot.localToWorldMatrix); var p0 = rollPivot.InverseTransformPoint(transform.position); Handles.DrawLine(p0, Vector3.zero); var p1 = rollPivot.InverseTransformPoint(trs.MultiplyPoint(new Vector3(0, 0, -distance))); Handles.DrawLine(p1, Vector3.zero); var w = size * 1.2f; var h = size * 0.2f; var points = new Vector3[] { new Vector3(+w, 0, -h), new Vector3(-w, 0, -h), new Vector3(0, 0, h * 4), new Vector3(+w, 0, -h) }; Handles.DrawPolyLine(points); } Handles.DrawPolyLine(trail.ToArray()); #endif } private List GetPoints(int sides, bool close) { var da = 2 * Mathf.PI / sides; var points = new List(); for (var i = 0; i < sides; i++) { var a = da * i + Mathf.PI / 2; points.Add(new Vector3(Mathf.Cos(a), 0, Mathf.Sin(a))); } if (close) { points.Add(points.First()); } return points; } } }