This document is about: QUANTUM 2
SWITCH TO

Character Controllers

MovementSystem and CustomKCCSystem

Each character controller has its own Update function located in the KCC Behaviour asset implementation. The KCC component references a behavior asset which is then loaded in the CustomKCCSystem in order to execute the behaviour.

C#

var behaviour = f.FindAsset<CustomKCCBehaviour>(filter.KCC->Behavior.Id);
behaviour.UpdateKCC(f, ref filter);

Then, the MovementSystem sends the input direction to the Move function in the behavior asset. This function will set up and validate the movement to the next frame update.

C#

var input = f.GetPlayerInput(filter.PlayerLink->Id);
var behaviour = f.FindAsset<CustomKCCBehaviour>(filter.KCC->Behavior.Id);

if (input->Jump.WasPressed && filter.KCC->Grounded){
    behaviour.Jump(f, filter.KCC);
}

behaviour.Move(f, filter.Entity, FPVector2.Right * input->MovementDirection.X, filter.KCC);

Each character controller behavior will handle this direction with its own implementation. Also, this system will trigger the events that updates the visual of the character.

Common concepts between the two KCCs

The two character controllers share some values that are generic to any platformer game system. The static values used as parameters for the character movement were grouped in an asset called CustomKccConfig. The runtime values used to define the state of the character were grouped in the component CustomKCC. The main difference between the two KCCs is how these assets and components are used in the systems.

Assets

The CustomKccConfig is the asset with the main parameters to be used by any character controller that inherits from CustomKCCBehaviour.Core. These parameters are generic and can be used or ignored by the character controller if necessary.

Parameters Description
JumpImpulse The base value to calculate the force applied to jump.
ShapeExtents The extents (half total size) used in the box shaped query.
Offset The offset of the box shape.
MaxSpeed The max velocity that the character can reach while running.
Gravity The falling velocity of the character. It is limited by Max Vertical Speed but the higher the gravity value, the faster the character accelerates downwards.
SlopeAngle The maximum angle value that can be interpreted as ground. Values greater than this are considered walls or roofs.
Braking The value subtracted from the velocity, applied every frame when the character is not moving on the ground. This value is multiplied by deltaTime.
AirBraking The value subtracted from the velocity, applied every frame when the character is not moving in the air. This value is multiplied by deltaTime.
AirAcceleration The value added to the velocity when the character is moving in the air. This value is multiplied by deltaTime.
Acceleration The value added to the velocity when the player is moving in the ground. This value is multiplied by deltaTime.
MaxVerticalSpeed Velocity limit when the character is falling or jumping.
CCDContinuousThreshold If the velocity of movement is greater than this value, then the character controller uses a shape cast to avoid penetrating or passing through meshes.
DrawDebug Enable the drawing of some lines, points and squares to debug the character status.

Components

The components has fields that can be used by any of the showcased game mechanic, but some of these fields are used only by the Advanced KCC.

Attributes Description
Config The reference to the `CustomKccConfig` asset with the main movement parameters.
Behavior The reference to the `CustomKCCBehaviour` asset with the main functions of the character.
Velocity The character’s runtime velocity vector.
QueryResult The data computed every frame during the collisions.
State Used by the Advanced KCC, this enum stores the character's current state, such as `Grounded` or `Jumping`.
IsGrounded A boolean value that specifies if the character is touching the ground at this frame.
CharacterDirection The direction to which the character is looking towards. A value greater than 0 means Right and a value smaller than 0 means left.
MaxSpeed The maximum allowed velocity value, which can be tweaked in runtime.
LastPosition The character’s last world position which is used in the position correction when the character penetrates a space smaller than its own size.
InputSpeed Value used by the `Advanced KCC` as the magnitude and the movement direction. This value is interpolated based on acceleration or brake values.

Query result

This is a struct with the main values computed during the collision. The values are updated in the UpdateKCC function by the CustomKCCSystem. These values are used by the game mechanics to poll the character’s state and details of the character and what it is colliding with.

Attributes Description
Normal The collision’s normal vector with any object, used to know the collision direction.
Angle The angle of the ground check used to identify slopes. The Advanced KCC uses this value to calculate the direction of movement. In the air, the value is always 0.
IsTouching A boolean value that is true during frames when the character is colliding with something.
IsDynamic A boolean value that is true when the character touches a dynamic collider.
BroadphaseQueryIndexPlusOne Value used to register the query added to the broadphase.
GroundStatus A byte used to identify if the character is on an edge. The byte value can be compared with the enum EdgeDirection.

Collision Signal

