Creating an Advanced Rigidbody Character Controller with Unity C#
Developing an advanced Rigidbody-based character controller in Unity is a challenging yet rewarding process. In this article, I will take you through the work I did to create a responsive and fluid character controller in three key sections: Player Movement, Camera Movement, and bonus advanced movement mechanics. You can also check out how I used this in a projects
Player Movement
Part 1: Raycast Sensor
The first component of the player movement is the Raycast Sensor. This is a generic script that casts a Physics.Raycast
and stores the results, allowing for flexible and reusable object detection logic. The direction of the raycast can be specified, making it adaptable for different use cases. For our use case in the character controller it will check for ground detection.
Check The Raycast Code
Part 2: Player Mover
This component is the interface that interacts with the Rigidbody to handle the actual movement of the player. It manages the capsule collider settings (height, thickness, and offset) and the player's step height ratio, which, using the Raycast Sensor, determines how the player hovers above the ground. This hovering creates space under the player, allowing for smooth movement over bumps and steps.
Based on these settings, the currentGroundAdjustmentVelocity
is recalculated each fixed frame to keep the player grounded, which is layered on the player velocity with a public SetVelocity
function.
Check Ground Adjustment Velocity Calulation
Part 3: Player Controller
The Controller is responsible for calculating the player's momentum and velocity based on input and the current state. It feeds this information into the Player Mover to move the player accordingly.
Check How to Calculate the Momentum and Velocity
To manage the state of the player effectively, I implemented a State Machine. This state machine allows for cleaner, modular control of different player behaviors depending on conditions like grounded, the ground too steep, or jump key is pressed. For more information on state machines, check out the following video.
Check State Machine States and Transitions
Third Person Camera Movement
Part 1: Camera Controller
This Controller is responsible for handling the rotation of the camera based on player input. In the Update method, it reads the input for looking around and then rotates the camera angles accordingly. If smooth rotation is enabled, ithe input value is the interpolated for smoother experience. The horizontal and vertical angles are updated and clamped to prevent excessive rotations.
Check Camera Rotation Code
Part 2: Camera Collision
The Camera Collision component ensures that the camera maintains an appropriate distance from the player while avoiding obstacles. It adjusts the camera's position smoothly, using sphere casting to detect collisions and prevent clipping.
Check Camera Collision Code
Bonus: Advanced Movement mechanics
With the base character controller setup, this controller is highly flexible. Here are some advanced gemplay mechanics that I achieved with it's movement
Gravity Angle Flip
This mechanic shifts an object's gravity angle by aligning its "up" direction with a new direction. Quaternion.FromToRotation
calculates the smallest rotation to transform the object's current "up" vector into the target direction. The resulting rotation is applied to the object's current orientation, updating its alignment.
Check The Code
Gravity Well Loop
A TriggerArea
is a component I wrote that keeps track of all ridigbodies that goes inside of a trigger.
This mechanic rotates the rigidbodies inside this area to align with a dynamically shifted up direction. The process starts by calculating the vector from the loop's center to the rigidbody (Red), projecting it onto the loop's forward direction and adding the loop's position to determine the shifted center point (Green). The vector from the rigidbody to this shifted center (Yellow) becomes the new upward direction. The rigidbody is then rotated smoothly using Quaternion.FromToRotation
to align with this direction, creating the effect of being pushed from the loop's center.
Check The Code
Moving Platforms
For moving platforms that move over time, the key is to store the displacement the platform makes in each update frame and then apply that same displacement to all rigidbodies within an invisible trigger area so they are are smoothly shifted by the same amount using Rigidbody.MovePosition
.
Check The Code