407 lines
11 KiB
C#
407 lines
11 KiB
C#
|
|
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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|