Movement Agents
Movement Agents provide a flexible system for controlling entity movement in your game. They handle physics-based movement, navmesh integration, and velocity management with client-side prediction support. The player object uses a Movement_Agent
but it can also be used for NPCs or other player controlled objects like an RC car.
Overview
The Movement_Agent
component offers:
- Physics-based movement through rigidbody integration
- Automatic navmesh constraint support
- Customizable velocity calculation through a callback
- Built-in friction and acceleration handling
- Client-side prediction for networked games
Note that Movement_Agent
requires a Rigidbody
be on the same entity, but avoid directly modifying the Rigidbody
. Running all your logic through the Movement_Agent
ensures that client-side prediction/correction will happen properly and your movement will be smooth.
Tweakables
Movement Agents has several movement-related variables to play with:
Velocity
: Current movement velocity in meters per second. The player character is roughly one meter tall.Impulse
: One-time force to apply (cleared after use).Acceleration
: Constant acceleration applied over time.Friction
: How quickly velocity decays as a percentage of velocity lost per 1/60th of a second. The default player friction is 0.5.
Custom Movement Control
The most powerful feature of Movement Agents is the CustomVelocityCallback
. This callback lets you define exactly how the agent should move:
agent.CustomVelocityCallback = (agent, currentVelocity, input, dt) =>
{
float speed = 400.0f;
currentVelocity += input * dt * speed;
return currentVelocity;
};
Above is the default player movement code. If your game has speed modifiers then this is where you would account for those like this:
agent.CustomVelocityCallback = (agent, currentVelocity, input, dt) =>
{
float speed = 400.0f;
MyPlayer player = agent.Entity.GetComponent<MyPlayer>();
if (player.HasEffect<GlueTrapEffect>())
{
speed *= 0.25f;
}
if (player.IsStandingOnBoostPad())
{
speed *= 2f;
}
if (player.ControlsAreReversed())
{
input = -input;
}
currentVelocity += input * dt * speed;
return currentVelocity;
};
Navmesh Integration
Movement_Agent
can by configured to leverage Navmeshes with its field bool LockToNavmesh
. When it is set to true, the Movement Agent will automatically:
- Constrain movement to walkable areas
- Follow navmesh contours
- Prevent moving into obstacles
If LockToNavmesh
is set to true
then every frame the agent will look for the closest point on any navmesh to its current position, and snap there. Most of the time this will result in no change unless you teleport the agent off the navmesh, or you modify the navmesh underneath them; however the search over all navmeshes can be quite expensive performance-wise. To get around this there is a field Navmesh NavmeshToLockTo
. When this is set, the agent will only search this single navmesh during the closest point search so you should use this wherever possible.
An example with pathfinding for NPCs follows. The input
parameter will be unused for NPCs, instead calculating a movement direction using pathfinding based on some target position calculated elsewhere based on AI logic.
agent.CustomVelocityCallback = (agent, currentVelocity, input, dt) =>
{
Vector2 target = GetTargetPosition();
Vector2[] pathPoints = null; // It would be best practice to store this as a class variable
// and reuse it to prevent generating garbage every frame.
if (navmesh.TryFindPath(agent.Entity.Position, target, ref pathPoints, out int pathLength))
{
Vector2 nextPoint = pathPoints[1];
Vector2 direction = (nextPoint - agent.Entity.Position).Normalized;
return direction * 200 * dt;
}
return Vector2.Zero;
};