using System; using System.Collections.Generic; using UnityEngine; #if DOTWEEN using DG.Tweening; #endif namespace Test_2022.CameraControl { [DisallowMultipleComponent] public sealed class CameraWaypointSwitcherTweenedBackup : MonoBehaviour { public enum PathMode { Loop = 0, PingPong = 1 } public enum ShakeMode { Off = 0, Continuous = 1, WhileMoving = 2 } [Serializable] public struct Waypoint { public Transform point; [Min(0f)] public float holdSeconds; } [Header("Target")] [SerializeField] Transform target; [SerializeField] bool autoStartOnEnable = true; [SerializeField] bool useUnscaledTime; [SerializeField] bool restoreOnDisable = true; [Header("Path")] [SerializeField] PathMode pathMode = PathMode.PingPong; [SerializeField] List waypoints = new List(); [SerializeField, Min(0f)] float moveDurationSeconds = 1.2f; [SerializeField, Min(0f)] float defaultHoldSeconds = 1.5f; [SerializeField] AnimationCurve moveEase = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f); [SerializeField] bool startFromNearestWaypoint = true; [Header("Shake")] [SerializeField] ShakeMode shakeMode = ShakeMode.Continuous; [SerializeField] bool createRigForShake = true; [SerializeField] bool shakePosition = true; [SerializeField] bool shakeRotation = true; [SerializeField, Min(0f)] float shakeStrengthPosition = 0.06f; [SerializeField, Min(0f)] float shakeStrengthRotation = 0.65f; [SerializeField, Min(1)] int shakeVibrato = 12; [SerializeField, Range(0f, 180f)] float shakeRandomness = 90f; Vector3 basePosition; Quaternion baseRotation; Transform rig; Transform shakeTransform; #if DOTWEEN Sequence sequence; Tween shakePosTween; Tween shakeRotTween; #endif public IList Waypoints => waypoints; void Reset() { var cam = Camera.main; if (cam != null) target = cam.transform; } void OnEnable() { if (autoStartOnEnable) StartPlayback(); } void OnDisable() { StopPlayback(restoreOnDisable); } public void StartPlayback() { #if DOTWEEN if (target == null) return; if (waypoints == null || waypoints.Count == 0) return; StopPlayback(false); CacheBasePose(); SetupRigAndShakeTransforms(); var moveTransform = rig != null ? rig : target; var indices = BuildTraversalIndices(pathMode, waypoints.Count); if (indices.Count == 0) return; if (startFromNearestWaypoint) RotateTraversalToNearest(indices, moveTransform.position); sequence = DOTween.Sequence(); sequence.SetUpdate(useUnscaledTime); sequence.SetAutoKill(false); for (var i = 0; i < indices.Count; i++) { var wp = waypoints[indices[i]].point; if (wp == null) continue; var move = moveTransform.DOMove(wp.position, moveDurationSeconds); move.SetEase(moveEase); var rot = moveTransform.DORotateQuaternion(wp.rotation, moveDurationSeconds); rot.SetEase(moveEase); sequence.Append(move); sequence.Join(rot); if (shakeMode == ShakeMode.WhileMoving) sequence.Join(CreateOneShotShakeTween(moveDurationSeconds)); sequence.AppendInterval(GetHoldSeconds(indices[i])); } sequence.SetLoops(-1, LoopType.Restart); if (shakeMode == ShakeMode.Continuous) StartContinuousShake(); sequence.Play(); #endif } public void StopPlayback(bool restore) { #if DOTWEEN if (sequence != null) { sequence.Kill(false); sequence = null; } if (shakePosTween != null) { shakePosTween.Kill(false); shakePosTween = null; } if (shakeRotTween != null) { shakeRotTween.Kill(false); shakeRotTween = null; } #endif if (restore) RestoreBasePose(); TeardownRigIfCreated(); } public void ClearWaypoints() { waypoints.Clear(); } public bool AddWaypoint(Transform point, float holdSeconds = -1f) { if (point == null) return false; for (var i = 0; i < waypoints.Count; i++) { if (waypoints[i].point == point) return false; } waypoints.Add(new Waypoint { point = point, holdSeconds = holdSeconds }); return true; } public int RemoveNullWaypoints() { return waypoints.RemoveAll(w => w.point == null); } void CacheBasePose() { if (target == null) return; basePosition = target.position; baseRotation = target.rotation; } void RestoreBasePose() { if (target == null) return; target.position = basePosition; target.rotation = baseRotation; } void SetupRigAndShakeTransforms() { rig = null; shakeTransform = target; if (target == null) return; if (!createRigForShake || shakeMode == ShakeMode.Off) return; var rigGo = new GameObject(target.name + "_Rig"); rigGo.transform.SetPositionAndRotation(target.position, target.rotation); rigGo.transform.SetParent(target.parent, true); rig = rigGo.transform; target.SetParent(rig, true); shakeTransform = target; target.localPosition = Vector3.zero; target.localRotation = Quaternion.identity; } void TeardownRigIfCreated() { if (rig == null || target == null) { rig = null; shakeTransform = null; return; } if (target.parent == rig) target.SetParent(rig.parent, true); if (Application.isPlaying) Destroy(rig.gameObject); else DestroyImmediate(rig.gameObject); rig = null; shakeTransform = null; } float GetHoldSeconds(int waypointIndex) { if (waypointIndex < 0 || waypointIndex >= waypoints.Count) return defaultHoldSeconds; var h = waypoints[waypointIndex].holdSeconds; return h > 0f ? h : defaultHoldSeconds; } static List BuildTraversalIndices(PathMode mode, int count) { var list = new List(); if (count <= 0) return list; if (count == 1) { list.Add(0); return list; } if (mode == PathMode.Loop) { for (var i = 0; i < count; i++) list.Add(i); return list; } for (var i = 0; i < count; i++) list.Add(i); for (var i = count - 2; i >= 1; i--) list.Add(i); return list; } void RotateTraversalToNearest(List indices, Vector3 currentPosition) { if (indices == null || indices.Count == 0) return; var best = 0; var bestDist = float.PositiveInfinity; for (var i = 0; i < indices.Count; i++) { var wi = indices[i]; if (wi < 0 || wi >= waypoints.Count) continue; var p = waypoints[wi].point; if (p == null) continue; var d = (p.position - currentPosition).sqrMagnitude; if (d < bestDist) { bestDist = d; best = i; } } if (best <= 0) return; var rotated = new List(indices.Count); for (var i = best; i < indices.Count; i++) rotated.Add(indices[i]); for (var i = 0; i < best; i++) rotated.Add(indices[i]); indices.Clear(); indices.AddRange(rotated); } #if DOTWEEN Tween CreateOneShotShakeTween(float duration) { if (shakeTransform == null) return null; var seq = DOTween.Sequence(); seq.SetUpdate(useUnscaledTime); if (shakePosition && shakeStrengthPosition > 0f) { var t = shakeTransform.DOShakePosition( duration, shakeStrengthPosition, shakeVibrato, shakeRandomness, false, true ); seq.Join(t); } if (shakeRotation && shakeStrengthRotation > 0f) { var t = shakeTransform.DOShakeRotation( duration, shakeStrengthRotation, shakeVibrato, shakeRandomness, true ); seq.Join(t); } return seq; } void StartContinuousShake() { if (shakeTransform == null) return; var duration = Mathf.Max(0.5f, moveDurationSeconds + defaultHoldSeconds); if (shakePosition && shakeStrengthPosition > 0f) { shakePosTween = shakeTransform.DOShakePosition( duration, shakeStrengthPosition, shakeVibrato, shakeRandomness, false, true ) .SetUpdate(useUnscaledTime) .SetLoops(-1, LoopType.Restart); } if (shakeRotation && shakeStrengthRotation > 0f) { shakeRotTween = shakeTransform.DOShakeRotation( duration, shakeStrengthRotation, shakeVibrato, shakeRandomness, true ) .SetUpdate(useUnscaledTime) .SetLoops(-1, LoopType.Restart); } } #endif } }