Character
Description
The Character class serves as the foundational element for all game avatars, whether under player control or managed by AI. This class employs the CharacterMovement component as its character controller / motor, seamlessly integrating typical modes of movement, such as walking, falling, flying, and swimming. Additionally, it boasts fully configurable jump and crouch mechanics.
The Character class dictates its movement based on its current mode, delegating the execution of this movement to the CharacterMovement component, which ultimately performs the physical motion.
How Does a Character Work?
By default, a Character implements an auto-simulation, taking on the responsibility of invoking its Simulate method. This method undertakes the heavy lifting of the character's movement, including updating its current movement mode, adjusting rotation based on its current rotation mode, handling jump and crouch actions, triggering events, and more.
When auto-simulation is enabled (the default operation), the Character implements a LateFixedUpdate through a coroutine. This ensures that the Character is simulated after Unity's internal physics update, a crucial step to deliver seamless physical interactions.
In essence, a Character interprets input vectors and events, including keyboard inputs used to control the character. It translates these inputs into updates for the character's position and rotation. This process occurs through its different movement modes, such as walking, flying, swimming or any custom mode you may create. Each mode defines how inputs are processed and influences the resulting outputs.
All of this occurs within the Simulate method, with the following execution order:
public void Simulate(float deltaTime)
{
if (isPaused)
return;
BeforeSimulationUpdate(deltaTime);
SimulationUpdate(deltaTime);
AfterSimulationUpdate(deltaTime);
CharacterMovementUpdate(deltaTime);
}Where:
This is called before any movement mode is updated. It automatically switches between the Walking and Falling movement modes based on the CharacterMovement grounding status and handles the jump and crouch mechanics.
Finally, it triggers the OnBeforeSimulationUpdate event, allowing us to expand its functionality.
This utilizes the given movement direction vector (SetMovementDirection) to calculate a desired velocity vector. Subsequently, this information is handed over to the current movement mode, which, in turn, adjusts the character's velocity according to its defined rules.
Additionally, it performs the character's rotation according to its current rotation mode.
This method is responsible for triggering the ReachedJumpApex and AfterSimulationUpdated events. At this point, the character's rotation and velocity are updated according to its current modes, but the movement has not been applied yet. This occurs in the subsequent CharacterMovementUpdate method.
This final simulation method passes the character's velocity to the CharacterMovement Move method, which subsequently executes the requested movement. Finally, it triggers the CharacterMovementUpdated event. At this point, the character's state is up to date.
Character Movement Modes
The Character class introduces the concept of Movement Modes, such as Walking, Falling, Flying, Swimming, and Custom. Each movement mode has a set of predefined rules and properties that dictate how the character moves through the world. While this concept is somewhat related to character states (e.g., logical states like Jumping, Attacking, Dying, etc.), it should not be confused. The primary purpose of a Movement Mode is to determine how the character should move through the world.
For instance, the Flying Movement Mode, while implying that the character is in a flying logical state, specifically defines how the character is moved. For example, it allows the character to move through the air without being affected by gravity, giving it the freedom to remain unbound to the ground, with its vertical velocity preserved.
The movement modes are defined as follows:
You can change the character's movement mode by using its SetMovementMode function. This action automatically invokes the OnMovementModeChanged method and triggers the MovementModeChanged event. The OnMovementModeChanged method is specifically designed to handle the initiation of particular modes, such as enabling or disabling ground constraints, stopping jumps, resetting jump counts, and more.
It's important to note that Walking and Falling modes are automatically managed based on the character’s grounding status. When the character is constrained to the ground on a walk-able surface, the Walking movement mode is enabled. Conversely, if the character is not on the ground or is on a non-walkable surface, it will switch to the Falling movement mode.
For the Flying movement mode, you need to explicitly enable and disable it as necessary. Exiting the Flying state is safely achieved by transitioning to the Falling movement mode, which, in turn, automatically leads to the Walking mode. This principle also applies to Custom movement modes.
Moving a Character
A Character comprises a set of methods specifically crafted to streamline the execution of actions, usually in response to input events like on-down, on-up, and so forth.
The methods incorporated are:
Movement Relative to Camera
It is often necessary to align our character's movement with the camera's orientation, ensuring consistent motion regardless of the camera's viewing direction. To simplify this process, ECM2 incorporates a helpful Vector3 extension method:
This can be employed to align our movement direction with the character's follow camera as follows:
An alternative approach, without utilizing the relativeTo extension method, is demonstrated below:
The primary distinction lies in the relativeTo extension method, allowing us to execute the transformation on the plane defined by the given world-up axis. This becomes crucial when working with custom world-up directions, as seen in games like Mario Galaxy.
Character Rotation Modes
A Character, although it can be rotated as desired by directly modifying its rotation property, also incorporates a set of predefined Rotation Modes, similar to the Movement Modes.
The rotation modes are defined as follows:
The Character incorporates the following functions designed for alter its rotation:
This are processed on the next simulation update.
Ground Constraint
The CharacterMovement component, serving as our character controller / motor, introduces a crucial feature absent in Unity's built-in character controller—the GroundConstraint. This feature plays a pivotal role in ensuring the character remains grounded on walk-able surfaces, preventing unintended launches when moving at higher speeds. However, this enhancement necessitates informing the system when the character is permitted to disengage from the ground, for instance, when engaging in activities like climbing, flying, swimming, or jumping.
To achieve this, we leverage the constrainToGround property of the CharacterMovement component, allowing us to explicitly enable or disable it. Alternatively, the Character PauseGroundConstraint method can be employed to temporarily suspend the ground constraint for a specified duration (N seconds),
For example, a basic jump:
Events
The Character class provides a diverse array of events and event handlers that can be utilized for responsive actions either within the local context (e.g., within a derived class of Character) or externally.
The included events are:
And its corresponding virtual methods that raises the events are:
In order to receive the ReachedJumpApex event, it is imperative to set the notifyJumpApex property to true beforehand; otherwise, this event will not be triggered.
For example:
Collisions
While you have the flexibility to utilize Unity's OnTriggerXXX and OnEnterXXX events, the Character class (just like the Unity built-in cc) encompasses dedicated methods and events specifically designed to manage character collisions detected during its most recent CharacterMovement.Move invocation.
Custom Character Class Responding to Collision Events:
Character Class Responding to Collision Events:Moreover, you can conveniently access all collisions found during the last movement in an iterative manner, as demonstrated below:
Listening to Character Collision Events
Character Collision EventsNon Character-based classes should subscribe to the Character Collided event, for example:
Collision Filtering
In many game scenarios, there is a need to ignore collisions with another character, a specific collider, or even all colliders attached to a rigid body. To address this, the CharacterMovement component incorporates a set of functions that allow you to conveniently define your desired interaction.
When finer control is necessary, the CharacterMovement ColliderFilterCallback comes into play. This allows you to selectively ignore specific colliders based on your game criteria.
For example, to ignore collisions against other characters using the CharacterMovement component:
Physics Interactions
When enabled, a Character can interact with other rigidbodies—pushing or being pushed—and other characters, applying forces, explosion forces, downward forces, etc. These interactions are managed by the CharacterMovement component, and by default, the resulting behavior is influenced by the masses of the characters, where larger mass will push others more easily, and vice versa.
This default behavior can be modified using the CharacterMovement collisionResponseCallback function. This allows us to adjust the computed collision response impulses in any manner that our game may require.
The callback function prototype is declared as follows:
Where 'impulses' represent the computed resulting collision response impulses, and this can be modified as needed.
Additionally, the Character class includes a set of functions to externally influence the character. These functions are similar to Unity's Rigidbody functions, such as AddForce, AddExplosionForce, and a custom one called LaunchCharacter.
Animating a Character
When animating a character, you should query the character's state and/or subscribe to its numerous events, feeding this information to your AnimationController parameters to ensure your animation stays perfectly in sync with the character's state, such as whether it is grounded, falling, jumping, etc.
ECM2 does not require the use of animation or any specific animation techniques. You have the freedom to animate your characters using 'plain Unity code' or your preferred method.
Querying Character State
The Character class provides an extensive array of methods, events, and delegates that you can leverage to access the character's information, including GetPosition, GetRotation, GetVelocity, IsWalking, IsFalling, IsOnWalkableGround, etc. This should be utilized to maintain seamless synchronization in your animations.
In addition to the information provided by the Character class, you can access further details through the CharacterMovement component. This includes ground-related information, retrieval and adjustment of capsule collider dimensions, access to collision detection functions, and even the ability to compute a new set of ground-related details (e.g., is walk-able, is a step, distance to the ground, etc.).
The following example illustrates how to animate the 'UnityCharacter' using the information provided by the Character to feed parameters into its Animator.
As you can see, it updates the Animator parameters based on the information provided by the Character component.
Utilizing Root Motion
Root motion refers to motion integrated directly into the animation, where the animation itself dictates how far an object moves rather than relying on code.
To incorporate root motion in a Character-derived class, follow these steps:
Add the
RootMotionControllercomponent to your model'sGameObject. ThisRootMotionControlleris responsible for providing the animation's velocity, rotation, etc., to theCharacter.Enable the
useRootMotionproperty in theCharacter. This property can be toggled as needed.
Once a character is moved using root motion, the animation assumes complete control over the character's movement. This replaces all procedural movement, rendering properties such as maxWalkSpeed, maxFallSpeed, etc., irrelevant, as the character is entirely driven by the animation.
Worth noting, the character's ground constraint still applies when root motion is enabled. This implies that any vertical movement in your root motion-based animation won't function unless you explicitly disable this constraint. In such cases, it's advisable to assign the flying movement mode, as it automatically disables the ground constraint and allows for full vertical movement.
Last updated