/// <summary>Called during either Update or FixedUpdate depending on if rigidbodies are used for movement or not</summary> protected override void MovementUpdateInternal(float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) { float currentAcceleration = maxAcceleration; // If negative, calculate the acceleration from the max speed if (currentAcceleration < 0) { currentAcceleration *= -maxSpeed; } if (updatePosition) { // Get our current position. We read from transform.position as few times as possible as it is relatively slow // (at least compared to a local variable) simulatedPosition = tr.position; } if (updateRotation) { simulatedRotation = tr.rotation; } var currentPosition = simulatedPosition; // Update which point we are moving towards interpolator.MoveToCircleIntersection2D(currentPosition, pickNextWaypointDist, movementPlane); var dir = movementPlane.ToPlane(steeringTarget - currentPosition); currentDir = dir; // Calculate the distance to the end of the path float distanceToEnd = dir.magnitude + Mathf.Max(0, interpolator.remainingDistance); // Check if we have reached the target var prevTargetReached = reachedEndOfPath; reachedEndOfPath = distanceToEnd <= endReachedDistance && interpolator.valid; if (!prevTargetReached && reachedEndOfPath) { OnTargetReached(); } float slowdown; // Normalized direction of where the agent is looking var forwards = movementPlane.ToPlane(simulatedRotation * (orientation == OrientationMode.YAxisForward ? Vector3.up : Vector3.forward)); // Check if we have a valid path to follow and some other script has not stopped the character if (interpolator.valid && !isStopped) { // How fast to move depending on the distance to the destination. // Move slower as the character gets closer to the destination. // This is always a value between 0 and 1. slowdown = distanceToEnd < slowdownDistance?Mathf.Sqrt(distanceToEnd / slowdownDistance) : 1; if (reachedEndOfPath && whenCloseToDestination == CloseToDestinationMode.Stop) { // Slow down as quickly as possible velocity2D -= Vector2.ClampMagnitude(velocity2D, currentAcceleration * deltaTime); } else { velocity2D += MovementUtilities.CalculateAccelerationToReachPoint(dir, dir.normalized * maxSpeed, velocity2D, currentAcceleration, rotationSpeed, maxSpeed, forwards) * deltaTime; } } else { slowdown = 1; // Slow down as quickly as possible velocity2D -= Vector2.ClampMagnitude(velocity2D, currentAcceleration * deltaTime); } velocity2D = MovementUtilities.ClampVelocity(velocity2D, maxSpeed, slowdown, slowWhenNotFacingTarget && enableRotation, forwards); ApplyGravity(deltaTime); // Set how much the agent wants to move during this frame var delta2D = lastDeltaPosition = CalculateDeltaToMoveThisFrame(movementPlane.ToPlane(currentPosition), distanceToEnd, deltaTime); nextPosition = currentPosition + movementPlane.ToWorld(delta2D, verticalVelocity * lastDeltaTime); CalculateNextRotation(slowdown, out nextRotation); }
/// <summary>Calculates how far to move during a single frame</summary> protected Vector2 CalculateDeltaToMoveThisFrame(Vector2 position, float distanceToEndOfPath, float deltaTime) { // Direction and distance to move during this frame return(Vector2.ClampMagnitude(velocity2D * deltaTime, distanceToEndOfPath)); }
/** Called during either Update or FixedUpdate depending on if rigidbodies are used for movement or not */ protected override void MovementUpdateInternal (float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) { // a = v/t, should probably expose as a variable float acceleration = maxSpeed / 0.4f; if (updatePosition) { // Get our current position. We read from transform.position as few times as possible as it is relatively slow // (at least compared to a local variable) simulatedPosition = tr.position; } if (updateRotation) simulatedRotation = tr.rotation; var currentPosition = simulatedPosition; // Update which point we are moving towards interpolator.MoveToCircleIntersection2D(currentPosition, pickNextWaypointDist, movementPlane); var dir = movementPlane.ToPlane(steeringTarget - currentPosition); // Calculate the distance to the end of the path float distanceToEnd = dir.magnitude + Mathf.Max(0, interpolator.remainingDistance); // Check if we have reached the target var prevTargetReached = reachedEndOfPath; reachedEndOfPath = distanceToEnd <= endReachedDistance && interpolator.valid; if (!prevTargetReached && reachedEndOfPath) OnTargetReached(); float slowdown; // Check if we have a valid path to follow and some other script has not stopped the character if (interpolator.valid && !isStopped) { // How fast to move depending on the distance to the destination. // Move slower as the character gets closer to the destination. // This is always a value between 0 and 1. slowdown = distanceToEnd < slowdownDistance ? Mathf.Sqrt(distanceToEnd / slowdownDistance) : 1; if (reachedEndOfPath && whenCloseToDestination == CloseToDestinationMode.Stop) { // Slow down as quickly as possible velocity2D -= Vector2.ClampMagnitude(velocity2D, acceleration * deltaTime); } else { velocity2D += MovementUtilities.CalculateAccelerationToReachPoint(dir, dir.normalized*maxSpeed, velocity2D, acceleration, maxSpeed) * deltaTime; } } else { slowdown = 1; // Slow down as quickly as possible velocity2D -= Vector2.ClampMagnitude(velocity2D, acceleration * deltaTime); } velocity2D = MovementUtilities.ClampVelocity(velocity2D, maxSpeed, slowdown, slowWhenNotFacingTarget, movementPlane.ToPlane(rotationIn2D ? tr.up : tr.forward)); ApplyGravity(deltaTime); if (rvoController != null && rvoController.enabled) { // Send a message to the RVOController that we want to move // with this velocity. In the next simulation step, this // velocity will be processed and it will be fed back to the // rvo controller and finally it will be used by this script // when calling the CalculateMovementDelta method below // Make sure that we don't move further than to the end point // of the path. If the RVO simulation FPS is low and we did // not do this, the agent might overshoot the target a lot. var rvoTarget = currentPosition + movementPlane.ToWorld(Vector2.ClampMagnitude(velocity2D, distanceToEnd), 0f); rvoController.SetTarget(rvoTarget, velocity2D.magnitude, maxSpeed); } // Set how much the agent wants to move during this frame var delta2D = lastDeltaPosition = CalculateDeltaToMoveThisFrame(movementPlane.ToPlane(currentPosition), distanceToEnd, deltaTime); nextPosition = currentPosition + movementPlane.ToWorld(delta2D, verticalVelocity * lastDeltaTime); CalculateNextRotation(slowdown, out nextRotation); }
/// <summary>Called during either Update or FixedUpdate depending on if rigidbodies are used for movement or not</summary> protected override void MovementUpdateInternal(float deltaTime, out Vector3 nextPosition, out Quaternion nextRotation) { float currentAcceleration = maxAcceleration; // If negative, calculate the acceleration from the max speed if (currentAcceleration < 0) { currentAcceleration *= -maxSpeed; } if (updatePosition) { // Get our current position. We read from transform.position as few times as possible as it is relatively slow // (at least compared to a local variable) simulatedPosition = tr.position; } if (updateRotation) { simulatedRotation = tr.rotation; } var currentPosition = simulatedPosition; // Update which point we are moving towards interpolator.MoveToCircleIntersection2D(currentPosition, pickNextWaypointDist, movementPlane); var dir = movementPlane.ToPlane(steeringTarget - currentPosition); // Calculate the distance to the end of the path float distanceToEnd = dir.magnitude + Mathf.Max(0, interpolator.remainingDistance); // Check if we have reached the target var prevTargetReached = reachedEndOfPath; reachedEndOfPath = distanceToEnd <= endReachedDistance && interpolator.valid; if (!prevTargetReached && reachedEndOfPath) { OnTargetReached(); } float speedLimitFactor; // Normalized direction of where the agent is looking var forwards = movementPlane.ToPlane(simulatedRotation * (orientation == OrientationMode.YAxisForward ? Vector3.up : Vector3.forward)); // Check if we have a valid path to follow and some other script has not stopped the character bool stopped = isStopped || (reachedEndOfPath && whenCloseToDestination == CloseToDestinationMode.Stop); if (rvoController != null) { rvoDensityBehavior.Update(reachedDestination, ref stopped, ref rvoController.priorityMultiplier, ref rvoController.flowFollowingStrength); } if (interpolator.valid && !stopped) { // How fast to move depending on the distance to the destination. // Move slower as the character gets closer to the destination. // This is always a value between 0 and 1. speedLimitFactor = distanceToEnd < slowdownDistance?Mathf.Sqrt(distanceToEnd / slowdownDistance) : 1; velocity2D += MovementUtilities.CalculateAccelerationToReachPoint(dir, dir.normalized * maxSpeed, velocity2D, currentAcceleration, rotationSpeed, maxSpeed, forwards) * deltaTime; } else { // Slow down as quickly as possible velocity2D -= Vector2.ClampMagnitude(velocity2D, currentAcceleration * deltaTime); // We are already slowing down as quickly as possible. Avoid limiting the speed in other ways. speedLimitFactor = 1; } velocity2D = MovementUtilities.ClampVelocity(velocity2D, maxSpeed, speedLimitFactor, slowWhenNotFacingTarget && enableRotation, preventMovingBackwards, forwards); ApplyGravity(deltaTime); if (rvoController != null && rvoController.enabled) { // Send a message to the RVOController that we want to move // with this velocity. In the next simulation step, this // velocity will be processed and it will be fed back to the // rvo controller and finally it will be used by this script // when calling the CalculateMovementDelta method below // Make sure that we don't move further than to the end point // of the path. If the RVO simulation FPS is low and we did // not do this, the agent might overshoot the target a lot. var rvoTarget = currentPosition + movementPlane.ToWorld(Vector2.ClampMagnitude(velocity2D, distanceToEnd), 0f); rvoController.SetTarget(rvoTarget, velocity2D.magnitude, maxSpeed); } // Set how much the agent wants to move during this frame var delta2D = lastDeltaPosition = CalculateDeltaToMoveThisFrame(currentPosition, distanceToEnd, deltaTime); nextPosition = currentPosition + movementPlane.ToWorld(delta2D, verticalVelocity * deltaTime); CalculateNextRotation(speedLimitFactor, out nextRotation); }
void TraverseFunnel(RichFunnel fn, float deltaTime) { // Clamp the current position to the navmesh // and update the list of upcoming corners in the path // and store that in the 'nextCorners' variable float elevation; Vector2 position = movementPlane.ToPlane(UpdateTarget(fn), out elevation); // Only find nearby walls every 5th frame to improve performance if (Time.frameCount % 5 == 0 && wallForce > 0 && wallDist > 0) { wallBuffer.Clear(); fn.FindWalls(wallBuffer, wallDist); } // Target point Vector2 targetPoint = waypoint = movementPlane.ToPlane(nextCorners[0]); // Direction to target Vector2 dir = targetPoint - position; // Is the endpoint of the path (part) the current target point bool targetIsEndPoint = lastCorner && nextCorners.Count == 1; // Normalized direction to the target Vector2 normdir = VectorMath.Normalize(dir, out distanceToWaypoint); // Calculate force from walls Vector2 wallForceVector = CalculateWallForce(position, elevation, normdir); Vector2 targetVelocity; if (targetIsEndPoint) { targetVelocity = slowdownTime > 0 ? Vector2.zero : normdir * maxSpeed; // Reduce the wall avoidance force as we get closer to our target wallForceVector *= System.Math.Min(distanceToWaypoint / 0.5f, 1); if (distanceToWaypoint <= endReachedDistance) { // END REACHED NextPart(); } } else { var nextNextCorner = nextCorners.Count > 1 ? movementPlane.ToPlane(nextCorners[1]) : position + 2 * dir; targetVelocity = (nextNextCorner - targetPoint).normalized * maxSpeed; } Vector2 accel = MovementUtilities.CalculateAccelerationToReachPoint(targetPoint - position, targetVelocity, velocity2D, acceleration, maxSpeed); // Update the velocity using the acceleration velocity2D += (accel + wallForceVector * wallForce) * deltaTime; // Distance to the end of the path (as the crow flies) var distToEndOfPath = fn.DistanceToEndOfPath; var slowdownFactor = slowdownTime > 0 ? distToEndOfPath / (maxSpeed * slowdownTime) : 1; velocity2D = MovementUtilities.ClampVelocity(velocity2D, maxSpeed, slowdownFactor, slowWhenNotFacingTarget, movementPlane.ToPlane(rotationIn2D ? tr.up : tr.forward)); ApplyGravity(deltaTime); if (rvoController != null && rvoController.enabled) { // Send a message to the RVOController that we want to move // with this velocity. In the next simulation step, this // velocity will be processed and it will be fed back to the // rvo controller and finally it will be used by this script // when calling the CalculateMovementDelta method below // Make sure that we don't move further than to the end point // of the path. If the RVO simulation FPS is low and we did // not do this, the agent might overshoot the target a lot. var rvoTarget = movementPlane.ToWorld(position + Vector2.ClampMagnitude(velocity2D, distToEndOfPath), elevation); rvoController.SetTarget(rvoTarget, velocity2D.magnitude, maxSpeed); } // Direction and distance to move during this frame var deltaPosition = CalculateDeltaToMoveThisFrame(position, distToEndOfPath, deltaTime); // Rotate towards the direction we are moving in // Slow down the rotation of the character very close to the endpoint of the path to prevent oscillations var rotationSpeedFactor = targetIsEndPoint ? Mathf.Clamp01(1.1f * slowdownFactor - 0.1f) : 1f; RotateTowards(deltaPosition, rotationSpeed * rotationSpeedFactor * deltaTime); Move(movementPlane.ToWorld(position, elevation), movementPlane.ToWorld(deltaPosition, verticalVelocity * deltaTime)); }