diff --git a/Assets/PathTools/Scenes/PathToolsExample.unity b/Assets/PathTools/Scenes/PathToolsExample.unity index dfe1563..cd7c57d 100644 --- a/Assets/PathTools/Scenes/PathToolsExample.unity +++ b/Assets/PathTools/Scenes/PathToolsExample.unity @@ -158,22 +158,23 @@ MonoBehaviour: rightHandle: {x: -0.28970897, y: 0.013729572, z: -3.5340805} orientation: -90 tangentType: 0 - - localPos: {x: 1.4542342, y: 0.013729572, z: 0.3277291} - leftHandle: {x: 1.237987, y: 0.013729572, z: -0.6486104} - rightHandle: {x: 1.6652508, y: 0.013729572, z: 1.2804526} + - localPos: {x: 1.4273251, y: 0.27377248, z: 0.3277291} + leftHandle: {x: 1.2110769, y: 0.27377248, z: -0.6486101} + rightHandle: {x: 1.6383429, y: 0.27377248, z: 1.2804527} orientation: 90 tangentType: 0 - - localPos: {x: -0.2224189, y: 0.013729572, z: 2.9288592} - leftHandle: {x: 2.0906916, y: 0.013729572, z: 3.5246074} - rightHandle: {x: -2.2515397, y: 0.013729572, z: 2.4062533} + - localPos: {x: -0.39892852, y: 0.013729572, z: 3.2549062} + leftHandle: {x: 1.6936592, y: 0.013729572, z: 3.6338682} + rightHandle: {x: -2.3432288, y: 0.013729572, z: 2.9027987} orientation: 0 tangentType: 0 selectedId: 0 - closeLoop: 1 - showUpVector: 0 - lastPos: {x: 1.4542342, y: 0.013729572, z: 0.3277291} - lastLeftHandlePos: {x: 1.237987, y: 0.013729572, z: -0.6486104} - lastRightHandlePos: {x: 1.6652508, y: 0.013729572, z: 1.2804526} + handleMulti: 0.1 + closeLoop: 0 + showUpVector: 1 + lastPos: {x: 1.4273251, y: 0.27377248, z: 0.3277291} + lastLeftHandlePos: {x: 1.2110769, y: 0.27377248, z: -0.6486101} + lastRightHandlePos: {x: 1.6383429, y: 0.27377248, z: 1.2804527} --- !u!4 &800040797 Transform: m_ObjectHideFlags: 0 @@ -308,7 +309,8 @@ MonoBehaviour: loopMode: 0 useCustomUpVector: 0 customUpVector: {x: 0, y: 1, z: 0} - distance: 0 + distance: 9.30682 + pathLength: 9.30682 --- !u!23 &1977854513 MeshRenderer: m_ObjectHideFlags: 0 @@ -365,7 +367,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1977854510} - m_LocalRotation: {x: -0.6220584, y: 0.6220584, z: -0.3362193, w: 0.3362193} + m_LocalRotation: {x: -0.33422214, y: -0.3379994, z: 0.6231337, w: 0.6210929} m_LocalPosition: {x: -2.1664882, y: 0.027459145, z: -1.6148556} m_LocalScale: {x: 0.4, y: 0.4, z: 1} m_Children: [] diff --git a/Assets/PathTools/Scripts/Editor/PathScriptEditor.cs b/Assets/PathTools/Scripts/Editor/PathScriptEditor.cs index 0cc7776..8e2169f 100644 --- a/Assets/PathTools/Scripts/Editor/PathScriptEditor.cs +++ b/Assets/PathTools/Scripts/Editor/PathScriptEditor.cs @@ -13,11 +13,14 @@ public class PathScriptEditor : Editor float pickSize = 1f; float subPickSize = 0.3f; + SerializedProperty handleSize; + SelectedNode currentSelectedNode; private void OnEnable() { source = (PathScript)target; + handleSize = serializedObject.FindProperty("handleMulti"); } private void OnSceneGUI() @@ -30,7 +33,7 @@ private void OnSceneGUI() if (i == selectedId) continue; - if (Handles.Button(nodePos, Quaternion.identity, 0.2f, HandleUtility.GetHandleSize(nodePos) * pickSize, Handles.SphereHandleCap)) + if (Handles.Button(nodePos, Quaternion.identity, handleSize.floatValue, HandleUtility.GetHandleSize(nodePos) * pickSize, Handles.SphereHandleCap)) { source.lastPos = source.Nodes[i].localPos; source.lastLeftHandlePos = source.Nodes[i].leftHandle; @@ -46,6 +49,8 @@ private void OnSceneGUI() public override void OnInspectorGUI() { + serializedObject.Update(); + if (source.Nodes.Count == 0) selectedId = -1; @@ -60,12 +65,17 @@ public override void OnInspectorGUI() source.closeLoop = EditorGUILayout.Toggle("Close Loop", source.closeLoop); source.showUpVector = EditorGUILayout.Toggle("Show Orientation", source.showUpVector); + + EditorGUILayout.PropertyField(handleSize, new GUIContent("Handle Size")); + EditorGUILayout.LabelField(string.Format("Path Length: {0}", source.PathDistance)); if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(source); } + + serializedObject.ApplyModifiedProperties(); } void DrawNodeInspector(int id) @@ -94,18 +104,18 @@ void DrawBezierControl(int id) case SelectedNode.Main: //show position handle for main node, show button for tangent nodes source.Nodes[id].localPos = WorldToLocal(Handles.PositionHandle(LocalToWorld(source.Nodes[id].localPos), Quaternion.identity)); - DrawNodeButton(source.Nodes[id].leftHandle, SelectedNode.LeftTangent, 0.1f, Color.red); - DrawNodeButton(source.Nodes[id].rightHandle, SelectedNode.RightTangent, 0.1f, Color.red); + DrawNodeButton(source.Nodes[id].leftHandle, SelectedNode.LeftTangent, handleSize.floatValue * 0.6f, Color.red); + DrawNodeButton(source.Nodes[id].rightHandle, SelectedNode.RightTangent, handleSize.floatValue * 0.6f, Color.red); break; case SelectedNode.LeftTangent: source.Nodes[id].leftHandle = WorldToLocal(Handles.PositionHandle(LocalToWorld(source.Nodes[id].leftHandle), Quaternion.identity)); - DrawNodeButton(source.Nodes[id].localPos, SelectedNode.Main, 0.2f, Color.green); - DrawNodeButton(source.Nodes[id].rightHandle, SelectedNode.RightTangent, 0.1f, Color.red); + DrawNodeButton(source.Nodes[id].localPos, SelectedNode.Main, handleSize.floatValue, Color.green); + DrawNodeButton(source.Nodes[id].rightHandle, SelectedNode.RightTangent, handleSize.floatValue * 0.6f, Color.red); break; case SelectedNode.RightTangent: source.Nodes[id].rightHandle = WorldToLocal(Handles.PositionHandle(LocalToWorld(source.Nodes[id].rightHandle), Quaternion.identity)); - DrawNodeButton(source.Nodes[id].localPos, SelectedNode.Main, 0.2f, Color.green); - DrawNodeButton(source.Nodes[id].leftHandle, SelectedNode.LeftTangent, 0.1f, Color.red); + DrawNodeButton(source.Nodes[id].localPos, SelectedNode.Main, handleSize.floatValue, Color.green); + DrawNodeButton(source.Nodes[id].leftHandle, SelectedNode.LeftTangent, handleSize.floatValue * 0.6f, Color.red); break; } diff --git a/Assets/PathTools/Scripts/MoveAlongPath.cs b/Assets/PathTools/Scripts/MoveAlongPath.cs index 1ebbfd6..c106147 100644 --- a/Assets/PathTools/Scripts/MoveAlongPath.cs +++ b/Assets/PathTools/Scripts/MoveAlongPath.cs @@ -16,10 +16,14 @@ public class MoveAlongPath : MonoBehaviour [Header("Debug")] [SerializeField] float distance; + [SerializeField] float pathLength; private float runtimeDistance; private float speedDirection = 1f; + //only for loop mode stop, to stop update from running + private bool arrived; + private void Start() { runtimeDistance = 0f; @@ -28,6 +32,9 @@ private void Start() // Update is called once per frame void Update() { + if (arrived) + return; + runtimeDistance += speed * speedDirection * Time.deltaTime; if (loopMode == LoopMode.PingPong) @@ -39,13 +46,19 @@ void Update() } else if (loopMode == LoopMode.Stop) { - runtimeDistance = Mathf.Clamp(runtimeDistance, 0f, path.PathDistance); + var adjustedDistance = path.PathDistance * 0.999f; + + runtimeDistance = Mathf.Clamp(runtimeDistance, 0f, adjustedDistance); + + if (runtimeDistance >= adjustedDistance) + arrived = true; } else if (loopMode == LoopMode.Loop) { runtimeDistance %= path.PathDistance; } + Debug.Log(runtimeDistance); transform.position = path.GetPositionAtDistance(runtimeDistance); Quaternion targetRot = path.GetRotationAtDistance(runtimeDistance, useCustomUpVector ? customUpVector : path.GetUpVectorAtDistance(runtimeDistance)); transform.rotation = Quaternion.Lerp(transform.rotation, targetRot, rotationSpeed * Time.deltaTime); @@ -60,6 +73,8 @@ private void OnValidate() transform.position = path.GetPositionAtDistance(distance); Quaternion targetRot = path.GetRotationAtDistance(distance, path.GetUpVectorAtDistance(distance)); transform.rotation = targetRot; + + pathLength = path.PathDistance; } #endif } diff --git a/Assets/PathTools/Scripts/PathScript.cs b/Assets/PathTools/Scripts/PathScript.cs index c819cc2..3f93923 100644 --- a/Assets/PathTools/Scripts/PathScript.cs +++ b/Assets/PathTools/Scripts/PathScript.cs @@ -9,6 +9,7 @@ public class PathScript : MonoBehaviour #region VARIABLES [SerializeField] private List nodes = new List(); [SerializeField] private int selectedId; + [SerializeField] private float handleMulti = 0.2f; public bool closeLoop, showUpVector; private const int CURVE_SEGMENT = 20; @@ -53,11 +54,8 @@ List GetCurveNodes() Vector3 p1 = (nodes[i].rightHandle); Vector3 p2 = (nodes[i + 1].leftHandle); Vector3 p3 = (nodes[i + 1].localPos); - for (int j = 0; j < CURVE_SEGMENT; j++) - { - float t = j / (float)CURVE_SEGMENT; - curvedNodes.Add(CalculateBezierPath(p0, p1, p2, p3, t)); - } + + Interpolate(ref curvedNodes, p0, p1, p2, p3, i); } if (closeLoop) @@ -69,10 +67,21 @@ List GetCurveNodes() Vector3 p2 = (nodes[0].leftHandle); Vector3 p3 = (nodes[0].localPos); - for (int j = 0; j < CURVE_SEGMENT; j++) + Interpolate(ref curvedNodes, p0, p1, p2, p3, id); + } + + void Interpolate(ref List refNode, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, int i) + { + int start = !closeLoop ? (i == 0 ? 0 : 1) : 0; + int endOffset = closeLoop ? -1 : 0; + + for (int j = start; j <= CURVE_SEGMENT + endOffset; j++) { float t = j / (float)CURVE_SEGMENT; - curvedNodes.Add(CalculateBezierPath(p0, p1, p2, p3, t)); + + var point = CalculateBezierPath(p0, p1, p2, p3, t); + + refNode.Add(point); } } @@ -81,31 +90,36 @@ List GetCurveNodes() List GetOrientationAlongCurve() { - List orienationNodes = new List(); + List orientationNodes = new List(); for (int i = 0; i < nodes.Count - 1; i++) { - for (int j = 0; j < CURVE_SEGMENT; j++) - { - float t = j / (float)CURVE_SEGMENT; - - orienationNodes.Add(Mathf.Lerp(nodes[i].orientation, nodes[i + 1].orientation, t)); - } + Interpolate(ref orientationNodes, nodes, i, i + 1); } if (closeLoop) { int id = nodes.Count - 1; - for (int j = 0; j < CURVE_SEGMENT; j++) + Interpolate(ref orientationNodes, nodes, id, 0); + } + + void Interpolate(ref List refOrientationNodes, List _nodes, int i, int next) + { + int start = !closeLoop ? (i == 0 ? 0 : 1) : 0; + int endOffset = closeLoop ? -1 : 0; + + for (int j = start; j <= CURVE_SEGMENT + endOffset; j++) { float t = j / (float)CURVE_SEGMENT; - orienationNodes.Add(Mathf.Lerp(nodes[id].orientation, nodes[0].orientation, t)); + var value = Mathf.Lerp(_nodes[i].orientation, _nodes[next].orientation, t); + + refOrientationNodes.Add(value); } } - return orienationNodes; + return orientationNodes; } Vector3 LocalToWorld(Vector3 localPos) @@ -135,6 +149,8 @@ private void GetPrecisePoint(float distance, int count, out int posIndex, out fl //extract the decimals from the resulting index precision = distanceToIndex - posIndex; + + //Debug.Log($"index: {posIndex}, precision {precision}"); } #endregion @@ -174,14 +190,15 @@ public Vector3 GetPositionAtDistance(float distance) bool lastPosInList = posIndex == curvedPositions.Count - 1; //define the next index - int nextId = lastPosInList ? 0 : posIndex + 1; + int nextId = lastPosInList ? (!closeLoop ? posIndex : 0) : posIndex + 1; if (lastPosInList && !closeLoop) - precision = 1f; + precision = 0f; //get the precise position on curve at distance pos = transform.TransformPoint(Vector3.Lerp(curvedPositions[posIndex], curvedPositions[nextId], precision)); + //Debug.Log($"index: {posIndex}, precision {precision}"); return pos; } @@ -191,9 +208,13 @@ public Quaternion GetRotationAtDistance(float distance, Vector3 up) GetPrecisePoint(distance, curvedPositions.Count, out int posIndex, out float precision); - int nextId = posIndex == curvedPositions.Count - 1 ? 0 : posIndex + 1; + //int nextId = posIndex == curvedPositions.Count - 1 ? 0 : posIndex + 1; + int nextId = posIndex == 0 ? (closeLoop ? curvedPositions.Count - 1 : 1) : posIndex - 1; - quat = Quaternion.LookRotation(curvedPositions[nextId] - curvedPositions[posIndex], up); + if (!closeLoop && posIndex == 0) + quat = Quaternion.LookRotation(curvedPositions[posIndex] - curvedPositions[nextId], up); + else + quat = Quaternion.LookRotation(curvedPositions[nextId] - curvedPositions[posIndex], up); return quat; } @@ -206,9 +227,17 @@ public Quaternion GetRotationAtDistance(float distance) public Vector3 GetUpVectorAtDistance(float distance) { GetPrecisePoint(distance, curvedPositions.Count, out int posIndex, out float precision); + + Vector3 direction; + + int nextId = posIndex == 0 ? (closeLoop ? curvedPositions.Count - 1 : 1) : posIndex - 1; //draw point up with orientation influence - Vector3 direction = (curvedPositions[posIndex] - curvedPositions[posIndex == 0 ? (curvedPositions.Count - 1) : (posIndex - 1)]).normalized; + if (!closeLoop && posIndex == 0) + direction = (curvedPositions[nextId] - curvedPositions[posIndex]).normalized; + else + direction = (curvedPositions[posIndex] - curvedPositions[nextId]).normalized; + Vector3 finalDirection = Quaternion.AngleAxis(orientations[posIndex], direction) * Vector3.up; return finalDirection; @@ -241,10 +270,20 @@ private void OnDrawGizmos() for (int i = 0; i < orientations.Count; i++) { //draw point up with orientation influence - Vector3 direction = (curvedPositions[i] - curvedPositions[i == 0 ? (curvedPositions.Count - 1) : (i - 1)]).normalized; + int nextId = i == 0 ? (closeLoop ? curvedPositions.Count - 1 : 1) : i - 1; + Vector3 direction; + + //draw point up with orientation influence + if (!closeLoop && i == 0) + direction = (curvedPositions[nextId] - curvedPositions[i]).normalized; + else + direction = (curvedPositions[i] - curvedPositions[nextId]).normalized; + Vector3 finalDirection = Quaternion.AngleAxis(orientations[i], direction) * Vector3.up; + var worldPos = LocalToWorld(curvedPositions[i]); Gizmos.color = Color.red; - Gizmos.DrawLine(LocalToWorld(curvedPositions[i]), LocalToWorld(curvedPositions[i]) + (finalDirection * 0.4f)); + Gizmos.DrawLine(worldPos, worldPos + (finalDirection * 0.4f)); + //UnityEditor.Handles.Label(worldPos + (finalDirection * 0.5f), i.ToString()); } } }