The CharacterMovement component is a robust and feature-rich fully kinematic character controller, also known as a motor.
It has been developed as a compelling alternative to Unity's character controller, maintaining the familiar workflow through functions like Move / SimpleMove, while surpassing it with a myriad of enhanced features and advantages.
What is a character controller ?
A Character Controller facilitates straightforward movement constrained by collisions, eliminating the need to grapple with a dynamic Rigidbody.
Unlike a dynamic Rigidbody, a Character Controller remains unaffected by forces and will only initiate movement upon calling the Move function. Subsequently, it executes the intended movement while adhering to collision constraints.
In the earlier days of gaming, titles didn't rely on a 'true' physics engine like the PhysX SDK (Unity's underlying physics engine). However, they still employed a character controller to navigate players within a level. Games like Quake or Doom utilized a bespoke piece of code for collision detection and response, often constituting the sole physics aspect in the entire game. While these implementations lacked extensive physics, they heavily leaned on meticulously adjusted values to deliver an immersive player experience. The specific algorithm governing their behavior is commonly known as the 'collide and slide' algorithm, refined over more than a decade for optimal performance.
The CharacterMovement component is an implementation of such an algorithm, delivering a reliable and widely recognized behavior for character control.
The Character Movement Component
Property:
Function:
Plane Constraint
Allow to constrain the Character so movement along the locked axis is not possible.
Root Transform
The root GameObject of your character's model.
Root Transform Offset
The root transform will be positioned at this offset from foot position.
Radius
The Character's capsule collider radius.
Height
The Character's capsule collider height.
Slope Limit
The maximum angle (in degrees) for a walk-able surface.
Step Offset
The maximum height (in meters) for a valid step.
Perch Offset
Allow a Character to perch on the edge of a surface if the horizontal distance from the Character's position to the edge is closer than this.
Perch Additional Height
When perching on a ledge, add this additional distance to step offset when determining how high above a walk-able ground we can perch.
Slope Limit Override
If enabled, colliders with SlopeLimitBehaviour component will be able to override this slope limit.
Use Flat Top
If enabled, will treat head collisions as if the character is using a shape with a flat top.
Use Flat Base For Ground Checks
If enabled, performs ground checks as if the character is using a shape with a flat base. This avoids the situation where characters slowly lower off the side of a ledge (as their capsule 'balances' on the edge).
Collision Layers
Character collision layers mask.
Trigger Interaction
Overrides the global Physics.queriesHitTriggers to specify whether queries (raycasts, spherecasts, overlap tests, etc.) hit Triggers by default.
Advanced:
Min Move Distance
The minimum move distance of the character controller. In most situations this value should be left at 0.
Max Movement Iterations
Max number of iterations used during movement.
Max Depenetration Iterations
Max number of iterations used to resolve penetrations.
Enable Physics Interactions
If enabled, the character will interact with dynamic rigidbodies when walking into them.
Allow Push Characters
If enabled, the character will interact with other characters when walking into them.
Impart Platform Movement
If enabled, the character will move with the moving platform it is standing on.
Impart Platform Rotation
If enabled, the character will rotate (yaw-only) with the moving platform it is standing on.
Impart Platform Velocity
If enabled, impart the platform's velocity when jumping or falling off it.
Details
The CharacterMovement component, in addition to its primary function of constrained movement by collision, offers a broad range of specifically designed functions to leverage its capabilities to the fullest.
Properties
/// <summary>/// Cached character's transform./// </summary>publicnew Transform transform/// <summary>/// The Character's rigidbody./// </summary>publicnew Rigidbody rigidbody/// <summary>/// The Rigidbody interpolation setting./// </summary>public RigidbodyInterpolation interpolation/// <summary>/// The Character's collider./// </summary>publicnew Collider collider/// <summary>/// The root bone in the avatar./// </summary>public Transform rootTransform/// <summary>/// The root transform will be positioned at this offset./// </summary>public Vector3 rootTransformOffset/// <summary>/// The character's current position./// </summary>public Vector3 position/// <summary>/// The character's current rotation./// </summary>public Quaternion rotation/// <summary>/// The character's center in world space./// </summary>public Vector3 worldCenter/// <summary>/// The character's updated position./// </summary>public Vector3 updatedPosition/// <summary>/// The character's updated rotation./// </summary>public Quaternion updatedRotation/// <summary>/// The current relative velocity of the Character./// The velocity is relative because it won't track movements to the transform that happen outside of this,/// e.g. character parented under another moving Transform, such as a moving vehicle./// </summary>publicref Vector3 velocity/// <summary>/// The character's speed./// </summary>publicfloat speed/// <summary>/// The character's speed along its forward vector (e.g: in local space)./// </summary>publicfloat forwardSpeed/// <summary>/// The character's speed along its right vector (e.g: in local space)./// </summary>publicfloat sidewaysSpeed/// <summary>/// The Character's capsule collider radius./// </summary>publicfloat radius/// <summary>/// The Character's capsule collider height./// </summary>publicfloat height/// <summary>/// The maximum angle (in degrees) for a walkable slope./// </summary>publicfloat slopeLimit/// <summary>/// The maximum height (in meters) for a valid step./// </summary>publicfloat stepOffset/// <summary>/// Allow a Character to perch on the edge of a surface if the horizontal distance from the Character's position to the edge is closer than this./// Note that we still enforce stepOffset to start the step up, this just allows the Character to hang off the edge or step slightly higher off the ground./// </summary>publicfloat perchOffset/// <summary>/// When perching on a ledge, add this additional distance to stepOffset when determining how high above a walkable ground we can perch./// </summary>publicfloat perchAdditionalHeight/// <summary>/// Should allow external slope limit override ?/// </summary>publicbool slopeLimitOverride/// <summary>/// When enabled, will treat head collisions as if the character is using a shape with a flat top./// </summary>publicbool useFlatTop/// <summary>/// Performs ground checks as if the character is using a shape with a flat base./// This avoids the situation where characters slowly lower off the side of a ledge (as their capsule 'balances' on the edge)./// </summary>publicbool useFlatBaseForGroundChecks/// <summary>/// Layers to be considered during collision detection./// </summary>public LayerMask collisionLayers/// <summary>/// Determines how the Character should interact with triggers./// </summary>public QueryTriggerInteraction triggerInteraction/// <summary>/// Should perform collision detection ?/// </summary>publicbool detectCollisions/// <summary>/// What part of the capsule collided with the environment during the last Move call./// </summary>public CollisionFlags collisionFlags/// <summary>/// Is the Character's movement constrained to a plane ?/// </summary>publicbool isConstrainedToPlane/// <summary>/// Should movement be constrained to ground when on walkable ground ?/// Toggles ground constraint. /// </summary>publicbool constrainToGround/// <summary>/// Is the Character constrained to walkable ground ?/// </summary>publicbool isConstrainedToGround/// <summary>/// Is the ground constraint temporary disabled?/// </summary>publicbool isGroundConstraintPaused/// <summary>/// If isGroundConstraintPaused is true, this represent the pause remaining time./// </summary>publicfloat unconstrainedTimer/// <summary>/// Was the character on ground last Move call ?/// </summary>publicbool wasOnGround/// <summary>/// Is the character on ground ?/// </summary>publicbool isOnGround/// <summary>/// Was the character on walkable ground last Move call ?/// </summary>publicbool wasOnWalkableGround/// <summary>/// Is the character on walkable ground ?/// </summary>publicbool isOnWalkableGround/// <summary>/// Was the character on walkable ground AND constrained to ground last Move call ?/// </summary>publicbool wasGrounded/// <summary>/// Is the character on walkable ground AND constrained to ground./// </summary>publicbool isGrounded/// <summary>/// The signed distance to ground./// </summary>publicfloat groundDistance/// <summary>/// The current ground impact point./// </summary>public Vector3 groundPoint/// <summary>/// The current ground normal./// </summary>public Vector3 groundNormal/// <summary>/// The current ground surface normal./// </summary>public Vector3 groundSurfaceNormal/// <summary>/// The current ground collider./// </summary>public Collider groundCollider/// <summary>/// The current ground transform./// </summary>public Transform groundTransform/// <summary>/// The Rigidbody of the collider that was hit. If the collider is not attached to a rigidbody then it is null./// </summary>public Rigidbody groundRigidbody/// <summary>/// Structure containing information about current ground./// </summary>public FindGroundResult currentGround/// <summary>/// Structure containing information about current moving platform (if any)./// </summary>public MovingPlatform movingPlatform/// <summary>/// The terminal velocity when landed (eg: isGrounded)./// </summary>public Vector3 landedVelocity/// <summary>/// Set this to true if riding on a moving platform that you know is clear from non-moving world obstructions./// Optimization to avoid sweeps during based movement, USE WITH CARE./// </summary>publicbool fastPlatformMove/// <summary>/// Whether the Character moves with the moving platform it is standing on./// If true, the Character moves with the moving platform./// </summary>publicbool impartPlatformMovement/// <summary>/// Whether the Character receives the changes in rotation of the platform it is standing on./// If true, the Character rotates with the moving platform./// </summary>publicbool impartPlatformRotation/// <summary>/// If true, impart the platform's velocity when jumping or falling off it./// </summary>publicbool impartPlatformVelocity/// <summary>/// If enabled, the player will interact with dynamic rigidbodies when walking into them./// </summary>publicbool enablePhysicsInteraction/// <summary>/// If enabled, the player will interact with other characters when walking into them./// </summary>publicbool physicsInteractionAffectsCharacters/// <summary>/// Force applied to rigidbodies when walking into them (due to mass and relative velocity) is scaled by this amount./// </summary>publicfloat pushForceScale
Public Methods
/// <summary>/// Specifies the character's bounding volume (eg: capsule) dimensions./// </summary>/// <paramname="characterRadius">The character's volume radius.</param>/// <paramname="characterHeight">The character's volume height</param>publicvoidSetDimensions(float characterRadius,float characterHeight)/// <summary>/// Specifies the character's bounding volume (eg: capsule) height./// </summary>/// <paramname="characterHeight">The character's volume height</param>public void SetHeight(float characterHeight)/// <summary>/// Returns the character current position./// </summary>public Vector3 GetPosition()/// <summary>/// Update character current position./// If updateGround is true, will find for ground and update character's current ground result./// </summary>public void SetPosition(Vector3 newPosition,bool updateGround =false)/// <summary>/// Returns the character current rotation./// </summary>public Quaternion GetRotation()/// <summary>/// Update character current rotation./// </summary>public void SetRotation(Quaternion newRotation)/// <summary>/// Sets the world space position and rotation of this character./// If updateGround is true, will find for ground and update character's current ground result./// </summary>public void SetPositionAndRotation(Vector3 newPosition,Quaternion newRotation,bool updateGround =false)/// <summary>/// Orient the character's towards the given direction (in world space) using maxDegreesDelta as the rate of rotation change./// </summary>/// <paramname="worldDirection">The target direction in world space.</param>/// <paramname="maxDegreesDelta">Change in rotation per second (Deg / s).</param>/// <paramname="updateYawOnly">If True, the rotation will be performed on the Character's plane (defined by its up-axis).</param>public void RotateTowards(Vector3 worldDirection,float maxDegreesDelta,bool updateYawOnly =true)/// <summary>/// Current plane constraint normal./// </summary>public Vector3 GetPlaneConstraintNormal()/// <summary>/// Defines the axis that constraints movement, so movement along the given axis is not possible./// </summary>public void SetPlaneConstraint(PlaneConstraint constrainAxis,Vector3 planeNormal)/// <summary>/// Returns the given DIRECTION (Normalized) vector constrained to current constraint plane (if _constrainToPlane != None)/// or given vector (if _constrainToPlane == None)./// </summary>public Vector3 ConstrainDirectionToPlane(Vector3 direction)/// <summary>/// Constrain the given vector to current PlaneConstraint (if any)./// </summary>public Vector3 ConstrainVectorToPlane(Vector3 vector)/// <summary>/// Adds a force to the Character./// This forces will be accumulated and applied during Move method call./// </summary>public void AddForce(Vector3 force,ForceMode forceMode =ForceMode.Force)/// <summary>/// Applies a force to this Character that simulates explosion effects./// The explosion is modeled as a sphere with a certain centre position and radius in world space; /// normally, anything outside the sphere is not affected by the explosion and the force decreases in proportion to distance from the centre./// However, if a value of zero is passed for the radius then the full force will be applied regardless of how far the centre is from the rigidbody./// The force direction is from the given origin to the Character center./// </summary>public void AddExplosionForce(float strength,Vector3 origin,float radius,ForceMode forceMode =ForceMode.Force)/// <summary>/// Set a pending launch velocity on the Character. This velocity will be processed next Move call./// </summary>/// <paramname="launchVelocity">The desired launch velocity.</param>/// <paramname="overrideVerticalVelocity">If true replace the vertical component of the Character's velocity instead of adding to it.</param>/// <paramname="overrideLateralVelocity">If true replace the XY part of the Character's velocity instead of adding to it.</param>public void LaunchCharacter(Vector3 launchVelocity,bool overrideVerticalVelocity =false,bool overrideLateralVelocity =false)/// <summary>/// Allows you to explicitly attach this to a moving 'platform' so it no depends of ground state./// </summary>public void AttachTo(Rigidbody parent)/// <summary>/// Temporarily disable ground constraint allowing the Character to freely leave the ground./// Eg: LaunchCharacter, Jump, etc./// </summary>public void PauseGroundConstraint(float unconstrainedTime =0.1f)/// <summary>/// Moves the character along its current velocity./// This performs collision constrained movement resolving any collisions / overlaps found during this movement./// </summary>/// <paramname="deltaTime">The simulation deltaTime.</param>public CollisionFlags Move(float deltaTime)/// <summary>/// Moves the character along the given velocity vector./// This performs collision constrained movement resolving any collisions / overlaps found during this movement./// </summary>/// <paramname="newVelocity">The updated velocity for current frame. It is typically a combination of vertical motion due to gravity and lateral motion when your character is moving.</param>/// <paramname="deltaTime">The simulation deltaTime. If not assigned, it defaults to Time.deltaTime.</param>/// <returns>Return CollisionFlags. It indicates the direction of a collision: None, Sides, Above, and Below.</returns>public CollisionFlags Move(Vector3 newVelocity,float deltaTime)/// <summary>/// Update the character's velocity using a friction-based physical model and move the character along its updated velocity./// This performs collision constrained movement resolving any collisions / overlaps found during this movement./// </summary>/// <paramname="desiredVelocity">Target velocity</param>/// <paramname="maxSpeed">The maximum speed when grounded. Also determines maximum horizontal speed when falling (i.e. not-grounded).</param>/// <paramname="acceleration">The rate of change of velocity when accelerating (i.e desiredVelocity != Vector3.zero).</param>/// <paramname="deceleration">The rate at which the character slows down when braking (i.e. not accelerating or if character is exceeding max speed)./// This is a constant opposing force that directly lowers velocity by a constant value.</param>/// <paramname="friction">Setting that affects movement control. Higher values allow faster changes in direction.</param>/// <paramname="brakingFriction">Friction (drag) coefficient applied when braking (whenever desiredVelocity == Vector3.zero, or if character is exceeding max speed).</param>/// <paramname="gravity">The current gravity force. Defaults to zero.</param>/// <paramname="onlyHorizontal">Determines if the vertical velocity component should be ignored when falling (i.e. not-grounded) preserving gravity effects. Defaults to true.</param>/// <paramname="deltaTime">The simulation deltaTime.</param>/// <returns>Return CollisionFlags. It indicates the direction of a collision: None, Sides, Above, and Below.</returns>public CollisionFlags SimpleMove(Vector3 desiredVelocity,float maxSpeed,float acceleration,float deceleration,float friction,float brakingFriction,Vector3 gravity =default,bool onlyHorizontal =true,float deltaTime =0.0f)/// <summary>/// Return the number of collisions found during last Move call./// </summary>public int GetCollisionCount()/// <summary>/// Retrieves a CollisionResult from last Move call list./// </summary>public CollisionResult GetCollisionResult(int index)/// <summary>/// Makes the character to ignore all collisions vs otherCollider./// </summary>public void IgnoreCollision(Collider otherCollider,bool ignore =true)/// <summary>/// Makes the character to ignore collisions vs all colliders attached to the otherRigidbody./// </summary>public void IgnoreCollision(Rigidbody otherRigidbody,bool ignore =true)/// <summary>/// Makes the character's collider (eg: CapsuleCollider) to ignore all collisions vs otherCollider./// NOTE: The character can still collide with other during a Move call if otherCollider is in CollisionLayers mask./// </summary>public void CapsuleIgnoreCollision(Collider otherCollider,bool ignore =true)/// <summary>/// Sweeps the character's volume along its displacement vector, stopping at near hit point if collision is detected./// Returns True when the rigidbody sweep intersects any collider, otherwise false./// </summary>public bool MovementSweepTest(Vector3 characterPosition,Vector3 sweepDirection,float sweepDistance,outCollisionResult collisionResult){returnMovementSweepTest(characterPosition, velocity, sweepDirection * sweepDistance,out collisionResult);}/// <summary>/// Compute distance to the ground from bottom sphere of capsule and store the result in collisionResult./// This distance is the swept distance of the capsule to the first point impacted by the lower hemisphere,/// or distance from the bottom of the capsule in the case of a raycast./// </summary>publicvoidComputeGroundDistance(Vector3 characterPosition,float sweepRadius,float sweepDistance,float castDistance,outFindGroundResult outGroundResult)/// <summary>/// Sweeps a vertical cast to find the ground for the capsule at the given location./// Will attempt to perch if ShouldComputePerchResult() returns true for the downward sweep result./// No ground will be found if collision is disabled (eg: detectCollisions == false)./// </summary>public void FindGround(Vector3 characterPosition,outFindGroundResult outGroundResult)/// <summary>/// Checks if any colliders overlaps the character's capsule-shaped volume in world space using testHeight as capsule's height./// Returns true if there is a blocking overlap, false otherwise./// </summary>public bool CheckCapsule()/// <summary>/// Checks if any colliders overlaps the character's capsule-shaped volume in world space using testHeight as capsule's height./// Returns true if there is a blocking overlap, false otherwise./// </summary>public bool CheckHeight(float testHeight)/// <summary>/// Casts a ray, from point origin, in direction direction, of length distance, against specified colliders (by layerMask) in the Scene./// </summary>public bool Raycast(Vector3 origin,Vector3 direction,float distance,int layerMask,outRaycastHit hitResult,float thickness =0.0f)/// <summary>/// Check the given capsule against the physics world and return all overlapping colliders./// Return overlapped colliders count./// </summary>public int OverlapTest(Vector3 characterPosition,Quaternion characterRotation,float testRadius,float testHeight,int layerMask,Collider[] results,QueryTriggerInteraction queryTriggerInteraction)/// <summary>/// Check the character's capsule against the physics world and return all overlapping colliders./// Return an array of overlapped colliders./// </summary>public Collider[] OverlapTest(int layerMask,QueryTriggerInteraction queryTriggerInteraction,outint overlapCount)/// <summary>/// Check the given capsule against the physics world and return all overlapping colliders./// Return an array of overlapped colliders./// </summary>public Collider[] OverlapTest(Vector3 characterPosition,Quaternion characterRotation,float testRadius,float testHeight,int layerMask,QueryTriggerInteraction queryTriggerInteraction,outint overlapCount)
Callbacks
/// <summary>/// Let you define if the character should collide with given collider./// </summary>/// <paramname="collider">The collider.</param>/// <returns>True to filter (ignore) given collider, false to collide with given collider.</returns>publicdelegateboolColliderFilterCallback(Collider collider);/// <summary>/// Let you define the character behaviour when collides with collider./// </summary>/// <paramname="collider">The collided collider</param>/// <returns>The desired collision behaviour flags.</returns>publicdelegateCollisionBehaviourCollisionBehaviourCallback(Collider collider);/// <summary>/// Let you modify the collision response vs dynamic objects,/// eg: compute resultant impulse and / or application point (CollisionResult.point)./// </summary>publicdelegatevoidCollisionResponseCallback(refCollisionResult inCollisionResult,refVector3 characterImpulse,refVector3 otherImpulse);/// <summary>/// Let you define if the character should collide with given collider./// Return true to filter (ignore) collider, false otherwise./// </summary>public ColliderFilterCallback colliderFilterCallback/// <summary>/// Let you define the character behaviour when collides with collider./// </summary>public CollisionBehaviourCallback collisionBehaviourCallback/// <summary>/// Let you modify the collision response vs dynamic objects,/// eg: compute resultant impulse and / or application point (CollisionResult.point)./// </summary>public CollisionResponseCallback collisionResponseCallback
Events
/// <summary>/// Event triggered when characters collides with other during a Move./// Can be called multiple times./// </summary>public event CollidedEventHandler Collided;/// <summary>/// Event triggered when a character finds ground (walkable or non-walkable) as a result of a downcast sweep (eg: FindGround method)./// </summary>public event FoundGroundEventHandler FoundGround;