-
Notifications
You must be signed in to change notification settings - Fork 5
Animation Parameters
Animation Parameters are variables that are defined within an Animator Controller that can be accessed and assigned values from scripts. This is how a script can control or affect the flow of the state machine. For example, a script can set a parameter to control a Blend Tree.
They can be of four basic types:
- Integer - a whole number
- Float - a number with a fractional part
- Bool - true or false value
- Trigger - a boolean parameter that is reset by the controller when consumed by a transition
Parameters can be assigned values from a script using functions in the AnimatorController
class: setFloat
, setInt
, setBool
, setTrigger
and resetTrigger
.
Variables of type Float can be configured with the following conditions and require a threshold value:
AnimatorConditionMode.Greater
AnimatorConditionMode.Less
Variables of type Int can be configured with the following conditions and require a threshold value:
AnimatorConditionMode.Greater
AnimatorConditionMode.Less
AnimatorConditionMode.Equals
AnimatorConditionMode.NotEquals
// Create the controller and add it to the player node.
AnimatorController animator = new AnimatorController(animComposer);
player.addControl(animator);
// Create the parameters by which you want to control the transitions between states.
animator.addParameter("moveSpeed", AnimatorControllerParameterType.Float);
// Define states for animations.
AnimatorStateMachine sm = animator.getLayer(AnimComposer.DEFAULT_LAYER).getStateMachine();
AnimatorState idle = sm.addState("IdleState", "idle");
AnimatorState walk = sm.addState("WalkState", "walking_inPlace");
// Define the transitions and conditions for each state using the previously created parameters.
AnimatorStateTransition idleToWalk = idle.addTransition(walk);
// changes state when the 'moveSpeed' parameter is greater than 1
idleToWalk.addCondition(AnimatorConditionMode.Greater, 1f, "moveSpeed");
AnimatorStateTransition walkToIdle = walk.addTransition(idle);
// changes state when the 'moveSpeed' parameter is less than 0.8
walkToIdle.addCondition(AnimatorConditionMode.Less, 0.8f, "moveSpeed");
// Don't forget to set the initial state.
sm.setDefaultState(idle);
Now you can control the state machine through the parameters you defined earlier..
public class PlayerMovementControl extends AbstractControl {
private Camera camera;
private AnimatorController animator;
private BetterCharacterControl bcc;
private final Quaternion dr = new Quaternion();
private final Vector3f cameraDir = new Vector3f();
private final Vector3f cameraLeft = new Vector3f();
private final Vector3f walkDirection = new Vector3f();
private boolean _MoveForward, _MoveBackward, _TurnLeft, _TurnRight;
...
@Override
public void controlUpdate(float tpf) {
camera.getDirection(cameraDir).setY(0);
camera.getLeft(cameraLeft).setY(0);
walkDirection.set(0, 0, 0);
if (_MoveForward) {
walkDirection.addLocal(cameraDir);
} else if (_MoveBackward) {
walkDirection.subtractLocal(cameraDir);
}
if (_TurnLeft) {
walkDirection.addLocal(cameraLeft);
} else if (_TurnRight) {
walkDirection.subtractLocal(cameraLeft);
}
walkDirection.normalizeLocal();
boolean isMoving = walkDirection.lengthSquared() > 0;
if (isMoving) {
// smooth rotation
float angle = FastMath.atan2(walkDirection.x, walkDirection.z);
dr.fromAngleNormalAxis(angle, Vector3f.UNIT_Y);
spatial.getWorldRotation().slerp(dr, m_TurnSpeed * tpf);
bcc.setViewDirection(spatial.getWorldRotation().mult(Vector3f.UNIT_Z));
}
bcc.setWalkDirection(walkDirection.multLocal(m_MoveSpeed));
animator.setFloat("moveSpeed", bcc.getVelocity().length());
}
}
Variables of type Boolean can be configured with the following conditions and do not need a threshold value that can be set to 0 as it will be ignored:
AnimatorConditionMode.If
AnimatorConditionMode.IfNot
AnimatorController animator = new AnimatorController(animComposer);
player.addControl(animator)
animator.addParameter("isRunning", AnimatorControllerParameterType.Bool);
// Define states for animations.
AnimatorStateMachine sm = animator.getLayer(AnimComposer.DEFAULT_LAYER).getStateMachine();
AnimatorState idle = sm.addState("IdleState", "idle");
AnimatorState run = sm.addState("RunnigState", "running_inPlace");
AnimatorStateTransition idleToRun = idle.addTransition(run);
// change state when 'isRunning' parameter is equals to true
// the threshold can be set to zero because it will be ignored
idleToRun.addCondition(AnimatorConditionMode.If, 0, "isRunning");
AnimatorStateTransition runToIdle = run.addTransition(idle);
// change state when 'isRunning' parameter is equals to false
// the threshold can be set to zero because it will be ignored
runToIdle.addCondition(AnimatorConditionMode.IfNot, 0, "isRunning");
// set the initial state.
sm.setDefaultState(idle);
// Then activate the transition in the PlayerControl
animator.setBool("isRunning", true);
You can now access the AnimatorController variables to perform other actions such as turning on / off the sound of footsteps
public class FootstepsControl extends AbstractControl {
private AnimatorController animator;
private AudioNode footstepsSFX;
...
@Override
public void controlUpdate(float tpf) {
if (animator.getBool("isRunning")) {
footstepsSFX.play();
} else {
footstepsSFX.stop();
}
}
}
Trigger variables only work with AnimatorConditionMode.If
and do not need a threshold value that can be set to 0 as it will be ignored.
If you have to wait for an animation to finish before transitioning to another state, specify in the transition the percentage of completion between 0 and 1 that the animation must perform before changing state.
AnimatorController animator = new AnimatorController(animComposer);
player.addControl(animator);
animator.addParameter("isJumping", AnimatorControllerParameterType.Trigger);
// Define states for animations.
AnimatorStateMachine sm = animator.getLayer(AnimComposer.DEFAULT_LAYER).getStateMachine();
AnimatorState idle = sm.addState("IdleState", "idle");
AnimatorState jump = sm.addState("JumpState", "jump_inPlace");
AnimatorStateTransition idleToJump = idle.addTransition(jump);
// changes state when the 'isJumping' parameter is activated by the trigger
// the threshold can be set to zero because it will be ignored
idleToJump.addCondition(AnimatorConditionMode.If, 0, "isJumping");
// execute 90% of the jump animation before returning to idle state
AnimatorStateTransition jumpToIdle = jump.addTransition(idle, 0.90f);
// set the initial state.
sm.setDefaultState(idle);
Then activate the trigger in the PlayerControl
public class PlayerControl extends AbstractControl implements ActionListener {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (name.equals(InputMapping.JUMP) && isPressed) {
animator.setTrigger("isJumping");
}
}
...
}