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

Example of grid uses in games

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