Files

407 lines
11 KiB
C#
Raw Permalink Normal View History

2026-07-08 19:53:15 +08:00
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<Waypoint> waypoints = new List<Waypoint>();
[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<Waypoint> 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<int> BuildTraversalIndices(PathMode mode, int count)
{
var list = new List<int>();
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<int> 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<int>(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
}
}