123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- using UnityEngine;
- using System.Collections.Generic;
- using Pathfinding.Util;
- namespace Pathfinding {
- [AddComponentMenu("Pathfinding/Modifiers/Simple Smooth")]
- [System.Serializable]
- [RequireComponent(typeof(Seeker))]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [HelpURL("http://arongranberg.com/astar/documentation/stable/class_pathfinding_1_1_simple_smooth_modifier.php")]
- public class SimpleSmoothModifier : MonoModifier {
- #if UNITY_EDITOR
- [UnityEditor.MenuItem("CONTEXT/Seeker/Add Simple Smooth Modifier")]
- public static void AddComp (UnityEditor.MenuCommand command) {
- (command.context as Component).gameObject.AddComponent(typeof(SimpleSmoothModifier));
- }
- #endif
- public override int Order { get { return 50; } }
-
- public SmoothType smoothType = SmoothType.Simple;
-
- [Tooltip("The number of times to subdivide (divide in half) the path segments. [0...inf] (recommended [1...10])")]
- public int subdivisions = 2;
-
- [Tooltip("Number of times to apply smoothing")]
- public int iterations = 2;
-
- [Tooltip("Determines how much smoothing to apply in each smooth iteration. 0.5 usually produces the nicest looking curves")]
- [Range(0, 1)]
- public float strength = 0.5F;
-
-
-
-
- [Tooltip("Toggle to divide all lines in equal length segments")]
- public bool uniformLength = true;
-
-
-
-
- [Tooltip("The length of each segment in the smoothed path. A high value yields rough paths and low value yields very smooth paths, but is slower")]
- public float maxSegmentLength = 2F;
-
- [Tooltip("Length factor of the bezier curves' tangents")]
- public float bezierTangentLength = 0.4F;
-
- [Tooltip("Offset to apply in each smoothing iteration when using Offset Simple")]
- public float offset = 0.2F;
-
- [Tooltip("How much to smooth the path. A higher value will give a smoother path, but might take the character far off the optimal path.")]
- public float factor = 0.1F;
- public enum SmoothType {
- Simple,
- Bezier,
- OffsetSimple,
- CurvedNonuniform
- }
- public override void Apply (Path p) {
-
- if (p.vectorPath == null) {
- Debug.LogWarning("Can't process NULL path (has another modifier logged an error?)");
- return;
- }
- List<Vector3> path = null;
- switch (smoothType) {
- case SmoothType.Simple:
- path = SmoothSimple(p.vectorPath); break;
- case SmoothType.Bezier:
- path = SmoothBezier(p.vectorPath); break;
- case SmoothType.OffsetSimple:
- path = SmoothOffsetSimple(p.vectorPath); break;
- case SmoothType.CurvedNonuniform:
- path = CurvedNonuniform(p.vectorPath); break;
- }
- if (path != p.vectorPath) {
- ListPool<Vector3>.Release(ref p.vectorPath);
- p.vectorPath = path;
- }
- }
- public List<Vector3> CurvedNonuniform (List<Vector3> path) {
- if (maxSegmentLength <= 0) {
- Debug.LogWarning("Max Segment Length is <= 0 which would cause DivByZero-exception or other nasty errors (avoid this)");
- return path;
- }
- int pointCounter = 0;
- for (int i = 0; i < path.Count-1; i++) {
-
- float dist = (path[i]-path[i+1]).magnitude;
-
-
- for (float t = 0; t <= dist; t += maxSegmentLength) {
- pointCounter++;
- }
- }
- List<Vector3> subdivided = ListPool<Vector3>.Claim(pointCounter);
-
- Vector3 preEndVel = (path[1]-path[0]).normalized;
- for (int i = 0; i < path.Count-1; i++) {
- float dist = (path[i]-path[i+1]).magnitude;
- Vector3 startVel1 = preEndVel;
- Vector3 endVel1 = i < path.Count-2 ? ((path[i+2]-path[i+1]).normalized - (path[i]-path[i+1]).normalized).normalized : (path[i+1]-path[i]).normalized;
- Vector3 startVel = startVel1 * dist * factor;
- Vector3 endVel = endVel1 * dist * factor;
- Vector3 start = path[i];
- Vector3 end = path[i+1];
- float onedivdist = 1F / dist;
- for (float t = 0; t <= dist; t += maxSegmentLength) {
- float t2 = t * onedivdist;
- subdivided.Add(GetPointOnCubic(start, end, startVel, endVel, t2));
- }
- preEndVel = endVel1;
- }
- subdivided[subdivided.Count-1] = path[path.Count-1];
- return subdivided;
- }
- public static Vector3 GetPointOnCubic (Vector3 a, Vector3 b, Vector3 tan1, Vector3 tan2, float t) {
- float t2 = t*t, t3 = t2*t;
- float h1 = 2*t3 - 3*t2 + 1;
- float h2 = -2*t3 + 3*t2;
- float h3 = t3 - 2*t2 + t;
- float h4 = t3 - t2;
- return h1*a +
- h2*b +
- h3*tan1 +
- h4*tan2;
- }
- public List<Vector3> SmoothOffsetSimple (List<Vector3> path) {
- if (path.Count <= 2 || iterations <= 0) {
- return path;
- }
- if (iterations > 12) {
- Debug.LogWarning("A very high iteration count was passed, won't let this one through");
- return path;
- }
- int maxLength = (path.Count-2)*(int)Mathf.Pow(2, iterations)+2;
- List<Vector3> subdivided = ListPool<Vector3>.Claim(maxLength);
- List<Vector3> subdivided2 = ListPool<Vector3>.Claim(maxLength);
- for (int i = 0; i < maxLength; i++) { subdivided.Add(Vector3.zero); subdivided2.Add(Vector3.zero); }
- for (int i = 0; i < path.Count; i++) {
- subdivided[i] = path[i];
- }
- for (int iteration = 0; iteration < iterations; iteration++) {
- int currentPathLength = (path.Count-2)*(int)Mathf.Pow(2, iteration)+2;
-
- List<Vector3> tmp = subdivided;
- subdivided = subdivided2;
- subdivided2 = tmp;
- const float nextMultiplier = 1F;
- for (int i = 0; i < currentPathLength-1; i++) {
- Vector3 current = subdivided2[i];
- Vector3 next = subdivided2[i+1];
- Vector3 normal = Vector3.Cross(next-current, Vector3.up);
- normal = normal.normalized;
- bool firstRight = false;
- bool secondRight = false;
- bool setFirst = false;
- bool setSecond = false;
- if (i != 0 && !VectorMath.IsColinearXZ(current, next, subdivided2[i-1])) {
- setFirst = true;
- firstRight = VectorMath.RightOrColinearXZ(current, next, subdivided2[i-1]);
- }
- if (i < currentPathLength-1 && !VectorMath.IsColinearXZ(current, next, subdivided2[i+2])) {
- setSecond = true;
- secondRight = VectorMath.RightOrColinearXZ(current, next, subdivided2[i+2]);
- }
- if (setFirst) {
- subdivided[i*2] = current + (firstRight ? normal*offset*nextMultiplier : -normal*offset*nextMultiplier);
- } else {
- subdivided[i*2] = current;
- }
- if (setSecond) {
- subdivided[i*2+1] = next + (secondRight ? normal*offset*nextMultiplier : -normal*offset*nextMultiplier);
- } else {
- subdivided[i*2+1] = next;
- }
- }
- subdivided[(path.Count-2)*(int)Mathf.Pow(2, iteration+1)+2-1] = subdivided2[currentPathLength-1];
- }
- ListPool<Vector3>.Release(ref subdivided2);
- return subdivided;
- }
- public List<Vector3> SmoothSimple (List<Vector3> path) {
- if (path.Count < 2) return path;
- List<Vector3> subdivided;
- if (uniformLength) {
-
- maxSegmentLength = Mathf.Max(maxSegmentLength, 0.005f);
- float pathLength = 0;
- for (int i = 0; i < path.Count-1; i++) {
- pathLength += Vector3.Distance(path[i], path[i+1]);
- }
- int estimatedNumberOfSegments = Mathf.FloorToInt(pathLength / maxSegmentLength);
-
- subdivided = ListPool<Vector3>.Claim(estimatedNumberOfSegments+2);
- float distanceAlong = 0;
-
- for (int i = 0; i < path.Count-1; i++) {
- var start = path[i];
- var end = path[i+1];
- float length = Vector3.Distance(start, end);
- while (distanceAlong < length) {
- subdivided.Add(Vector3.Lerp(start, end, distanceAlong / length));
- distanceAlong += maxSegmentLength;
- }
- distanceAlong -= length;
- }
-
- subdivided.Add(path[path.Count-1]);
- } else {
- subdivisions = Mathf.Max(subdivisions, 0);
- if (subdivisions > 10) {
- Debug.LogWarning("Very large number of subdivisions. Cowardly refusing to subdivide every segment into more than " + (1 << subdivisions) + " subsegments");
- subdivisions = 10;
- }
- int steps = 1 << subdivisions;
- subdivided = ListPool<Vector3>.Claim((path.Count-1)*steps + 1);
- Polygon.Subdivide(path, subdivided, steps);
- }
- if (strength > 0) {
- for (int it = 0; it < iterations; it++) {
- Vector3 prev = subdivided[0];
- for (int i = 1; i < subdivided.Count-1; i++) {
- Vector3 tmp = subdivided[i];
-
-
- subdivided[i] = Vector3.Lerp(tmp, (prev+subdivided[i+1])/2F, strength);
- prev = tmp;
- }
- }
- }
- return subdivided;
- }
- public List<Vector3> SmoothBezier (List<Vector3> path) {
- if (subdivisions < 0) subdivisions = 0;
- int subMult = 1 << subdivisions;
- List<Vector3> subdivided = ListPool<Vector3>.Claim();
- for (int i = 0; i < path.Count-1; i++) {
- Vector3 tangent1;
- Vector3 tangent2;
- if (i == 0) {
- tangent1 = path[i+1]-path[i];
- } else {
- tangent1 = path[i+1]-path[i-1];
- }
- if (i == path.Count-2) {
- tangent2 = path[i]-path[i+1];
- } else {
- tangent2 = path[i]-path[i+2];
- }
- tangent1 *= bezierTangentLength;
- tangent2 *= bezierTangentLength;
- Vector3 v1 = path[i];
- Vector3 v2 = v1+tangent1;
- Vector3 v4 = path[i+1];
- Vector3 v3 = v4+tangent2;
- for (int j = 0; j < subMult; j++) {
- subdivided.Add(AstarSplines.CubicBezier(v1, v2, v3, v4, (float)j/subMult));
- }
- }
-
- subdivided.Add(path[path.Count-1]);
- return subdivided;
- }
- }
- }
|