123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- using UnityEngine;
- using System.Collections;
- using System.Collections.Generic;
- namespace Pathfinding {
- using Pathfinding.RVO;
- using Pathfinding.Util;
- [AddComponentMenu("Pathfinding/AI/RichAI (3D, for navmesh)")]
-
-
-
-
-
-
-
- public partial class RichAI : AIBase, IAstarAI {
-
-
-
-
- public float acceleration = 5;
-
-
-
-
- public float rotationSpeed = 360;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public float slowdownTime = 0.5f;
-
-
-
-
-
-
- public float endReachedDistance = 0.01f;
-
-
-
-
-
-
- public float wallForce = 3;
-
-
-
-
-
-
- public float wallDist = 1;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public bool funnelSimplification = false;
-
-
-
-
- public bool slowWhenNotFacingTarget = true;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public System.Func<RichSpecial, IEnumerator> onTraverseOffMeshLink;
-
- protected readonly RichPath richPath = new RichPath();
- protected bool delayUpdatePath;
- protected bool lastCorner;
-
- protected float distanceToSteeringTarget = float.PositiveInfinity;
- protected readonly List<Vector3> nextCorners = new List<Vector3>();
- protected readonly List<Vector3> wallBuffer = new List<Vector3>();
- public bool traversingOffMeshLink { get; protected set; }
-
- public float remainingDistance {
- get {
- return distanceToSteeringTarget + Vector3.Distance(steeringTarget, richPath.Endpoint);
- }
- }
-
- public bool reachedEndOfPath { get { return approachingPathEndpoint && distanceToSteeringTarget < endReachedDistance; } }
-
- public bool reachedDestination {
- get {
- if (!reachedEndOfPath) return false;
-
- if (approachingPathEndpoint && distanceToSteeringTarget + movementPlane.ToPlane(destination - richPath.Endpoint).magnitude > endReachedDistance) return false;
-
- if (orientation != OrientationMode.YAxisForward) {
-
- float yDifference;
- movementPlane.ToPlane(destination - position, out yDifference);
- var h = tr.localScale.y * height;
- if (yDifference > h || yDifference < -h*0.5) return false;
- }
- return true;
- }
- }
-
- public bool hasPath { get { return richPath.GetCurrentPart() != null; } }
-
- public bool pathPending { get { return waitingForPathCalculation || delayUpdatePath; } }
-
- public Vector3 steeringTarget { get; protected set; }
-
- float IAstarAI.radius { get { return radius; } set { radius = value; } }
-
- float IAstarAI.height { get { return height; } set { height = value; } }
-
- float IAstarAI.maxSpeed { get { return maxSpeed; } set { maxSpeed = value; } }
-
- bool IAstarAI.canSearch { get { return canSearch; } set { canSearch = value; } }
-
- bool IAstarAI.canMove { get { return canMove; } set { canMove = value; } }
-
-
-
-
-
-
- public bool approachingPartEndpoint {
- get {
- return lastCorner && nextCorners.Count == 1;
- }
- }
-
-
-
-
-
-
- public bool approachingPathEndpoint {
- get {
- return approachingPartEndpoint && richPath.IsLastPart;
- }
- }
-
-
-
-
-
-
-
-
- public override void Teleport (Vector3 newPosition, bool clearPath = true) {
-
- var nearest = AstarPath.active != null? AstarPath.active.GetNearest(newPosition) : new NNInfo();
- float elevation;
- movementPlane.ToPlane(newPosition, out elevation);
- newPosition = movementPlane.ToWorld(movementPlane.ToPlane(nearest.node != null ? nearest.position : newPosition), elevation);
- base.Teleport(newPosition, clearPath);
- }
-
- protected override void OnDisable () {
-
- base.OnDisable();
- traversingOffMeshLink = false;
-
- StopAllCoroutines();
- }
- protected override bool shouldRecalculatePath {
- get {
-
- return base.shouldRecalculatePath && !traversingOffMeshLink;
- }
- }
- public override void SearchPath () {
-
- if (traversingOffMeshLink) {
- delayUpdatePath = true;
- } else {
- base.SearchPath();
- }
- }
- protected override void OnPathComplete (Path p) {
- waitingForPathCalculation = false;
- p.Claim(this);
- if (p.error) {
- p.Release(this);
- return;
- }
- if (traversingOffMeshLink) {
- delayUpdatePath = true;
- } else {
-
-
-
- if (p is RandomPath rpath) {
- destination = rpath.originalEndPoint;
- } else if (p is MultiTargetPath mpath) {
- destination = mpath.originalEndPoint;
- }
- richPath.Initialize(seeker, p, true, funnelSimplification);
-
-
-
- var part = richPath.GetCurrentPart() as RichFunnel;
- if (part != null) {
- if (updatePosition) simulatedPosition = tr.position;
-
- var localPosition = movementPlane.ToPlane(UpdateTarget(part));
-
- steeringTarget = nextCorners[0];
- Vector2 targetPoint = movementPlane.ToPlane(steeringTarget);
- distanceToSteeringTarget = (targetPoint - localPosition).magnitude;
- if (lastCorner && nextCorners.Count == 1 && distanceToSteeringTarget <= endReachedDistance) {
- NextPart();
- }
- }
- }
- p.Release(this);
- }
- protected override void ClearPath () {
- CancelCurrentPathRequest();
- richPath.Clear();
- lastCorner = false;
- delayUpdatePath = false;
- distanceToSteeringTarget = float.PositiveInfinity;
- }
-
-
-
-
- protected void NextPart () {
- if (!richPath.CompletedAllParts) {
- if (!richPath.IsLastPart) lastCorner = false;
- richPath.NextPart();
- if (richPath.CompletedAllParts) {
- OnTargetReached();
- }
- }
- }
-
- public void GetRemainingPath (List<Vector3> buffer, out bool stale) {
- richPath.GetRemainingPath(buffer, simulatedPosition, out stale);
- }
-
- protected virtual void OnTargetReached () {
- }
- protected virtual Vector3 UpdateTarget (RichFunnel fn) {
- nextCorners.Clear();
-
-
-
- bool requiresRepath;
- Vector3 position = fn.Update(simulatedPosition, nextCorners, 2, out lastCorner, out requiresRepath);
- if (requiresRepath && !waitingForPathCalculation && canSearch) {
-
- SearchPath();
- }
- return position;
- }
-
- protected override void MovementUpdateInternal (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) {
- if (updatePosition) simulatedPosition = tr.position;
- if (updateRotation) simulatedRotation = tr.rotation;
- RichPathPart currentPart = richPath.GetCurrentPart();
- if (currentPart is RichSpecial) {
-
- if (!traversingOffMeshLink && !richPath.CompletedAllParts) {
- StartCoroutine(TraverseSpecial(currentPart as RichSpecial));
- }
- nextPosition = steeringTarget = simulatedPosition;
- nextRotation = rotation;
- } else {
- var funnel = currentPart as RichFunnel;
-
- if (funnel != null && !isStopped) {
- TraverseFunnel(funnel, deltaTime, out nextPosition, out nextRotation);
- } else {
-
-
- velocity2D -= Vector2.ClampMagnitude(velocity2D, acceleration * deltaTime);
- FinalMovement(simulatedPosition, deltaTime, float.PositiveInfinity, 1f, out nextPosition, out nextRotation);
- steeringTarget = simulatedPosition;
- }
- }
- }
- void TraverseFunnel (RichFunnel fn, float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) {
-
-
-
- var position3D = UpdateTarget(fn);
- float elevation;
- Vector2 position = movementPlane.ToPlane(position3D, out elevation);
-
- if (Time.frameCount % 5 == 0 && wallForce > 0 && wallDist > 0) {
- wallBuffer.Clear();
- fn.FindWalls(wallBuffer, wallDist);
- }
-
- steeringTarget = nextCorners[0];
- Vector2 targetPoint = movementPlane.ToPlane(steeringTarget);
-
- Vector2 dir = targetPoint - position;
-
- Vector2 normdir = VectorMath.Normalize(dir, out distanceToSteeringTarget);
-
- Vector2 wallForceVector = CalculateWallForce(position, elevation, normdir);
- Vector2 targetVelocity;
- if (approachingPartEndpoint) {
- targetVelocity = slowdownTime > 0 ? Vector2.zero : normdir * maxSpeed;
-
- wallForceVector *= System.Math.Min(distanceToSteeringTarget/0.5f, 1);
- if (distanceToSteeringTarget <= endReachedDistance) {
-
- NextPart();
- }
- } else {
- var nextNextCorner = nextCorners.Count > 1 ? movementPlane.ToPlane(nextCorners[1]) : position + 2*dir;
- targetVelocity = (nextNextCorner - targetPoint).normalized * maxSpeed;
- }
- var forwards = movementPlane.ToPlane(simulatedRotation * (orientation == OrientationMode.YAxisForward ? Vector3.up : Vector3.forward));
- Vector2 accel = MovementUtilities.CalculateAccelerationToReachPoint(targetPoint - position, targetVelocity, velocity2D, acceleration, rotationSpeed, maxSpeed, forwards);
-
- velocity2D += (accel + wallForceVector*wallForce)*deltaTime;
-
- var distanceToEndOfPath = distanceToSteeringTarget + Vector3.Distance(steeringTarget, fn.exactEnd);
- var slowdownFactor = distanceToEndOfPath < maxSpeed * slowdownTime? Mathf.Sqrt(distanceToEndOfPath / (maxSpeed * slowdownTime)) : 1;
- FinalMovement(position3D, deltaTime, distanceToEndOfPath, slowdownFactor, out nextPosition, out nextRotation);
- }
- void FinalMovement (Vector3 position3D, float deltaTime, float distanceToEndOfPath, float slowdownFactor, out Vector3 nextPosition, out Quaternion nextRotation) {
- var forwards = movementPlane.ToPlane(simulatedRotation * (orientation == OrientationMode.YAxisForward ? Vector3.up : Vector3.forward));
- velocity2D = MovementUtilities.ClampVelocity(velocity2D, maxSpeed, slowdownFactor, slowWhenNotFacingTarget && enableRotation, forwards);
- ApplyGravity(deltaTime);
- if (rvoController != null && rvoController.enabled) {
-
-
-
-
-
-
-
-
- var rvoTarget = position3D + movementPlane.ToWorld(Vector2.ClampMagnitude(velocity2D, distanceToEndOfPath));
- rvoController.SetTarget(rvoTarget, velocity2D.magnitude, maxSpeed);
- }
-
- var deltaPosition = lastDeltaPosition = CalculateDeltaToMoveThisFrame(movementPlane.ToPlane(position3D), distanceToEndOfPath, deltaTime);
-
-
- var rotationSpeedFactor = approachingPartEndpoint ? Mathf.Clamp01(1.1f * slowdownFactor - 0.1f) : 1f;
- nextRotation = enableRotation ? SimulateRotationTowards(deltaPosition, rotationSpeed * rotationSpeedFactor * deltaTime) : simulatedRotation;
- nextPosition = position3D + movementPlane.ToWorld(deltaPosition, verticalVelocity * deltaTime);
- }
- protected override Vector3 ClampToNavmesh (Vector3 position, out bool positionChanged) {
- if (richPath != null) {
- var funnel = richPath.GetCurrentPart() as RichFunnel;
- if (funnel != null) {
- var clampedPosition = funnel.ClampToNavmesh(position);
-
-
- var difference = movementPlane.ToPlane(clampedPosition - position);
- float sqrDifference = difference.sqrMagnitude;
- if (sqrDifference > 0.001f*0.001f) {
-
-
- velocity2D -= difference * Vector2.Dot(difference, velocity2D) / sqrDifference;
-
-
-
- if (rvoController != null && rvoController.enabled) {
- rvoController.SetCollisionNormal(difference);
- }
- positionChanged = true;
-
- return position + movementPlane.ToWorld(difference);
- }
- }
- }
- positionChanged = false;
- return position;
- }
- Vector2 CalculateWallForce (Vector2 position, float elevation, Vector2 directionToTarget) {
- if (wallForce <= 0 || wallDist <= 0) return Vector2.zero;
- float wLeft = 0;
- float wRight = 0;
- var position3D = movementPlane.ToWorld(position, elevation);
- for (int i = 0; i < wallBuffer.Count; i += 2) {
- Vector3 closest = VectorMath.ClosestPointOnSegment(wallBuffer[i], wallBuffer[i+1], position3D);
- float dist = (closest-position3D).sqrMagnitude;
- if (dist > wallDist*wallDist) continue;
- Vector2 tang = movementPlane.ToPlane(wallBuffer[i+1]-wallBuffer[i]).normalized;
-
-
- float dot = Vector2.Dot(directionToTarget, tang);
- float weight = 1 - System.Math.Max(0, (2*(dist / (wallDist*wallDist))-1));
- if (dot > 0) wRight = System.Math.Max(wRight, dot * weight);
- else wLeft = System.Math.Max(wLeft, -dot * weight);
- }
- Vector2 normal = new Vector2(directionToTarget.y, -directionToTarget.x);
- return normal*(wRight-wLeft);
- }
-
- protected virtual IEnumerator TraverseSpecial (RichSpecial link) {
- traversingOffMeshLink = true;
-
-
- velocity2D = Vector3.zero;
- var offMeshLinkCoroutine = onTraverseOffMeshLink != null? onTraverseOffMeshLink(link) : TraverseOffMeshLinkFallback(link);
- yield return StartCoroutine(offMeshLinkCoroutine);
-
- traversingOffMeshLink = false;
- NextPart();
-
- if (delayUpdatePath) {
- delayUpdatePath = false;
-
- if (canSearch) SearchPath();
- }
- }
-
-
-
-
- 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;
- }
- }
- protected static readonly Color GizmoColorPath = new Color(8.0f/255, 78.0f/255, 194.0f/255);
- protected override void OnDrawGizmos () {
- base.OnDrawGizmos();
- if (tr != null) {
- Gizmos.color = GizmoColorPath;
- Vector3 lastPosition = position;
- for (int i = 0; i < nextCorners.Count; lastPosition = nextCorners[i], i++) {
- Gizmos.DrawLine(lastPosition, nextCorners[i]);
- }
- }
- }
- protected override int OnUpgradeSerializedData (int version, bool unityThread) {
- #pragma warning disable 618
- if (unityThread && animCompatibility != null) anim = animCompatibility;
- #pragma warning restore 618
- return base.OnUpgradeSerializedData(version, unityThread);
- }
- }
- }
|