/// <summary>Traverses an off-mesh link</summary> protected virtual IEnumerator TraverseSpecial(RichSpecial link) { traversingOffMeshLink = true; // The current path part is a special part, for example a link // Movement during this part of the path is handled by the TraverseSpecial coroutine velocity2D = Vector3.zero; var offMeshLinkCoroutine = onTraverseOffMeshLink != null?onTraverseOffMeshLink(link) : TraverseOffMeshLinkFallback(link); yield return(StartCoroutine(offMeshLinkCoroutine)); // Off-mesh link traversal completed traversingOffMeshLink = false; NextPart(); // If a path completed during the time we traversed the special connection, we need to recalculate it if (delayUpdatePath) { delayUpdatePath = false; // TODO: What if canSearch is false? How do we notify other scripts that might be handling the path calculation that a new path needs to be calculated? if (canSearch) { SearchPath(); } } }
protected virtual IEnumerator TraverseSpecial(RichSpecial rs) { traversingSpecialPath = true; velocity2D = Vector3.zero; var link = rs.nodeLink as AnimationLink; if (link == null) { Debug.LogError("Unhandled RichSpecial"); yield break; } // Rotate character to face the correct direction // Magic number, should expose as variable while (Vector2.Angle(movementPlane.ToPlane(tr.forward), movementPlane.ToPlane(rs.first.forward)) > 5f) { RotateTowards(movementPlane.ToPlane(rs.first.forward), rotationSpeed * Time.deltaTime); yield return(null); } // Reposition tr.parent.position = tr.position; tr.parent.rotation = tr.rotation; tr.localPosition = Vector3.zero; tr.localRotation = Quaternion.identity; // Set up animation speeds if (rs.reverse && link.reverseAnim) { anim[link.clip].speed = -link.animSpeed; anim[link.clip].normalizedTime = 1; anim.Play(link.clip); anim.Sample(); } else { anim[link.clip].speed = link.animSpeed; anim.Rewind(link.clip); anim.Play(link.clip); } // Fix required for animations in reverse direction tr.parent.position -= tr.position - tr.parent.position; // Wait for the animation to finish yield return(new WaitForSeconds(Mathf.Abs(anim[link.clip].length / link.animSpeed))); traversingSpecialPath = false; NextPart(); // If a path completed during the time we traversed the special connection, we need to recalculate it if (delayUpdatePath) { delayUpdatePath = false; UpdatePath(); } }
IEnumerator TraverseSpecial(RichSpecial rs) { traversingSpecialPath = true; velocity = Vector3.zero; var al = rs.nodeLink as AnimationLink; if (al == null) { Debug.LogError("Unhandled RichSpecial"); yield break; } //Rotate character to face the correct direction while (!RotateTowards(rs.first.forward)) { yield return(null); } //Reposition tr.parent.position = tr.position; tr.parent.rotation = tr.rotation; tr.localPosition = Vector3.zero; tr.localRotation = Quaternion.identity; //Set up animation speeds if (rs.reverse && al.reverseAnim) { anim[al.clip].speed = -al.animSpeed; anim[al.clip].normalizedTime = 1; anim.Play(al.clip); anim.Sample(); } else { anim[al.clip].speed = al.animSpeed; anim.Rewind(al.clip); anim.Play(al.clip); } //Fix required for animations in reverse direction tr.parent.position -= tr.position - tr.parent.position; //Wait for the animation to finish yield return(new WaitForSeconds(Mathf.Abs(anim[al.clip].length / al.animSpeed))); traversingSpecialPath = false; NextPart(); //If a path completed during the time we traversed the special connection, we need to recalculate it if (delayUpdatePath) { delayUpdatePath = false; UpdatePath(); } }
protected virtual IEnumerator TraverseSpecial(RichSpecial rs) { this.traversingSpecialPath = true; this.velocity2D = Vector3.zero; AnimationLink link = rs.nodeLink as AnimationLink; if (link == null) { Debug.LogError("Unhandled RichSpecial"); yield break; } while (Vector2.Angle(this.movementPlane.ToPlane(this.rotationIn2D ? this.tr.up : this.tr.forward), this.movementPlane.ToPlane(rs.first.forward)) > 5f) { this.RotateTowards(this.movementPlane.ToPlane(rs.first.forward), this.rotationSpeed * Time.deltaTime); yield return(null); } this.tr.parent.position = this.tr.position; this.tr.parent.rotation = this.tr.rotation; this.tr.localPosition = Vector3.zero; this.tr.localRotation = Quaternion.identity; if (rs.reverse && link.reverseAnim) { this.anim[link.clip].speed = -link.animSpeed; this.anim[link.clip].normalizedTime = 1f; this.anim.Play(link.clip); this.anim.Sample(); } else { this.anim[link.clip].speed = link.animSpeed; this.anim.Rewind(link.clip); this.anim.Play(link.clip); } this.tr.parent.position -= this.tr.position - this.tr.parent.position; yield return(new WaitForSeconds(Mathf.Abs(this.anim[link.clip].length / link.animSpeed))); this.traversingSpecialPath = false; this.NextPart(); if (this.delayUpdatePath) { this.delayUpdatePath = false; this.UpdatePath(); } yield break; yield break; }
private IEnumerator TraverseSpecial(RichSpecial rs) { this.traversingSpecialPath = true; this.velocity = Vector3.zero; AnimationLink al = rs.nodeLink as AnimationLink; if (al == null) { Debug.LogError("Unhandled RichSpecial"); yield break; } while (!this.RotateTowards(rs.first.forward)) { yield return(null); } this.tr.parent.position = this.tr.position; this.tr.parent.rotation = this.tr.rotation; this.tr.localPosition = Vector3.zero; this.tr.localRotation = Quaternion.identity; if (rs.reverse && al.reverseAnim) { this.anim[al.clip].speed = -al.animSpeed; this.anim[al.clip].normalizedTime = 1f; this.anim.Play(al.clip); this.anim.Sample(); } else { this.anim[al.clip].speed = al.animSpeed; this.anim.Rewind(al.clip); this.anim.Play(al.clip); } this.tr.parent.position -= this.tr.position - this.tr.parent.position; yield return(new WaitForSeconds(Mathf.Abs(this.anim[al.clip].length / al.animSpeed))); this.traversingSpecialPath = false; this.NextPart(); if (this.delayUpdatePath) { this.delayUpdatePath = false; this.UpdatePath(); } yield break; }
// Token: 0x06002180 RID: 8576 RVA: 0x0018EA3B File Offset: 0x0018CC3B protected virtual IEnumerator TraverseSpecial(RichSpecial link) { this.traversingOffMeshLink = true; this.velocity2D = Vector3.zero; IEnumerator routine = (this.onTraverseOffMeshLink != null) ? this.onTraverseOffMeshLink(link) : this.TraverseOffMeshLinkFallback(link); yield return(base.StartCoroutine(routine)); this.traversingOffMeshLink = false; this.NextPart(); if (this.delayUpdatePath) { this.delayUpdatePath = false; if (this.canSearch) { this.SearchPath(); } } yield break; }
// Token: 0x06002181 RID: 8577 RVA: 0x0018EA51 File Offset: 0x0018CC51 protected IEnumerator TraverseOffMeshLinkFallback(RichSpecial link) { float duration = (this.maxSpeed > 0f) ? (Vector3.Distance(link.second.position, link.first.position) / this.maxSpeed) : 1f; float startTime = Time.time; for (;;) { Vector3 vector = Vector3.Lerp(link.first.position, link.second.position, Mathf.InverseLerp(startTime, startTime + duration, Time.time)); if (this.updatePosition) { this.tr.position = vector; } else { this.simulatedPosition = vector; } if (Time.time >= startTime + duration) { break; } yield return(null); } yield break; }
/// <summary> /// Fallback for traversing off-mesh links in case <see cref="onTraverseOffMeshLink"/> is not set. /// This will do a simple linear interpolation along the link. /// </summary> protected IEnumerator TraverseOffMeshLinkFallback(RichSpecial link) { float duration = maxSpeed > 0 ? Vector3.Distance(link.second.position, link.first.position) / maxSpeed : 1; float startTime = Time.time; while (true) { var pos = Vector3.Lerp(link.first.position, link.second.position, Mathf.InverseLerp(startTime, startTime + duration, Time.time)); if (updatePosition) { tr.position = pos; } else { simulatedPosition = pos; } if (Time.time >= startTime + duration) { break; } yield return(null); } }
/** Use this for initialization. * * \param s Optionally provide in order to take tag penalties into account. May be null if you do not use a Seeker\ * \param p Path to follow * \param mergePartEndpoints If true, then adjacent parts that the path is split up in will * try to use the same start/end points. For example when using a link on a navmesh graph * Instead of first following the path to the center of the node where the link is and then * follow the link, the path will be adjusted to go to the exact point where the link starts * which usually makes more sense. * \param simplificationMode The path can optionally be simplified. This can be a bit expensive for long paths. */ public void Initialize(Seeker s, Path p, bool mergePartEndpoints, RichFunnel.FunnelSimplification simplificationMode) { if (p.error) { throw new System.ArgumentException("Path has an error"); } List <GraphNode> nodes = p.path; if (nodes.Count == 0) { throw new System.ArgumentException("Path traverses no nodes"); } seeker = s; // Release objects back to object pool // Yeah, I know, it's casting... but this won't be called much for (int i = 0; i < parts.Count; i++) { if (parts[i] is RichFunnel) { ObjectPool <RichFunnel> .Release(parts[i] as RichFunnel); } else if (parts[i] is RichSpecial) { ObjectPool <RichSpecial> .Release(parts[i] as RichSpecial); } } parts.Clear(); currentPart = 0; // Initialize new //Break path into parts for (int i = 0; i < nodes.Count; i++) { if (nodes[i] is TriangleMeshNode) { var graph = AstarData.GetGraph(nodes[i]); RichFunnel f = ObjectPool <RichFunnel> .Claim().Initialize(this, graph); f.funnelSimplificationMode = simplificationMode; int sIndex = i; uint currentGraphIndex = nodes[sIndex].GraphIndex; for (; i < nodes.Count; i++) { if (nodes[i].GraphIndex != currentGraphIndex && !(nodes[i] is NodeLink3Node)) { break; } } i--; if (sIndex == 0) { f.exactStart = p.vectorPath[0]; } else { f.exactStart = (Vector3)nodes[mergePartEndpoints ? sIndex - 1 : sIndex].position; } if (i == nodes.Count - 1) { f.exactEnd = p.vectorPath[p.vectorPath.Count - 1]; } else { f.exactEnd = (Vector3)nodes[mergePartEndpoints ? i + 1 : i].position; } f.BuildFunnelCorridor(nodes, sIndex, i); parts.Add(f); } else if (NodeLink2.GetNodeLink(nodes[i]) != null) { NodeLink2 nl = NodeLink2.GetNodeLink(nodes[i]); int sIndex = i; uint currentGraphIndex = nodes[sIndex].GraphIndex; for (i++; i < nodes.Count; i++) { if (nodes[i].GraphIndex != currentGraphIndex) { break; } } i--; if (i - sIndex > 1) { throw new System.Exception("NodeLink2 path length greater than two (2) nodes. " + (i - sIndex)); } else if (i - sIndex == 0) { //Just continue, it might be the case that a NodeLink was the closest node continue; } RichSpecial rps = ObjectPool <RichSpecial> .Claim().Initialize(nl, nodes[sIndex]); parts.Add(rps); } } }
// Token: 0x06002140 RID: 8512 RVA: 0x001890A0 File Offset: 0x001872A0 public void Initialize(Seeker seeker, Path path, bool mergePartEndpoints, bool simplificationMode) { if (path.error) { throw new ArgumentException("Path has an error"); } List <GraphNode> path2 = path.path; if (path2.Count == 0) { throw new ArgumentException("Path traverses no nodes"); } this.seeker = seeker; for (int i = 0; i < this.parts.Count; i++) { RichFunnel richFunnel = this.parts[i] as RichFunnel; RichSpecial richSpecial = this.parts[i] as RichSpecial; if (richFunnel != null) { ObjectPool <RichFunnel> .Release(ref richFunnel); } else if (richSpecial != null) { ObjectPool <RichSpecial> .Release(ref richSpecial); } } this.Clear(); this.Endpoint = path.vectorPath[path.vectorPath.Count - 1]; for (int j = 0; j < path2.Count; j++) { if (path2[j] is TriangleMeshNode) { NavmeshBase navmeshBase = AstarData.GetGraph(path2[j]) as NavmeshBase; if (navmeshBase == null) { throw new Exception("Found a TriangleMeshNode that was not in a NavmeshBase graph"); } RichFunnel richFunnel2 = ObjectPool <RichFunnel> .Claim().Initialize(this, navmeshBase); richFunnel2.funnelSimplification = simplificationMode; int num = j; uint graphIndex = path2[num].GraphIndex; while (j < path2.Count && (path2[j].GraphIndex == graphIndex || path2[j] is NodeLink3Node)) { j++; } j--; if (num == 0) { richFunnel2.exactStart = path.vectorPath[0]; } else { richFunnel2.exactStart = (Vector3)path2[mergePartEndpoints ? (num - 1) : num].position; } if (j == path2.Count - 1) { richFunnel2.exactEnd = path.vectorPath[path.vectorPath.Count - 1]; } else { richFunnel2.exactEnd = (Vector3)path2[mergePartEndpoints ? (j + 1) : j].position; } richFunnel2.BuildFunnelCorridor(path2, num, j); this.parts.Add(richFunnel2); } else if (NodeLink2.GetNodeLink(path2[j]) != null) { NodeLink2 nodeLink = NodeLink2.GetNodeLink(path2[j]); int num2 = j; uint graphIndex2 = path2[num2].GraphIndex; j++; while (j < path2.Count && path2[j].GraphIndex == graphIndex2) { j++; } j--; if (j - num2 > 1) { throw new Exception("NodeLink2 path length greater than two (2) nodes. " + (j - num2)); } if (j - num2 != 0) { RichSpecial item = ObjectPool <RichSpecial> .Claim().Initialize(nodeLink, path2[num2]); this.parts.Add(item); } } } }
// Token: 0x06000033 RID: 51 RVA: 0x0000417C File Offset: 0x0000257C public void Initialize(Seeker s, Path p, bool mergePartEndpoints, RichFunnel.FunnelSimplification simplificationMode) { if (p.error) { throw new ArgumentException("Path has an error"); } List <GraphNode> path = p.path; if (path.Count == 0) { throw new ArgumentException("Path traverses no nodes"); } this.seeker = s; for (int i = 0; i < this.parts.Count; i++) { if (this.parts[i] is RichFunnel) { ObjectPool <RichFunnel> .Release(this.parts[i] as RichFunnel); } else if (this.parts[i] is RichSpecial) { ObjectPool <RichSpecial> .Release(this.parts[i] as RichSpecial); } } this.parts.Clear(); this.currentPart = 0; for (int j = 0; j < path.Count; j++) { if (path[j] is TriangleMeshNode) { IFunnelGraph graph = AstarData.GetGraph(path[j]) as IFunnelGraph; RichFunnel richFunnel = ObjectPool <RichFunnel> .Claim().Initialize(this, graph); richFunnel.funnelSimplificationMode = simplificationMode; int num = j; uint graphIndex = path[num].GraphIndex; while (j < path.Count) { if (path[j].GraphIndex != graphIndex && !(path[j] is NodeLink3Node)) { break; } j++; } j--; if (num == 0) { richFunnel.exactStart = p.vectorPath[0]; } else if (mergePartEndpoints) { richFunnel.exactStart = (Vector3)path[num - 1].position; } else { richFunnel.exactStart = (Vector3)path[num].position; } if (j == path.Count - 1) { richFunnel.exactEnd = p.vectorPath[p.vectorPath.Count - 1]; } else if (mergePartEndpoints) { richFunnel.exactEnd = (Vector3)path[j + 1].position; } else { richFunnel.exactEnd = (Vector3)path[j].position; } richFunnel.BuildFunnelCorridor(path, num, j); this.parts.Add(richFunnel); } else if (path[j] != null && NodeLink2.GetNodeLink(path[j]) != null) { NodeLink2 nodeLink = NodeLink2.GetNodeLink(path[j]); int num2 = j; uint graphIndex2 = path[num2].GraphIndex; for (j++; j < path.Count; j++) { if (path[j].GraphIndex != graphIndex2) { break; } } j--; if (j - num2 > 1) { throw new Exception("NodeLink2 path length greater than two (2) nodes. " + (j - num2)); } if (j - num2 != 0) { RichSpecial item = ObjectPool <RichSpecial> .Claim().Initialize(nodeLink, path[num2]); this.parts.Add(item); } } } }
protected virtual void Update() { RichAI.deltaTime = Mathf.Min(Time.smoothDeltaTime * 2f, Time.deltaTime); if (this.rp != null) { RichPathPart currentPart = this.rp.GetCurrentPart(); RichFunnel richFunnel = currentPart as RichFunnel; if (richFunnel != null) { Vector3 vector = this.UpdateTarget(richFunnel); if (Time.frameCount % 5 == 0) { this.wallBuffer.Clear(); richFunnel.FindWalls(this.wallBuffer, this.wallDist); } int num = 0; Vector3 vector2 = this.buffer[num]; Vector3 vector3 = vector2 - vector; vector3.y = 0f; bool flag = Vector3.Dot(vector3, this.currentTargetDirection) < 0f; if (flag && this.buffer.Count - num > 1) { num++; vector2 = this.buffer[num]; } if (vector2 != this.lastTargetPoint) { this.currentTargetDirection = vector2 - vector; this.currentTargetDirection.y = 0f; this.currentTargetDirection.Normalize(); this.lastTargetPoint = vector2; } vector3 = vector2 - vector; vector3.y = 0f; float magnitude = vector3.magnitude; this.distanceToWaypoint = magnitude; vector3 = ((magnitude != 0f) ? (vector3 / magnitude) : Vector3.zero); Vector3 vector4 = vector3; Vector3 vector5 = Vector3.zero; if (this.wallForce > 0f && this.wallDist > 0f) { float num2 = 0f; float num3 = 0f; for (int i = 0; i < this.wallBuffer.Count; i += 2) { Vector3 vector6 = AstarMath.NearestPointStrict(this.wallBuffer[i], this.wallBuffer[i + 1], this.tr.position); float sqrMagnitude = (vector6 - vector).sqrMagnitude; if (sqrMagnitude <= this.wallDist * this.wallDist) { Vector3 normalized = (this.wallBuffer[i + 1] - this.wallBuffer[i]).normalized; float num4 = Vector3.Dot(vector3, normalized) * (1f - Math.Max(0f, 2f * (sqrMagnitude / (this.wallDist * this.wallDist)) - 1f)); if (num4 > 0f) { num3 = Math.Max(num3, num4); } else { num2 = Math.Max(num2, -num4); } } } Vector3 vector7 = Vector3.Cross(Vector3.up, vector3); vector5 = vector7 * (num3 - num2); } bool flag2 = this.lastCorner && this.buffer.Count - num == 1; if (flag2) { if (this.slowdownTime < 0.001f) { this.slowdownTime = 0.001f; } Vector3 vector8 = vector2 - vector; vector8.y = 0f; if (this.preciseSlowdown) { vector3 = (6f * vector8 - 4f * this.slowdownTime * this.velocity) / (this.slowdownTime * this.slowdownTime); } else { vector3 = 2f * (vector8 - this.slowdownTime * this.velocity) / (this.slowdownTime * this.slowdownTime); } vector3 = Vector3.ClampMagnitude(vector3, this.acceleration); vector5 *= Math.Min(magnitude / 0.5f, 1f); if (magnitude < this.endReachedDistance) { this.NextPart(); } } else { vector3 *= this.acceleration; } this.velocity += (vector3 + vector5 * this.wallForce) * RichAI.deltaTime; if (this.slowWhenNotFacingTarget) { float num5 = (Vector3.Dot(vector4, this.tr.forward) + 0.5f) * 0.6666667f; float num6 = Mathf.Sqrt(this.velocity.x * this.velocity.x + this.velocity.z * this.velocity.z); float y = this.velocity.y; this.velocity.y = 0f; float num7 = Mathf.Min(num6, this.maxSpeed * Mathf.Max(num5, 0.2f)); this.velocity = Vector3.Lerp(this.tr.forward * num7, this.velocity.normalized * num7, Mathf.Clamp((!flag2) ? 0f : (magnitude * 2f), 0.5f, 1f)); this.velocity.y = y; } else { float num8 = Mathf.Sqrt(this.velocity.x * this.velocity.x + this.velocity.z * this.velocity.z); num8 = this.maxSpeed / num8; if (num8 < 1f) { this.velocity.x = this.velocity.x * num8; this.velocity.z = this.velocity.z * num8; } } if (flag2) { Vector3 trotdir = Vector3.Lerp(this.velocity, this.currentTargetDirection, Math.Max(1f - magnitude * 2f, 0f)); this.RotateTowards(trotdir); } else { this.RotateTowards(this.velocity); } this.velocity += RichAI.deltaTime * this.gravity; if (this.rvoController != null && this.rvoController.enabled) { this.tr.position = vector; this.rvoController.Move(this.velocity); } else if (this.controller != null && this.controller.enabled) { this.tr.position = vector; this.controller.Move(this.velocity * RichAI.deltaTime); } else { float y2 = vector.y; vector += this.velocity * RichAI.deltaTime; vector = this.RaycastPosition(vector, y2); this.tr.position = vector; } } else if (this.rvoController != null && this.rvoController.enabled) { this.rvoController.Move(Vector3.zero); } if (currentPart is RichSpecial) { RichSpecial rs = currentPart as RichSpecial; if (!this.traversingSpecialPath) { base.StartCoroutine(this.TraverseSpecial(rs)); } } } else if (this.rvoController != null && this.rvoController.enabled) { this.rvoController.Move(Vector3.zero); } else if (!(this.controller != null) || !this.controller.enabled) { this.tr.position = this.RaycastPosition(this.tr.position, this.tr.position.y); } }
private IEnumerator TraverseSpecial(RichSpecial rs) { RichAI.<TraverseSpecial>c__Iterator2 <TraverseSpecial>c__Iterator = new RichAI.<TraverseSpecial>c__Iterator2(); <TraverseSpecial>c__Iterator.rs = rs; <TraverseSpecial>c__Iterator.<$>rs = rs; <TraverseSpecial>c__Iterator.<>f__this = this; return <TraverseSpecial>c__Iterator; }
/** Update is called once per frame */ protected virtual void Update() { deltaTime = Mathf.Min(Time.smoothDeltaTime * 2, Time.deltaTime); if (rp != null) { //System.Diagnostics.Stopwatch w = new System.Diagnostics.Stopwatch(); //w.Start(); RichPathPart pt = rp.GetCurrentPart(); RichFunnel fn = pt as RichFunnel; if (fn != null) { //Clear buffers for reuse Vector3 position = UpdateTarget(fn); //tr.position = ps; //Only get walls every 5th frame to save on performance if (Time.frameCount % 5 == 0) { wallBuffer.Clear(); fn.FindWalls(wallBuffer, wallDist); } /*for (int i=0;i<wallBuffer.Count;i+=2) { * Debug.DrawLine (wallBuffer[i],wallBuffer[i+1],Color.magenta); * }*/ //Pick next waypoint if current is reached int tgIndex = 0; /*if (buffer.Count > 1) { * if ((buffer[tgIndex]-tr.position).sqrMagnitude < pickNextWaypointDist*pickNextWaypointDist) { * tgIndex++; * } * }*/ //Target point Vector3 tg = buffer[tgIndex]; Vector3 dir = tg - position; dir.y = 0; bool passedTarget = Vector3.Dot(dir, currentTargetDirection) < 0; //Check if passed target in another way if (passedTarget && buffer.Count - tgIndex > 1) { tgIndex++; tg = buffer[tgIndex]; } if (tg != lastTargetPoint) { currentTargetDirection = (tg - position); currentTargetDirection.y = 0; currentTargetDirection.Normalize(); lastTargetPoint = tg; //Debug.DrawRay (tr.position, Vector3.down*2,Color.blue,0.2f); } //Direction to target dir = (tg - position); dir.y = 0; float magn = dir.magnitude; //Write out for other scripts to read distanceToWaypoint = magn; //Normalize dir = magn == 0 ? Vector3.zero : dir / magn; Vector3 normdir = dir; Vector3 force = Vector3.zero; if (wallForce > 0 && wallDist > 0) { float wLeft = 0; float wRight = 0; for (int i = 0; i < wallBuffer.Count; i += 2) { Vector3 closest = AstarMath.NearestPointStrict(wallBuffer[i], wallBuffer[i + 1], tr.position); float dist = (closest - position).sqrMagnitude; if (dist > wallDist * wallDist) { continue; } Vector3 tang = (wallBuffer[i + 1] - wallBuffer[i]).normalized; //Using the fact that all walls are laid out clockwise (seeing from inside) //Then left and right (ish) can be figured out like this float dot = Vector3.Dot(dir, tang) * (1 - System.Math.Max(0, (2 * (dist / (wallDist * wallDist)) - 1))); if (dot > 0) { wRight = System.Math.Max(wRight, dot); } else { wLeft = System.Math.Max(wLeft, -dot); } } Vector3 norm = Vector3.Cross(Vector3.up, dir); force = norm * (wRight - wLeft); //Debug.DrawRay (tr.position, force, Color.cyan); } //Is the endpoint of the path (part) the current target point bool endPointIsTarget = lastCorner && buffer.Count - tgIndex == 1; if (endPointIsTarget) { //Use 2nd or 3rd degree motion equation to figure out acceleration to reach target in "exact" [slowdownTime] seconds //Clamp to avoid divide by zero if (slowdownTime < 0.001f) { slowdownTime = 0.001f; } Vector3 diff = tg - position; diff.y = 0; if (preciseSlowdown) { //{ t = slowdownTime //{ diff = vt + at^2/2 + qt^3/6 //{ 0 = at + qt^2/2 //{ solve for a dir = (6 * diff - 4 * slowdownTime * velocity) / (slowdownTime * slowdownTime); } else { dir = 2 * (diff - slowdownTime * velocity) / (slowdownTime * slowdownTime); } dir = Vector3.ClampMagnitude(dir, acceleration); force *= System.Math.Min(magn / 0.5f, 1); if (magn < endReachedDistance) { //END REACHED NextPart(); } } else { dir *= acceleration; } //Debug.DrawRay (tr.position+Vector3.up, dir*3, Color.blue); velocity += (dir + force * wallForce) * deltaTime; if (slowWhenNotFacingTarget) { float dot = (Vector3.Dot(normdir, tr.forward) + 0.5f) * (1.0f / 1.5f); //velocity = Vector3.ClampMagnitude (velocity, maxSpeed * Mathf.Max (dot, 0.2f) ); float xzmagn = Mathf.Sqrt(velocity.x * velocity.x + velocity.z * velocity.z); float prevy = velocity.y; velocity.y = 0; float mg = Mathf.Min(xzmagn, maxSpeed * Mathf.Max(dot, 0.2f)); velocity = Vector3.Lerp(tr.forward * mg, velocity.normalized * mg, Mathf.Clamp(endPointIsTarget ? (magn * 2) : 0, 0.5f, 1.0f)); velocity.y = prevy; } else { // Clamp magnitude on the XZ axes float xzmagn = Mathf.Sqrt(velocity.x * velocity.x + velocity.z * velocity.z); xzmagn = maxSpeed / xzmagn; if (xzmagn < 1) { velocity.x *= xzmagn; velocity.z *= xzmagn; //Vector3.ClampMagnitude (velocity, maxSpeed); } } //Debug.DrawLine (tr.position, tg, lastCorner ? Color.red : Color.green); if (endPointIsTarget) { Vector3 trotdir = Vector3.Lerp(velocity, currentTargetDirection, System.Math.Max(1 - magn * 2, 0)); RotateTowards(trotdir); } else { RotateTowards(velocity); } //Applied after rotation to enable proper checks on if velocity is zero velocity += deltaTime * gravity; if (rvoController != null && rvoController.enabled) { //Use RVOController tr.position = position; rvoController.Move(velocity); } else if (controller != null && controller.enabled) { //Use CharacterController tr.position = position; controller.Move(velocity * deltaTime); } else { //Use Transform float lasty = position.y; position += velocity * deltaTime; position = RaycastPosition(position, lasty); tr.position = position; } } else { if (rvoController != null && rvoController.enabled) { //Use RVOController rvoController.Move(Vector3.zero); } } if (pt is RichSpecial) { RichSpecial rs = pt as RichSpecial; if (!traversingSpecialPath) { StartCoroutine(TraverseSpecial(rs)); } } //w.Stop(); //Debug.Log ((w.Elapsed.TotalMilliseconds*1000)); } else { if (rvoController != null && rvoController.enabled) { //Use RVOController rvoController.Move(Vector3.zero); } else if (controller != null && controller.enabled) { } else { tr.position = RaycastPosition(tr.position, tr.position.y); } } }
/// <summary>Use this for initialization.</summary> /// <param name="seeker">Optionally provide in order to take tag penalties into account. May be null if you do not use a Seeker\</param> /// <param name="path">Path to follow</param> /// <param name="mergePartEndpoints">If true, then adjacent parts that the path is split up in will /// try to use the same start/end points. For example when using a link on a navmesh graph /// Instead of first following the path to the center of the node where the link is and then /// follow the link, the path will be adjusted to go to the exact point where the link starts /// which usually makes more sense.</param> /// <param name="simplificationMode">The path can optionally be simplified. This can be a bit expensive for long paths.</param> public void Initialize(Seeker seeker, Path path, bool mergePartEndpoints, bool simplificationMode) { if (path.error) { throw new System.ArgumentException("Path has an error"); } List <GraphNode> nodes = path.path; if (nodes.Count == 0) { throw new System.ArgumentException("Path traverses no nodes"); } this.seeker = seeker; // Release objects back to object pool // Yeah, I know, it's casting... but this won't be called much for (int i = 0; i < parts.Count; i++) { var funnelPart = parts[i] as RichFunnel; var specialPart = parts[i] as RichSpecial; if (funnelPart != null) { ObjectPool <RichFunnel> .Release(ref funnelPart); } else if (specialPart != null) { ObjectPool <RichSpecial> .Release(ref specialPart); } } Clear(); // Initialize new Endpoint = path.vectorPath[path.vectorPath.Count - 1]; //Break path into parts for (int i = 0; i < nodes.Count; i++) { if (nodes[i] is TriangleMeshNode) { var graph = AstarData.GetGraph(nodes[i]) as NavmeshBase; if (graph == null) { throw new System.Exception("Found a TriangleMeshNode that was not in a NavmeshBase graph"); } RichFunnel f = ObjectPool <RichFunnel> .Claim().Initialize(this, graph); f.funnelSimplification = simplificationMode; int sIndex = i; uint currentGraphIndex = nodes[sIndex].GraphIndex; for (; i < nodes.Count; i++) { if (nodes[i].GraphIndex != currentGraphIndex && !(nodes[i] is NodeLink3Node)) { break; } } i--; if (sIndex == 0) { f.exactStart = path.vectorPath[0]; } else { f.exactStart = (Vector3)nodes[mergePartEndpoints ? sIndex - 1 : sIndex].position; } if (i == nodes.Count - 1) { f.exactEnd = path.vectorPath[path.vectorPath.Count - 1]; } else { f.exactEnd = (Vector3)nodes[mergePartEndpoints ? i + 1 : i].position; } f.BuildFunnelCorridor(nodes, sIndex, i); parts.Add(f); } else if (NodeLink2.GetNodeLink(nodes[i]) != null) { NodeLink2 nl = NodeLink2.GetNodeLink(nodes[i]); int sIndex = i; uint currentGraphIndex = nodes[sIndex].GraphIndex; for (i++; i < nodes.Count; i++) { if (nodes[i].GraphIndex != currentGraphIndex) { break; } } i--; if (i - sIndex > 1) { throw new System.Exception("NodeLink2 path length greater than two (2) nodes. " + (i - sIndex)); } else if (i - sIndex == 0) { //Just continue, it might be the case that a NodeLink was the closest node continue; } RichSpecial rps = ObjectPool <RichSpecial> .Claim().Initialize(nl, nodes[sIndex]); parts.Add(rps); } else if (!(nodes[i] is PointNode)) { // Some other graph type which we do not have support for throw new System.InvalidOperationException("The RichAI movment script can only be used on recast/navmesh graphs. A node of type " + nodes[i].GetType().Name + " was in the path."); } } }
IEnumerator TraverseSpecial (RichSpecial rs) { traversingSpecialPath = true; velocity = Vector3.zero; AnimationLink al = rs.nodeLink as AnimationLink; if (al == null) { Debug.LogError ("Unhandled RichSpecial"); yield break; } //Rotate character to face the correct direction while (!RotateTowards(rs.first.forward)) yield return null; //Reposition tr.parent.position = tr.position; tr.parent.rotation = tr.rotation; tr.localPosition = Vector3.zero; tr.localRotation = Quaternion.identity; //Set up animation speeds if (rs.reverse && al.reverseAnim) { anim[al.clip].speed = -al.animSpeed; anim[al.clip].normalizedTime = 1; anim.Play(al.clip); anim.Sample(); } else { anim[al.clip].speed = al.animSpeed; anim.Rewind(al.clip); anim.Play(al.clip); } //Fix required for animations in reverse direction tr.parent.position -= tr.position-tr.parent.position; //Wait for the animation to finish yield return new WaitForSeconds(Mathf.Abs(anim[al.clip].length/al.animSpeed)); traversingSpecialPath = false; NextPart (); //If a path completed during the time we traversed the special connection, we need to recalculate it if (delayUpdatePath) { delayUpdatePath = false; UpdatePath(); } }
public void Initialize(Seeker s, Path p, bool mergePartEndpoints, RichFunnel.FunnelSimplification simplificationMode) { if (p.error) { throw new ArgumentException("Path has an error"); } List <GraphNode> path = p.path; if (path.Count == 0) { throw new ArgumentException("Path traverses no nodes"); } this.seeker = s; for (int i = 0; i < this.parts.Count; i++) { RichFunnel funnel = this.parts[i] as RichFunnel; RichSpecial special = this.parts[i] as RichSpecial; if (funnel != null) { Pathfinding.Util.ObjectPool <RichFunnel> .Release(ref funnel); } else if (special != null) { Pathfinding.Util.ObjectPool <RichSpecial> .Release(ref special); } } this.parts.Clear(); this.currentPart = 0; for (int j = 0; j < path.Count; j++) { if (path[j] is TriangleMeshNode) { NavGraph graph = AstarData.GetGraph(path[j]); RichFunnel item = Pathfinding.Util.ObjectPool <RichFunnel> .Claim().Initialize(this, graph); item.funnelSimplificationMode = simplificationMode; int start = j; uint graphIndex = path[start].GraphIndex; while (j < path.Count) { if ((path[j].GraphIndex != graphIndex) && !(path[j] is NodeLink3Node)) { break; } j++; } j--; if (start == 0) { item.exactStart = p.vectorPath[0]; } else { item.exactStart = (Vector3)path[!mergePartEndpoints ? start : (start - 1)].position; } if (j == (path.Count - 1)) { item.exactEnd = p.vectorPath[p.vectorPath.Count - 1]; } else { item.exactEnd = (Vector3)path[!mergePartEndpoints ? j : (j + 1)].position; } item.BuildFunnelCorridor(path, start, j); this.parts.Add(item); continue; } if (NodeLink2.GetNodeLink(path[j]) != null) { NodeLink2 nodeLink = NodeLink2.GetNodeLink(path[j]); int num5 = j; uint num6 = path[num5].GraphIndex; j++; while (j < path.Count) { if (path[j].GraphIndex != num6) { break; } j++; } j--; if ((j - num5) > 1) { throw new Exception("NodeLink2 path length greater than two (2) nodes. " + (j - num5)); } if ((j - num5) != 0) { RichSpecial special2 = Pathfinding.Util.ObjectPool <RichSpecial> .Claim().Initialize(nodeLink, path[num5]); this.parts.Add(special2); } } } }