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.
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:
- BroadPhaseQueryKCCSystem: Register the box shape query into the broad phase.
- PhysicsSystem2D: The physics math is computed here, then the results are stored.
- CustomKCCSystem: Evaluate all collisions and push the character outside if it penetrates some mesh. Then execute the respective update to the KCCs state machines.
- 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:
- 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. - 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. - 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.
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.
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.
Improvement - Reduce the movement angle from 90 to 80 degrees (or any arbitrary value).
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.
This raycast is used for the character to grab on walls when it is jumping.
Back to top