Thank you for your quick reply!
I don’t think it is quite that simple. I have included my debug code and output here for the RichAI Update() method. I merged in the debug output with the code so you could see what each statement produced for one iteration of Update. I recognize that I must be doing something wrong, but I can not for the life of me figure it out. One of the places that I am concerned about is the position coming out of the targetPoint coming out of the nextCorners array as it’s y-value is different than the position of the character. That difference seems to produce a significant y velocity that is put into the prevy variable and then extracted from prevy.
Here is the code and output:
public override void Update()
{
if (logger == null)
{
logger = new LogHelper("CustomRichAI", MyName);
logger.MethodName = "Update";
}
deltaTime = Mathf.Min(Time.smoothDeltaTime * 2, Time.deltaTime);
if (target != null)
{
logger.Msg("Just entering Update. target is " + target.position.ToString() + " and velocity is " + velocity.ToString());
[10/29/2016 4:14:22 PM] Just entering Update. target is (1511.7, 63.4, 963.2) and velocity is (-0.1, 0.0, -0.1)
}
if (rp != null)
{
logger.Msg("Using RichPath.");
[10/29/2016 4:14:22 PM] Using RichPath.
RichPathPart currentPart = rp.GetCurrentPart();
var fn = currentPart as RichFunnel;
if (fn != null)
{
logger.Msg(“Using RichFunnel.”);
[10/29/2016 4:14:22 PM] Using RichFunnel.
// Clamp the current position to the navmesh
// and update the list of upcoming corners in the path
// and store that in the ‘nextCorners’ variable
Vector3 position = UpdateTarget(fn);
// Only get walls every 5th frame to save on performance
if (Time.frameCount % 5 == 0 && wallForce > 0 && wallDist > 0)
{
wallBuffer.Clear();
fn.FindWalls(wallBuffer, wallDist);
}
// Target point
int tgIndex = 0;
Vector3 targetPoint = nextCorners[tgIndex];
Vector3 dir = targetPoint - position;
dir.y = 0;
bool passedTarget = Vector3.Dot(dir, currentTargetDirection) < 0;
// Check if passed target in another way
if (passedTarget && nextCorners.Count - tgIndex > 1)
{
tgIndex++;
targetPoint = nextCorners[tgIndex];
}
// Check if the target point changed compared to last frame
if (targetPoint != lastTargetPoint)
{
currentTargetDirection = targetPoint - position;
currentTargetDirection.y = 0;
currentTargetDirection.Normalize();
lastTargetPoint = targetPoint;
}
// Direction to target
dir = targetPoint - position;
dir.y = 0;
// Normalized direction
Vector3 normdir = VectorMath.Normalize(dir, out distanceToWaypoint);
// Is the endpoint of the path (part) the current target point
bool targetIsEndPoint = lastCorner && nextCorners.Count - tgIndex == 1;
// When very close to the target point, move directly towards the target
// instead of using accelerations as they tend to be a bit jittery in this case
if (targetIsEndPoint && distanceToWaypoint < 0.01f * maxSpeed)
{
// Velocity will be at most 1 times max speed, it will be further clamped below
velocity = (targetPoint - position) * 100;
//velocity = (targetPoint - position);
logger.Msg("Close to target. Setting velocity to (targetPoint - position) * 100) targetPoint " + targetPoint.ToString() + " position " + position.ToString() + " targetPoint - position = " + (targetPoint-position).ToString() + " * 100 = " + velocity.ToString());
[10/29/2016 4:14:22 PM] Close to target. Setting velocity to (targetPoint - position) * 100) targetPoint (1511.9, 67.5, 963.1) position (1511.9, 63.4, 963.1) targetPoint - position = (0.0, 4.1, 0.0) * 100 = (-1.4, 407.2, -1.5)
}
else
{
// Calculate force from walls
Vector3 wallForceVector = CalculateWallForce(position, normdir);
Vector2 accelerationVector;
if (targetIsEndPoint)
{
accelerationVector = CalculateAccelerationToReachPoint(To2D(targetPoint - position), Vector2.zero, To2D(velocity));
//accelerationVector = Vector3.ClampMagnitude(accelerationVector, acceleration);
// 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 = tgIndex < nextCorners.Count - 1 ? nextCorners[tgIndex + 1] : (targetPoint - position) * 2 + position;
var targetVelocity = (nextNextCorner - targetPoint).normalized * maxSpeed;
accelerationVector = CalculateAccelerationToReachPoint(To2D(targetPoint - position), To2D(targetVelocity), To2D(velocity));
}
// Update the velocity using the acceleration
velocity += (new Vector3(accelerationVector.x, 0, accelerationVector.y) + wallForceVector * wallForce) * deltaTime;
logger.Msg("Adjusted velocity by acceleration. velocity is " + velocity.ToString());
}
var currentNode = fn.CurrentNode;
Vector3 closestOnNode;
if (currentNode != null)
{
closestOnNode = currentNode.ClosestPointOnNode(position);
}
else
{
closestOnNode = position;
}
// Distance to the end of the path (as the crow flies)
var distToEndOfPath = (fn.exactEnd - closestOnNode).magnitude;
// Max speed to use for this frame
var currentMaxSpeed = maxSpeed;
currentMaxSpeed *= Mathf.Sqrt(Mathf.Min(1, distToEndOfPath / (maxSpeed * slowdownTime)));
// Check if the agent should slow down in case it is not facing the direction it wants to move in
if (slowWhenNotFacingTarget)
{
// 1 when normdir is in the same direction as tr.forward
// 0.2 when they point in the opposite directions
float directionSpeedFactor = Mathf.Max((Vector3.Dot(normdir, tr.forward) + 0.5f) / 1.5f, 0.2f);
currentMaxSpeed *= directionSpeedFactor;
float currentSpeed = VectorMath.MagnitudeXZ(velocity);
float prevy = velocity.y;
velocity.y = 0;
currentSpeed = Mathf.Min(currentSpeed, currentMaxSpeed);
// Make sure the agent always moves in the forward direction
// except when getting close to the end of the path in which case
// the velocity can be in any direction
velocity = Vector3.Lerp(velocity.normalized * currentSpeed, tr.forward * currentSpeed, Mathf.Clamp(targetIsEndPoint ? distanceToWaypoint * 2 : 1, 0.0f, 0.5f));
logger.Msg("(slowWhenNotFacingTarget) Doing a lerp. velocity is " + velocity.ToString());
[10/29/2016 4:14:22 PM] (slowWhenNotFacingTarget) Doing a lerp. velocity is (-0.1, 0.0, -0.1)
velocity.y = prevy;
logger.Msg("(slowWhenNotFacingTarget) Restored previous y value. velocity is " + velocity.ToString());
[10/29/2016 4:14:22 PM] (slowWhenNotFacingTarget) Restored previous y value. velocity is (-0.1, 407.2, -0.1)
}
else
{
velocity = VectorMath.ClampMagnitudeXZ(velocity, currentMaxSpeed);
logger.Msg("Adjusted velocity by ClampMagnitudeXZ. velocity is " + velocity.ToString());
}
// Apply gravity
velocity += deltaTime * gravity;
logger.Msg("Adjusted velocity by gravity. velocity is " + velocity.ToString());
[10/29/2016 4:14:22 PM] Adjusted velocity by gravity. velocity is (-0.1, 403.9, -0.1)
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 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 = position + VectorMath.ClampMagnitudeXZ(velocity, distToEndOfPath);
rvoController.SetTarget(rvoTarget, VectorMath.MagnitudeXZ(velocity), maxSpeed);
}
// Direction and distance to move during this frame
Vector3 deltaPosition;
if (rvoController != null && rvoController.enabled)
{
// Use RVOController to get a processed delta position
// such that collisions will be avoided if possible
deltaPosition = rvoController.CalculateMovementDelta(position, deltaTime);
Vector3 startDelta = deltaPosition;
// The RVOController does not know about gravity
// so we copy it from the normal velocity calculation
deltaPosition.y = velocity.y * deltaTime;
logger.Msg("Using rvoController to determine deltaPosition. start " + startDelta.ToString() + " adjusted by velocity.y " + velocity.ToString() + " and deltaTime " + deltaTime + " to come of with delta " + deltaPosition.ToString());
}
else
{
deltaPosition = velocity * deltaTime;
logger.Msg("Using velocity " + velocity.ToString() + " and deltaTime " + deltaTime + " to come of with delta " + deltaPosition.ToString());
[10/29/2016 4:14:22 PM] Using velocity (-0.1, 403.9, -0.1) and deltaTime 0.3333333 to come of with delta (0.0, 134.6, 0.0)
}
if (targetIsEndPoint)
{
// Rotate towards the direction that the agent was in
// when the target point was seen for the first time
// TODO: Some magic constants here, should probably compute them from other variables
// or expose them as separate variables
Vector3 trotdir = Vector3.Lerp(deltaPosition.normalized, currentTargetDirection, System.Math.Max(1 - distanceToWaypoint * 2, 0));
RotateTowards(trotdir);
}
else
{
// Rotate towards the direction we are moving in
RotateTowards(deltaPosition);
}
if (controller != null && controller.enabled)
{
logger.Msg("CharacterController is moving me.");
// Use CharacterController
tr.position = position;
controller.Move(deltaPosition);
// Grab the position after the movement to be able to take physics into account
position = tr.position;
}
else
{
// Use Transform
float lastY = position.y;
Vector3 origPos = position;
position += deltaPosition;
Vector3 deltaPos = position;
// Position the character on the ground
position = RaycastPosition(position, lastY);
Vector3 raycastPos = position;
logger.Msg("Am at " + origPos.ToString() + " headed to " + deltaPos.ToString() + " y adjusted " + raycastPos.ToString());
[10/29/2016 4:14:22 PM] Am at (1511.9, 63.4, 963.1) headed to (1511.8, 198.0, 963.1) y adjusted (1511.8, 198.0, 963.1)
}
// Clamp the position to the navmesh after movement is done
var clampedPosition = fn.ClampToNavmesh(position);
if (position != clampedPosition)
{
// The agent was outside the navmesh. Remove that component of the velocity
// so that the velocity only goes along the direction of the wall, not into it
var difference = clampedPosition - position;
velocity -= difference * Vector3.Dot(difference, velocity) / difference.sqrMagnitude;
// Make sure the RVO system knows that there was a collision here
// Otherwise other agents may think this agent continued to move forwards
// and avoidance quality may suffer
if (rvoController != null && rvoController.enabled)
{
rvoController.SetCollisionNormal(difference);
}
}
logger.Msg("Setting transform to " + clampedPosition.ToString());
[10/29/2016 4:14:22 PM] Setting transform to (1511.8, 198.0, 963.1)
tr.position = clampedPosition;
}
else
{
logger.Msg(“Not using RichFunnel.”);
if (rvoController != null && rvoController.enabled)
{
//Use RVOController
rvoController.Move(Vector3.zero);
}
}
if (currentPart is RichSpecial)
{
logger.Msg(“Checking RichSpecial.”);
// 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
if (!traversingSpecialPath)
{
StartCoroutine(TraverseSpecial(currentPart as RichSpecial));
}
}
}
else
{
logger.Msg(“Not using RichPath.”);
if (rvoController != null && rvoController.enabled)
{
// Use RVOController
rvoController.Move(Vector3.zero);
}
else
if (controller != null && controller.enabled)
{
}
else
{
logger.Msg("Raycasting position for " + tr.position.ToString());
tr.position = RaycastPosition(tr.position, tr.position.y);
logger.Msg("Setting transform to " + tr.position.ToString());
}
}
if (tr != null)
{
logger.Msg("AI position is " + tr.position.ToString());
[10/29/2016 4:14:22 PM] AI position is (1511.8, 198.0, 963.1)
}
}