using Cinemachine; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Serialization; namespace game { [ExecuteAlways] [DisallowMultipleComponent] public class CameraFollowPathMoveController : MonoBehaviour { /// The path to follow [Tooltip("The path to follow")] public CinemachinePath Path; [Tooltip("路径停歇点索引,循环执行。eg:5个点,执行到最后一个位置会从移动到初始位置开始。")] public List m_RestIndexList = new List(); [Tooltip("磁性距离,距离目标点一定距离,即认为到达目标点。")] public float m_MagneticDistance = 0.01f; [Tooltip("一直移动,可以查看整个移动过程,主要用于本地测试。发包时关闭。")] public bool IsAlwaysMoving = false; [Tooltip("是否开启测试停歇点,配合TestRestIndex使用,主要用于本地测试。发包时关闭。")] public bool m_IsTestRest = false; [Tooltip("测试用停歇点索引,配合IsTestRest使用,默认为-1,在0-停歇点索引位之间生效。")] public int m_TestRestIndex = -1; /// This enum defines the options available for the update method. public enum UpdateMethod { /// Updated in normal MonoBehaviour Update. Update, /// Updated in sync with the Physics module, in FixedUpdate FixedUpdate, /// Updated in normal MonoBehaviour LateUpdate LateUpdate }; /// When to move the cart, if Velocity is non-zero [Tooltip("When to move the cart, if Velocity is non-zero")] public UpdateMethod m_UpdateMethod = UpdateMethod.Update; /// How to interpret the Path Position [Tooltip("How to interpret the Path Position. If set to Path Units, values are as follows: 0 represents the first waypoint on the path, 1 is the second, and so on. Values in-between are points on the path in between the waypoints. If set to Distance, then Path Position represents distance along the path.")] [HideInInspector] public CinemachinePathBase.PositionUnits m_PositionUnits = CinemachinePathBase.PositionUnits.Distance; /// Move the cart with this speed [Tooltip("Move the cart with this speed along the path. The value is interpreted according to the Position Units setting.")] [FormerlySerializedAs("m_Velocity")] public float Speed; /// The cart's current position on the path, in distance units [Tooltip("The position along the path at which the cart will be placed. This can be animated directly or, if the velocity is non-zero, will be updated automatically. The value is interpreted according to the Position Units setting.")] [FormerlySerializedAs("m_CurrentDistance")] [HideInInspector] public float m_Position; private enum MoveState { Stationary, Moving } private MoveState m_State = MoveState.Stationary; private int m_MoveIndex = 0; private int m_CurPathIndex = 0; //private int m_MoveDirection = 0; void Awake() { #if UNITY_EDITOR if (Path != null) { ResamplePath(); JumpToRestIndex(0); SetCartPosition(0f); } #endif } void FixedUpdate() { if (m_State == MoveState.Stationary && !IsAlwaysMoving) return; if (m_UpdateMethod == UpdateMethod.FixedUpdate) SetCartPosition(m_Position + Speed * Time.deltaTime); } void Update() { CheckTestRest(); if (m_State == MoveState.Stationary && !IsAlwaysMoving) return; float speed = Application.isPlaying ? Speed : 0; if (m_UpdateMethod == UpdateMethod.Update) SetCartPosition(m_Position + speed * Time.deltaTime); } void LateUpdate() { if (m_State == MoveState.Stationary && !IsAlwaysMoving) return; if (!Application.isPlaying) SetCartPosition(m_Position); else if (m_UpdateMethod == UpdateMethod.LateUpdate) SetCartPosition(m_Position + Speed * Time.deltaTime); } public void ResetAndInit(CinemachinePath followPath, List restIndexList, float magneticDistance) { if (followPath == null || restIndexList == null) return; if (Path) { Path = null; } if (m_RestIndexList != null) { m_RestIndexList = null; } m_MagneticDistance = magneticDistance; Path = followPath; m_RestIndexList = restIndexList; ResamplePath(); } void SetCartPosition(float distanceAlongPath) { if (Path != null) { m_Position = Path.StandardizeUnit(distanceAlongPath, m_PositionUnits); if (!IsAlwaysMoving) m_Position = CheckPosition(distanceAlongPath, m_Position); transform.position = Path.EvaluatePositionAtUnit(m_Position, m_PositionUnits); transform.rotation = Path.EvaluateOrientationAtUnit(m_Position, m_PositionUnits); } } private void CheckTestRest() { if (m_IsTestRest) { if (m_TestRestIndex >= 0 && m_TestRestIndex < m_RestIndexList.Count) { m_MoveIndex = m_TestRestIndex; if (!IsEffective()) return; GoToPositionIndex(m_RestIndexList[m_MoveIndex]); } } } List m_RestPointDistance = new List(); private void ResamplePath() { int stepsPerSegment = Path.DistanceCacheSampleStepsPerSegment; float minPos = Path.MinPos; float maxPos = Path.MaxPos; float stepSize = 1f / Mathf.Max(1, stepsPerSegment); // Sample the positions int numKeys = Mathf.RoundToInt((maxPos - minPos) / stepSize) + 1; float[] m_PosToDistance = new float[numKeys]; m_RestPointDistance.Clear(); Vector3[] firstRestDistance = new Vector3[2]; Vector3 firstDest = new Vector3(-m_MagneticDistance, 0f, m_MagneticDistance); firstRestDistance[0] = firstDest; m_RestPointDistance.Add(firstRestDistance); int index = 0; float m_PathLength = 0f; Vector3 p0 = Path.EvaluatePosition(0); m_PosToDistance[0] = 0; float pos = minPos; for (int i = 1; i < numKeys; ++i) { pos += stepSize; Vector3 p = Path.EvaluatePosition(pos); float d = Vector3.Distance(p0, p); m_PathLength += d; p0 = p; m_PosToDistance[i] = m_PathLength; if (i % stepsPerSegment == 0) { Vector3[] tmpArr = new Vector3[1]; Vector3 tmp = new Vector3(m_PathLength - m_MagneticDistance, m_PathLength, m_PathLength + m_MagneticDistance); tmpArr[0] = tmp; m_RestPointDistance.Add(tmpArr); ++index; } } if (Path.Looped) m_RestPointDistance.RemoveAt(index); Vector3 lastDest = new Vector3(m_PathLength - m_MagneticDistance, m_PathLength, m_PathLength + m_MagneticDistance); firstRestDistance[1] = lastDest; } private bool IsEffective() { return (m_MoveIndex >= 0 && m_MoveIndex < m_RestIndexList.Count && m_RestIndexList.Count > 0) ? true : false; } public int RestCount { get { if (m_RestIndexList == null) return 0; return m_RestIndexList.Count; } } public int MoveIndex { get => m_MoveIndex; } public int ForwardToNextRest() { m_MoveIndex++; if (m_MoveIndex > m_RestIndexList.Count - 1) { if (Path.Looped) m_MoveIndex = 0; else m_MoveIndex = m_RestIndexList.Count - 1; } GoToPositionIndex(m_RestIndexList[m_MoveIndex]); if (Speed < 0) { Speed = -Speed; } return m_MoveIndex; } public int BackwardToNextRest() { m_MoveIndex--; if (m_MoveIndex < 0) { if (Path.Looped) m_MoveIndex = m_RestIndexList.Count - 1; else m_MoveIndex = 0; } GoToPositionIndex(m_RestIndexList[m_MoveIndex]); if (Speed > 0) { Speed = -Speed; } return m_MoveIndex; } public int JumpToNextRestForward() { JumpToRestIndex(++m_MoveIndex); return m_MoveIndex; } public int JumpToNextRestBackward() { JumpToRestIndex(--m_MoveIndex); return m_MoveIndex; } public int JumpToRestIndex(int index) { m_MoveIndex = index; if (m_MoveIndex < 0) { if (Path.Looped) m_MoveIndex = m_RestIndexList.Count - 1; else m_MoveIndex = 0; } else if (m_MoveIndex > m_RestIndexList.Count - 1) { if (Path.Looped) m_MoveIndex = 0; else m_MoveIndex = m_RestIndexList.Count - 1; } GoToPositionIndex(m_RestIndexList[m_MoveIndex]); var tmpArr = m_RestPointDistance[m_CurPathIndex]; Vector3 boundary = tmpArr[0]; float distanceAlongPath = boundary.y; m_Position = Path.StandardizeUnit(distanceAlongPath, m_PositionUnits); SetCartPosition(m_Position); m_State = MoveState.Stationary; return m_MoveIndex; } public void GoToPositionIndex(int index) { m_CurPathIndex = index; m_State = MoveState.Moving; } float CheckPosition(float distanceAlongPath, float distance) { if (m_CurPathIndex < 0 || m_CurPathIndex >= m_RestPointDistance.Count) return distance; var tmpArr = m_RestPointDistance[m_CurPathIndex]; int count = tmpArr.Length; if (count == 0) return distance; if (m_CurPathIndex == 0) { if (Speed > 0f) { if (distanceAlongPath >= Path.PathLength) { m_State = MoveState.Stationary; m_IsTestRest = false; FishingYachtAct.Publish(new EventWashingCameraStopData()); return tmpArr[0].y; } } else if (Speed < 0f) { if (distanceAlongPath <= 0f) { m_State = MoveState.Stationary; m_IsTestRest = false; FishingYachtAct.Publish(new EventWashingCameraStopData()); return tmpArr[0].y; } } } else { if (Speed > 0f) { if (distance >= tmpArr[0].x) { m_State = MoveState.Stationary; m_IsTestRest = false; FishingYachtAct.Publish(new EventWashingCameraStopData()); return tmpArr[0].y; } } else if (Speed <= 0f) { if (distance <= tmpArr[0].z) { m_State = MoveState.Stationary; m_IsTestRest = false; FishingYachtAct.Publish(new EventWashingCameraStopData()); return tmpArr[0].y; } } } return distance; } } }