Untitled
unknown
csharp
5 months ago
11 kB
2
Indexable
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using CodeMonkey.Utils; public class SplineDone : MonoBehaviour { private static readonly Vector3 normal2D = new Vector3(0, 0, -1f); public event EventHandler OnDirty; [SerializeField] private Transform dots = null; [SerializeField] private Vector3 normal = new Vector3(0, 0, -1); [SerializeField] private bool closedLoop; [SerializeField] private List<Anchor> anchorList; private float moveDistance; private float pointAmountInCurve; private float pointAmountPerUnitInCurve = 2f; private List<Point> pointList; private float splineLength; private void Awake() { splineLength = GetSplineLength(); SetupPointList(); } private void Start() { //PrintPath(); } private Vector3 QuadraticLerp(Vector3 a, Vector3 b, Vector3 c, float t) { Vector3 ab = Vector3.Lerp(a, b, t); Vector3 bc = Vector3.Lerp(b, c, t); return Vector3.Lerp(ab, bc, t); } private Vector3 CubicLerp(Vector3 a, Vector3 b, Vector3 c, Vector3 d, float t) { Vector3 abc = QuadraticLerp(a, b, c, t); Vector3 bcd = QuadraticLerp(b, c, d, t); return Vector3.Lerp(abc, bcd, t); } public Vector3 GetPositionAt(float t) { if (t == 1) { // Full position, special case Anchor anchorA, anchorB; if (closedLoop) { anchorA = anchorList[anchorList.Count - 1]; anchorB = anchorList[0]; } else { anchorA = anchorList[anchorList.Count - 2]; anchorB = anchorList[anchorList.Count - 1]; } return transform.position + CubicLerp(anchorA.position, anchorA.handleBPosition, anchorB.handleAPosition, anchorB.position, t); } else { int addClosedLoop = (closedLoop ? 1 : 0); float tFull = t * (anchorList.Count - 1 + addClosedLoop); int anchorIndex = Mathf.FloorToInt(tFull); float tAnchor = tFull - anchorIndex; Anchor anchorA, anchorB; if (anchorIndex < anchorList.Count - 1) { anchorA = anchorList[anchorIndex + 0]; anchorB = anchorList[anchorIndex + 1]; } else { // anchorIndex is final one, either don't link to "next" one or loop back if (closedLoop) { anchorA = anchorList[anchorList.Count - 1]; anchorB = anchorList[0]; } else { anchorA = anchorList[anchorIndex - 1]; anchorB = anchorList[anchorIndex + 0]; tAnchor = 1f; } } return transform.position + CubicLerp(anchorA.position, anchorA.handleBPosition, anchorB.handleAPosition, anchorB.position, tAnchor); } } public Vector3 GetForwardAt(float t) { Point pointA = GetPreviousPoint(t); int pointBIndex; pointBIndex = (pointList.IndexOf(pointA) + 1) % pointList.Count; Point pointB = pointList[pointBIndex]; return Vector3.Lerp(pointA.forward, pointB.forward, (t - pointA.t) / Mathf.Abs(pointA.t - pointB.t)); } public Point GetPreviousPoint(float t) { int previousIndex = 0; for (int i=1; i<pointList.Count; i++) { Point point = pointList[i]; if (t < point.t) { return pointList[previousIndex]; } else { previousIndex++; } } return pointList[previousIndex]; } public Point GetClosestPoint(float t) { Point closestPoint = pointList[0]; foreach (Point point in pointList) { if (Mathf.Abs(t - point.t) < Mathf.Abs(t - closestPoint.t)) { closestPoint = point; } } return closestPoint; } public Vector3 GetPositionAtUnits(float unitDistance, float stepSize = .01f) { float splineUnitDistance = 0f; Vector3 lastPosition = GetPositionAt(0f); float incrementAmount = stepSize; for (float t = 0; t < 1f; t += incrementAmount) { splineUnitDistance += Vector3.Distance(lastPosition, GetPositionAt(t)); lastPosition = GetPositionAt(t); if (splineUnitDistance >= unitDistance) { /* float remainingDistance = splineUnitDistance - unitDistance; Debug.Log(remainingDistance + " " + unitDistance + " " + splineUnitDistance + " " + t); Debug.Log(t - (remainingDistance / splineLength)); return GetPositionAt(t - (remainingDistance / splineLength)); */ Vector3 direction = (GetPositionAt(t) - GetPositionAt(t - incrementAmount)).normalized; return GetPositionAt(t) + direction * (unitDistance - splineUnitDistance); } } // Default Anchor anchorA = anchorList[0]; Anchor anchorB = anchorList[1]; return CubicLerp(anchorA.position, anchorA.handleBPosition, anchorB.handleAPosition, anchorB.position, unitDistance / splineLength); } public Vector3 GetForwardAtUnits(float unitDistance, float stepSize = .01f) { float splineUnitDistance = 0f; Vector3 lastPosition = GetPositionAt(0f); float incrementAmount = stepSize; float lastDistance = 0f; for (float t = 0; t < 1f; t += incrementAmount) { lastDistance = Vector3.Distance(lastPosition, GetPositionAt(t)); splineUnitDistance += lastDistance; lastPosition = GetPositionAt(t); if (splineUnitDistance >= unitDistance) { float remainingDistance = splineUnitDistance - unitDistance; return GetForwardAt(t - ((remainingDistance / lastDistance) * incrementAmount)); } } // Default Anchor anchorA = anchorList[0]; Anchor anchorB = anchorList[1]; return CubicLerp(anchorA.position, anchorA.handleBPosition, anchorB.handleAPosition, anchorB.position, unitDistance / splineLength); } private void SetupPointList() { pointList = new List<Point>(); pointAmountInCurve = pointAmountPerUnitInCurve * splineLength; for (float t = 0; t < 1f; t += 1f / pointAmountInCurve) { pointList.Add(new Point { t = t, position = GetPositionAt(t), normal = normal, }); } pointList.Add(new Point { t = 1f, position = GetPositionAt(1f), }); UpdateForwardVectors(); } private void UpdatePointList() { if (pointList == null) return; foreach (Point point in pointList) { point.position = GetPositionAt(point.t); } UpdateForwardVectors(); } private void UpdateForwardVectors() { // Set forward vectors for (int i = 0; i < pointList.Count - 1; i++) { pointList[i].forward = (pointList[i + 1].position - pointList[i].position).normalized; } // Set final forward vector if (closedLoop) { pointList[pointList.Count - 1].forward = pointList[0].forward; } else { pointList[pointList.Count - 1].forward = pointList[pointList.Count - 2].forward; } } private void PrintPath() { foreach (Point point in pointList) { Transform dotTransform = Instantiate(dots, point.position, Quaternion.identity); FunctionUpdater.Create(() => { dotTransform.position = point.position; }); } } public float GetSplineLength(float stepSize = .01f) { float splineLength = 0f; Vector3 lastPosition = GetPositionAt(0f); for (float t = 0; t < 1f; t += stepSize) { splineLength += Vector3.Distance(lastPosition, GetPositionAt(t)); lastPosition = GetPositionAt(t); } splineLength += Vector3.Distance(lastPosition, GetPositionAt(1f)); return splineLength; } public List<Anchor> GetAnchorList() { return anchorList; } public void AddAnchor() { if (anchorList == null) anchorList = new List<Anchor>(); Anchor lastAnchor = anchorList[anchorList.Count - 1]; anchorList.Add(new Anchor { position = lastAnchor.position + new Vector3(1, 1, 0), handleAPosition = lastAnchor.handleAPosition + new Vector3(1, 1, 0), handleBPosition = lastAnchor.handleBPosition + new Vector3(1, 1, 0), }); } public void RemoveLastAnchor() { if (anchorList == null) anchorList = new List<Anchor>(); anchorList.RemoveAt(anchorList.Count - 1); } public List<Point> GetPointList() { return pointList; } public bool GetClosedLoop() { return closedLoop; } public void SetAllZZero() { foreach (Anchor anchor in anchorList) { anchor.position = new Vector3(anchor.position.x, anchor.position.y, 0f); anchor.handleAPosition = new Vector3(anchor.handleAPosition.x, anchor.handleAPosition.y, 0f); anchor.handleBPosition = new Vector3(anchor.handleBPosition.x, anchor.handleBPosition.y, 0f); } } public void SetAllYZero() { foreach (Anchor anchor in anchorList) { anchor.position = new Vector3(anchor.position.x, 0f, anchor.position.z); anchor.handleAPosition = new Vector3(anchor.handleAPosition.x, 0f, anchor.handleAPosition.z); anchor.handleBPosition = new Vector3(anchor.handleBPosition.x, 0f, anchor.handleBPosition.z); } } public void SetDirty() { splineLength = GetSplineLength(); UpdatePointList(); OnDirty?.Invoke(this, EventArgs.Empty); } [Serializable] public class Point { public float t; public Vector3 position; public Vector3 forward; public Vector3 normal; } [Serializable] public class Anchor { public Vector3 position; public Vector3 handleAPosition; public Vector3 handleBPosition; } }
Editor is loading...
Leave a Comment