/* This file is part of the "Simple Waypoint System" project by Rebound Games. * You are only allowed to use these resources if you've bought them from the Unity Asset Store. * You shall not license, sublicense, sell, resell, transfer, assign, distribute or * otherwise make available to any third party the Service or the Content. */ using UnityEngine; using System.Collections.Generic; using System.Linq; namespace SWS { /// /// Stores waypoints of a bezier path, accessed by walker objects. /// Provides gizmo visualization in the editor. /// public class BezierPathManager : PathManager { /// /// Path points (not waypoints) creating the path. /// public Vector3[] pathPoints = new Vector3[]{}; /// /// List to store bezier points for this path. /// public List bPoints = new List(); /// /// Toggles drawing of control point handles. /// public bool showHandles = true; /// /// Toggles independent control point handles on/off. /// public bool connectHandles = true; /// /// Gizmo color for control point handles. /// public Color color3 = new Color(108 / 255f, 151 / 255f, 1, 1); /// /// Detail value for interpolations when calculating path points. /// public float pathDetail = 1; /// /// Toggles custom detail for single path segments. /// public bool customDetail = false; /// /// List of detail values for single path segments, when enabled. /// public List segmentDetail = new List(); //recalculate path points void Awake() { WaypointManager.AddPath(gameObject); //do not recalculate automatically with runtime created paths if (bPoints == null || bPoints.Count == 0) return; CalculatePath(); } /// /// Create or update waypoint representation from the array passed in, optionally parenting them to the path. /// public override void Create(Transform[] waypoints, bool makeChildren = false) { if (waypoints.Length < 2) { Debug.LogWarning("Not enough waypoints placed - minimum is 2. Cancelling."); return; } if (makeChildren) { for (int i = 0; i < waypoints.Length; i++) waypoints[i].parent = transform; } bPoints.Clear(); for(int i = 0; i < waypoints.Length; i++) { BezierPoint point = new BezierPoint(); point.wp = waypoints[i]; point.cp = new Transform[2]; point.cp[0] = point.wp.GetChild(0); point.cp[1] = point.wp.GetChild(1); bPoints.Add(point); } //recalculate after modification CalculatePath(); } //editor visualization void OnDrawGizmos() { if (bPoints.Count <= 0) return; //assign path ends color Vector3 start = bPoints[0].wp.position; Vector3 end = bPoints[bPoints.Count - 1].wp.position; Gizmos.color = color1; Gizmos.DrawWireCube(start, size * GetHandleSize(start) * 1.5f); Gizmos.DrawWireCube(end, size * GetHandleSize(end) * 1.5f); //assign line and waypoints color Gizmos.color = color2; for (int i = 1; i < bPoints.Count - 1; i++) Gizmos.DrawWireSphere(bPoints[i].wp.position, radius * GetHandleSize(bPoints[i].wp.position)); //draw linear or curved lines with the same color if (drawCurved && bPoints.Count >= 2) WaypointManager.DrawCurved(pathPoints); else WaypointManager.DrawStraight(pathPoints); } /// /// Returns waypoint positions (path positions) as Vector3 array. /// public override Vector3[] GetPathPoints(bool local = false) { if(local) { Vector3[] localPathPoints = new Vector3[pathPoints.Length]; for (int i = 0; i < localPathPoints.Length; i++) localPathPoints[i] = transform.InverseTransformPoint(pathPoints[i]); return localPathPoints; } return pathPoints; } /// /// Returns waypoint length (should be equal to events count). /// public override int GetWaypointCount() { return bPoints.Count; } /// /// Returns the bezier path waypoint transform according to the index passed in. /// public override Transform GetWaypoint(int index) { return bPoints[index].wp; } /// /// Converts bezier points on the path to waypoint index. /// Returns -1 if the point lies in between two waypoints. /// public override int GetWaypointIndex(int point) { int index = -1; int summedPoints = 0; int defaultPoints = 10; for(int i = 0; i < segmentDetail.Count; i++) { if(point == summedPoints) { index = i; break; } if (customDetail) summedPoints += Mathf.CeilToInt(segmentDetail[i] * defaultPoints); else summedPoints += Mathf.CeilToInt(pathDetail * defaultPoints); } return index; } /// /// Recalculates final path points. Can be called at runtime to allow repositioning, /// but do consider update rate vs. performance! /// public void CalculatePath() { //temporary list for final points List temp = new List(); //loop over bezier points (segments) for (int i = 0; i < bPoints.Count - 1; i++) { BezierPoint bp = bPoints[i]; //get path detail value (default or custom) float detail = pathDetail; if (customDetail) detail = segmentDetail[i]; //calculate path points on this segment. Parameters: //current waypoint position, current right handle position, //next left handle position, next waypoint position, path detail temp.AddRange(GetPoints(bp.wp.position, bp.cp[1].position, bPoints[i + 1].cp[0].position, bPoints[i + 1].wp.position, detail)); } //remove duplicates after calculation pathPoints = temp.Distinct().ToArray(); } //returns all path points on a segment, see CalculatePath() for parameters private List GetPoints(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float detail) { //temporary list for final points on this segment List segmentPoints = new List(); //multiply detail value to have at least 5-10+ iterations float iterations = detail * 10f; for (int n = 0; n <= iterations; n++) { //cannot increment i as a float float i = (float)n / iterations; float rest = (1f - i); //bezier formula Vector3 newPos = Vector3.zero; newPos += p0 * rest * rest * rest; newPos += p1 * i * 3f * rest * rest; newPos += p2 * 3f * i * i * rest; newPos += p3 * i * i * i; //add calculated point to segment segmentPoints.Add(newPos); } //return points on this segment return segmentPoints; } } /// /// Custom class to store waypoint transforms and control points for bezier paths. /// [System.Serializable] public class BezierPoint { /// /// Waypoint transform for this bezier point. /// public Transform wp = null; /// /// Control points for this bezier point. /// [0] = first point (left), [1] = second point (right) /// public Transform[] cp = new Transform[2]; } }