Hi, as I am working on animations, I have created a very rudimentary blend tree implementation. I’m currently working a lot on this. If anyone is interested, I am happy to explain more in detail.
Basic idea: separation of concerns, I don’t want animation code in a character controller, so I added a control that pulls information from a character instead of pushing it from the character control code.
A first implementation would look like:
public class SinbadAnimControl extends AbstractControl {
AnimBlendTree blendTree = new AnimBlendTree();
@Override
public void setSpatial(Spatial spatial) {
super.setSpatial(spatial);
spatial.addControl(blendTree);
AnimComposer animComposer = this.getSpatial().getControl(AnimComposer.class);
ActionState idleState = blendTree.addState("idle", new ClipAction(animComposer.getAnimClip("IdleTop")));
EventAction jumpStart = new EventAction(new ClipAction(animComposer.getAnimClip("JumpStart")));
jumpStart.setSpeed(0.8f);
ActionState startJumpingState = blendTree.addState("start jumping", jumpStart);
ActionState inAirState = blendTree.addState("in air", new ClipAction(animComposer.getAnimClip("JumpLoop")));
ClipAction runAction = new ClipAction(animComposer.getAnimClip("RunBase"));
ActionState runningState = blendTree.addState("running", runAction);
Link idleToStartJumping = blendTree.addLink(idleState, startJumpingState);
Link startJumpingToInAir = blendTree.addLink(startJumpingState, inAirState);
Link inAirToIdle = blendTree.addLink(inAirState, idleState);
Link idleToRunning = blendTree.addLink(idleState, runningState);
Link runningToIdle = blendTree.addLink(runningState, idleState);
Link runningToStartJumping = blendTree.addLink(runningState, startJumpingState);
blendTree.setDefaultState(idleState);
BetterCharacterControl characterControl = this.getSpatial().getParent().getControl(BetterCharacterControl.class);
BooleanSupplier didJump = () -> !characterControl.isOnGround() && characterControl.getVelocity().y > 0;
idleToStartJumping.setCondition(didJump);
runningToStartJumping.setCondition(didJump);
startJumpingToInAir.setCondition(() -> true);
inAirToIdle.setCondition(characterControl::isOnGround);
Supplier<Float> getForwardVelocity = () -> characterControl.getSpatial()
.getLocalRotation()
.inverse()
.mult(characterControl.getVelocity())
.z;
idleToRunning.setCondition(() -> {
if (!characterControl.isOnGround()) return false;
return getForwardVelocity.get() >= 0.01f;
});
runningToIdle.setCondition(() -> {
if (!characterControl.isOnGround()) return false;
return getForwardVelocity.get() < 0.01f;
});
}
@Override
protected void controlUpdate(float tpf) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
}
This way I’m able to control which animations are played when and at what speed and other things of the animations based on parameters from the character controller. The main advantage is that this information is pulled when the AnimBlendTree
control find it necessary to pull this information.
The result of the above code: AnimBlendTree example