|
| 1 | +--- |
| 2 | +order: 2 |
| 3 | +title: Animation Controller |
| 4 | +type: Animation |
| 5 | +label: Animation |
| 6 | +--- |
| 7 | + |
| 8 | +The Animation Controller ([AnimatorController](/apis/core/#AnimatorController)) is used to organize [animation clips](/en/docs/animation-clip) ([AnimationClip](/apis/core/#AnimationClip)) to achieve more flexible and rich animation effects. |
| 9 | + |
| 10 | +## Editor Usage |
| 11 | + |
| 12 | +### Basic Usage |
| 13 | + |
| 14 | +Through the editor of the animation controller, users can organize the playback logic of [animation clips](/en/docs/animation-clip}). |
| 15 | + |
| 16 | +1. Prepare the animation clips ([Create animation clips](/en/docs/animation-clip})) |
| 17 | + |
| 18 | + |
| 19 | + |
| 20 | +2. To organize the playback of these animation clips, we need to create an animation controller ([AnimatorController](/apis/core/#AnimatorController})) asset |
| 21 | + |
| 22 | + |
| 23 | + |
| 24 | +3. The newly created animation controller has no data, we need to edit it, double-click the asset, and add an AnimatorState to it |
| 25 | + |
| 26 | + |
| 27 | + |
| 28 | +4. Click on AnimatorState to bind an animation clip to it |
| 29 | + |
| 30 | + |
| 31 | + |
| 32 | +5. Bind this animation controller ([AnimatorController](/apis/core/#AnimatorController})) asset to the [animation control component](/en/docs/animation-animator}) |
| 33 | + |
| 34 | + |
| 35 | + |
| 36 | +6. Now you can play the `run` animation by `animator.play("New State")` in your exported project |
| 37 | + |
| 38 | +You can achieve more functions through the editor of the animation controller: |
| 39 | + |
| 40 | +### Default Play |
| 41 | + |
| 42 | +Connect AnimatorState to `entry`, and the animation on it will automatically play when your exported project runs, without the need to call `animator.play`. At the same time, you will see the model in the editor also start playing the animation. |
| 43 | + |
| 44 | + |
| 45 | + |
| 46 | +### Animation Transition |
| 47 | + |
| 48 | +Connect two `AnimatorState` you want to transition between to achieve the effect of animation transition. Click on the line between the two animations to modify the parameters of the animation transition for adjustment. |
| 49 | + |
| 50 | + |
| 51 | + |
| 52 | +#### Parameter Description |
| 53 | + |
| 54 | +| Property | Function Description | |
| 55 | +| :------- | :------------------------------------------------------------------- | |
| 56 | +| duration | Transition duration, time is normalized time relative to the target state, default value is 1.0 | |
| 57 | +| offset | Forward offset time of the target state, time is normalized time relative to the target state, default value is 0 | |
| 58 | +| exitTime | Start state transition start time, time is normalized time relative to the start state, default value is 0.3 | |
| 59 | + |
| 60 | +### Animation Layering |
| 61 | + |
| 62 | +The Galacean engine supports multi-layer animation layering. Animation layering is achieved through blending between `AnimatorControllerLayer`. The first layer is the base animation layer, modifying its weight and blending mode will not take effect. |
| 63 | + |
| 64 | +Double-click the `AnimatorController` resource file to edit the animation, add a layer, and connect the blended actions to `entry`. |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | +Sometimes you may want to achieve a fixed pose, you need to trim the animation slices provided by the designer, you can modify the `StartTime` and `EndTime` of `AnimatorState`, click on `AnimatorState` to edit it: |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +| Property | Description | |
| 74 | +| :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 75 | +| Name | Modify the name of `AnimatorState`, the name should be **unique** in the layer it belongs to. | |
| 76 | +| AnimationClip | Used to bind the `AnimationClip` asset, `AnimationClip` stores the animation data of the model. | |
| 77 | +| WrapMode | Whether `AnimatorState` loops or plays once, default is `Once` which means play once. | |
| 78 | +| Speed | The playback speed of `AnimatorState`, default value is 1.0, the larger the value, the faster the animation speed. | |
| 79 | +| StartTime | Where `AnimatorState` starts playing from in the `AnimationClip`, time is normalized time relative to the duration of `AnimationClip`. Default is 0, starting from the beginning. For example, if the value is 1.0, it is the last frame state of `AnimationClip`. | |
| 80 | +| EndTime | Where `AnimatorState` stops playing in the `AnimationClip`, time is normalized time relative to the duration of `AnimationClip`. Default is 1.0, playing until the end. | |
| 81 | + |
| 82 | +You can also adjust the weight of the `Layer` in the blend by modifying the `Weight` parameter of the `Layer`, and modify the blending mode by changing the `Blending`. |
| 83 | + |
| 84 | + |
| 85 | + |
| 86 | +| Property | Description | |
| 87 | +| :-------- | :-------------------------------------------------------------------------- | |
| 88 | +| Name | The name of this layer. | |
| 89 | +| Weight | The blend weight of this layer, default value is 1.0. | |
| 90 | +| Blending | The blending mode of this layer, `Additive` for additive mode, `Override` for override mode, default is `Override`. | |
| 91 | + |
| 92 | +## Script Usage |
| 93 | + |
| 94 | +> Before using the script, it is recommended to read the [Animation System Composition](/en/docs/animation-system) documentation to help you better understand the operation logic of the animation system. |
| 95 | +
|
| 96 | +### Default Playback |
| 97 | + |
| 98 | +You can set the default playback animation for the layer by setting the [defaultState](/apis/core/#AnimatorStateMachine-defaultState) of the AnimatorStateMachine. This way, when Animator `enabled=true`, you do not need to call the `play` method to play the default animation. |
| 99 | + |
| 100 | +```typescript |
| 101 | +const layers = animator.animatorController.layers; |
| 102 | +layers[0].stateMachine.defaultState = animator.findAnimatorState("walk"); |
| 103 | +layers[1].stateMachine.defaultState = animator.findAnimatorState("sad_pose"); |
| 104 | +layers[1].blendingMode = AnimatorLayerBlendingMode.Additive; |
| 105 | +``` |
| 106 | + |
| 107 | +### Animation Transition |
| 108 | + |
| 109 | +You can implement transitions between animation states by adding `AnimatorTransition` to `AnimatorState`. |
| 110 | + |
| 111 | +```typescript |
| 112 | +const walkThenRunState = animatorStateMachine.addState("walkThenRun"); |
| 113 | +walkThenRunState.clip = walkClip; |
| 114 | +const runState = animatorStateMachine.addState("run"); |
| 115 | +runState.clip = runClip; |
| 116 | +const transition = new AnimatorStateTransition(); |
| 117 | +transition.duration = 1; |
| 118 | +transition.offset = 0; |
| 119 | +transition.exitTime = 0.5; |
| 120 | +transition.destinationState = runState; |
| 121 | +walkThenRunState.addTransition(transition); |
| 122 | +animator.play("walkThenRun"); |
| 123 | +``` |
| 124 | + |
| 125 | +By doing this, every time you play the `walkThenRun` animation in the layer of the animation state machine, it will transition from the `walk` animation to the `run` animation halfway through the `walk` animation. |
| 126 | + |
| 127 | +### Animation Overlay |
| 128 | + |
| 129 | +To overlay animations, add the desired animation state to another layer and set its blending mode to `AnimatorLayerBlendingMode.Additive` to achieve the animation overlay effect. |
| 130 | + |
| 131 | +<playground src="skeleton-animation-additive.ts"></playground> |
| 132 | + |
| 133 | +### Animation Data |
| 134 | + |
| 135 | +#### Setting Animation Data |
| 136 | + |
| 137 | +You can set the animation data of the animator controller using the [animatorController](/apis/core/#Animator-animatorController) property. A default AnimatorController will be automatically added when a GLTF model is loaded. |
| 138 | + |
| 139 | +```typescript |
| 140 | +animator.animatorController = new AnimatorController(); |
| 141 | +``` |
| 142 | + |
| 143 | +#### Reusing Animation Data |
| 144 | + |
| 145 | +Sometimes the animation data of a model is stored in another model, and you can import and use it as follows: |
| 146 | + |
| 147 | +<playground src="skeleton-animation-reuse.ts"></playground> |
| 148 | + |
| 149 | +In addition, the [AnimatorController](/apis/core/#AnimatorController) of the Animator is a class for storing data and does not contain runtime data. Based on this design, as long as the hierarchical structure and naming of the **skeleton nodes** of the model bound to the Animator component are the same, we can reuse the animation data. |
| 150 | + |
| 151 | +```typescript |
| 152 | +const animator = model1.getComponent(Animator); |
| 153 | +animator.animatorController = model2.getComponent(Animator).animatorController; |
| 154 | +``` |
| 155 | + |
| 156 | +### State Machine Script |
| 157 | + |
| 158 | +<playground src="animation-stateMachineScript.ts"></playground> |
| 159 | + |
| 160 | +The state machine script provides users with lifecycle hook functions for animation states to write their own game logic code. Users can use the state machine script by inheriting from the [StateMachineScript](/apis/core/#StateMachineScript) class. |
| 161 | + |
| 162 | +The state machine script provides three animation state cycles: |
| 163 | + |
| 164 | +- `onStateEnter`: Callback when the animation state starts playing. |
| 165 | +- `onStateUpdate`: Callback when the animation state is updated. |
| 166 | +- `onStateExit`: Callback when the animation state ends. |
| 167 | + |
| 168 | +```typescript |
| 169 | +class theScript extends StateMachineScript { |
| 170 | + // onStateEnter is called when a transition starts and the state machine starts to evaluate this state |
| 171 | + onStateEnter(animator: Animator, stateInfo: any, layerIndex: number) { |
| 172 | + console.log("onStateEnter", animator, stateInfo, layerIndex); |
| 173 | + } |
| 174 | + |
| 175 | + // onStateUpdate is called on each Update frame between onStateEnter and onStateExit callbacks |
| 176 | + onStateUpdate(animator: Animator, stateInfo: any, layerIndex: number) { |
| 177 | + console.log("onStateUpdate", animator, stateInfo, layerIndex); |
| 178 | + } |
| 179 | + |
| 180 | + // onStateExit is called when a transition ends and the state machine finishes evaluating this state |
| 181 | + onStateExit(animator: Animator, stateInfo: any, layerIndex: number) { |
| 182 | + console.log("onStateExit", animator, stateInfo, layerIndex); |
| 183 | + } |
| 184 | +} |
| 185 | + |
| 186 | +animatorState.addStateMachineScript(theScript); |
| 187 | +``` |
| 188 | + |
| 189 | +If your script does not need to be reused, you can also write it like this: |
| 190 | + |
| 191 | +```typescript |
| 192 | +state.addStateMachineScript( |
| 193 | + class extends StateMachineScript { |
| 194 | + onStateEnter( |
| 195 | + animator: Animator, |
| 196 | + animatorState: AnimatorState, |
| 197 | + layerIndex: number |
| 198 | + ): void { |
| 199 | + console.log("onStateEnter: ", animatorState); |
| 200 | + } |
| 201 | + } |
| 202 | +); |
| 203 | +``` |
0 commit comments