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.
In this example, we will animate the included UnityCharacter model, syncing its animator with the information provided by the Character class.
/// <summary>/// This example shows how to animate a Character,/// using the Character data (movement direction, velocity, is jumping, etc) to feed your Animator./// </summary>publicclassAnimationController:MonoBehaviour{ // Cache Animator parametersprivatestaticreadonlyint Forward =Animator.StringToHash("Forward");privatestaticreadonlyint Turn =Animator.StringToHash("Turn");privatestaticreadonlyint Ground =Animator.StringToHash("OnGround");privatestaticreadonlyint Crouch =Animator.StringToHash("Crouch");privatestaticreadonlyint Jump =Animator.StringToHash("Jump");privatestaticreadonlyint JumpLeg =Animator.StringToHash("JumpLeg"); // Cached CharacterprivateCharacter _character;privatevoidAwake() { // Cache our Character _character =GetComponentInParent<Character>(); }privatevoidUpdate() {float deltaTime =Time.deltaTime; // Get Character animatorAnimator animator =_character.GetAnimator(); // Compute input move vector in local spaceVector3 move =transform.InverseTransformDirection(_character.GetMovementDirection()); // Update the animator parametersfloat forwardAmount =_character.useRootMotion&&_character.GetRootMotionController()?move.z:Mathf.InverseLerp(0.0f,_character.GetMaxSpeed(),_character.GetSpeed());animator.SetFloat(Forward, forwardAmount,0.1f, deltaTime);animator.SetFloat(Turn,Mathf.Atan2(move.x,move.z),0.1f, deltaTime);animator.SetBool(Ground,_character.IsGrounded());animator.SetBool(Crouch,_character.IsCrouched());if (_character.IsFalling())animator.SetFloat(Jump,_character.GetVelocity().y,0.1f, deltaTime); // Calculate which leg is behind, so as to leave that leg trailing in the jump animation // (This code is reliant on the specific run cycle offset in our animations, // and assumes one leg passes the other at the normalized clip times of 0.0 and 0.5)float runCycle =Mathf.Repeat(animator.GetCurrentAnimatorStateInfo(0).normalizedTime+0.2f,1.0f);float jumpLeg = (runCycle <0.5f?1.0f:-1.0f) * forwardAmount;if (_character.IsGrounded())animator.SetFloat(JumpLeg, jumpLeg); }}
As you can observe, this is standard Unity code that leverages the character's state information to control our animator.
Root Motion
The Character class, includes built-in support for root motion.
Utilizing Root Motion
To enable root motion, follow these steps:
Add the RootMotionController component to your model's GameObject. This RootMotionController is responsible for providing the animation's velocity, rotation, etc., to the Character.
Enable the useRootMotion property in the Character. 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 vertical movement.
To enable vertical root motion movement, the character's movement mode must be set to Flying.
Toggling Root Motion
This example demonstrates how to toggle root motion at run-time. In this case, enabling root motion only while the character is in the Walking movement mode.
publicclassRootMotionToggle:MonoBehaviour{privateCharacter _character;privatevoidOnMovementModeChanged(Character.MovementMode prevMovementMode,int prevCustomMovementMode) { // Allow root motion only while walking_character.useRootMotion=_character.IsWalking(); }privatevoidAwake() { _character =GetComponent<Character>(); }privatevoidOnEnable() { // Subscribe to Character events_character.MovementModeChanged+= OnMovementModeChanged; }privatevoidOnDisable() { // Un-Subscribe from Character events_character.MovementModeChanged-= OnMovementModeChanged; }}
In this example, we utilize the MovementModeChange event, which is triggered each time the character changes its current movement mode, making it perfect this case.