In every frame where the character collides with something, it sends KCCCallbacks signals with the character entity and the hit object. For example, the moving platforms uses this callback to check if the character is touching it and making it stick to the platform movement.

Also, there is the OnKCCTunnelingCorrection that is triggered when the character has its velocity and position corrected by the anti-tunneling algorithm. Some systems like the DashSystem need to be interrupted when this happens.

Simple Kinematic Character Controller

This character controller is similar to the default Quantum’s character controller but it uses a 2D box as the shape on the physics query. This type of shape is good to be used in games with tilemaps like Tower Fall or Spelunker. Also, this sample uses only one box physics query which is a lightweight implementation that can be used if there are other resource intensive systems competing for the CPU budget, or if the amount of simultaneous KCCs gets high enough to become a performance bottleneck.

Ground detection

The Quantum default character controller uses a circle shape query with an extended value to detect the ground. This KCC uses the angle of the QueryResult from the box shaped query. If the angle is lower than the SlopeAngle in the asset config, then it recognizes that as ground.

simple kcc normal

Movement

The simple KCC movement has two components:

  • Horizontal Velocity: Changed only when the character receives input to the left or right.
  • Vertical Velocity: Changed when the character jumps or is falling from a platform.

Advanced Kinematic Character Controller

This character controller is recommended to be used in games with a complex level design where the terrain has curves and there are too many physics interactions with the dynamic body.

Workflow

This is the step-by-step definition of how this character controller works:

  1. BroadPhaseQueryKCCSystem: Register the box shape query into the broad phase.
  2. PhysicsSystem2D: The physics math is computed here, then the results are stored.
  3. CustomKCCSystem: Evaluate all collisions and push the character outside if it penetrates some mesh. Then execute the respective update to the KCCs state machines.
  4. MovementSystem: Applies the player inputs to the character controller.

State Machine

The state machine defines different behaviors for each state. This resource helps avoiding doing unnecessary line casts and allows modularizing each behavior. In summary, there are 3 states:

States Description
Grounded Happens when the character is on the ground or falls off a platform. It uses a raycast to calculate the movement direction and maintain the character on the ground.
Jumping Starts when the player presses the jumping button and ends when the character touches the ground.
GrabOnEdge Happens when the character touches an edge during the Jumping state. The character will stop moving until performing a jump again.

Each state has different behaviors:

  1. On Grounded State:
      a. It does a raycast to detect the normal vector in relation to the ground.
       b. Try to detect edges on the floor.
       c. If it is grounded, it does a raycast forward to detect slopes.
       d. Otherwise, it tries to see ledges to grab.
       e. The velocity is applied to the character position.
  2. On Jumping State:
       a. It does a raycast to see two-way platforms.
       b. Tries to detect ledges to grab.
       c. The velocity is applied to the character position.
       d. To each collision in this frame:
         i. If the collision is with the ground it changes the state to Grounded.
         ii. If it is with the roof, it changes the y velocity to 0.
         iii. if it is with a wall it changes the x velocity to 0.
  3. On Grab on Edge:
       a. Wait for jump input to go to the Jump state.

More states can be added if necessary.

Movement

Differently from the simple KCC, this character controller uses the InputSpeed attribute as a velocity magnitude to the left or right and uses the normal vector of the terrain mesh to know the final movement direction. This results in better stability on the ground and smoother movement. Also, it uses some queries to analyze the space around and better detect collisions.

Queries

Ground Line Query: A raycast downwards to get the angle of the ground. This angle determines the movement direction.

smooth movement

The direction of the movement will be based on the normal perpendicular vector but to avoid problems with platform edges, the raycast targets the point of collision if the shape is touching something. Otherwise, it targets down.

The same raycast is used during the Jumping state to evaluate if the floor is a two-way platform and change the state to Grounded.

Forward Line Query: A forward linecast to detect the slope before the collision which is used to avoid penetration before moving in the slope.

wall detection

Improving the movement flow

Maintaining the character on the ground is important for a good movement feeling. Using only the normal contact point isn’t enough because the character can fall out of the platform.

movement extrapolation

Improvement - Reduce the movement angle from 90 to 80 degrees (or any arbitrary value).

fix movement

NOTE: Although the character is out of the platform, other forces like gravity will push it down. Also, a smooth level design is better to avoid this problem.

Border Line Query: A raycast to detect edges when the character is jumping. It works only if the Y velocity of the character is lower than 0 and the Y normal of the raycast is exactly 1.

ledge detection

This raycast is used for the character to grab on walls when it is jumping.

Back to top