Since I’m deep into converting my game to the new system and its fresh in my head, I decided to update the HelloAnimation tutorial with a new version using AnimComposer and such.
I am unsure if I can be the one (or should be) to add additional text explaining everything thoroughly, but hopefully this will serve as a good starting point for replacing all of the old documentation eventually, and in the mean-time this can at least serve as a test-case / example that we can direct new users towards when they are trying to look for info on the new system
However there are 3 small things that I am not sure how to do in the new system that need fixed in this test case still:
-
How to compare the name of an Action thats being run on a specific layer? For example, the
animCoposer.getCurrentAction(layerName)
method returns an Action object, but there is no way to get the name for comparing to an animation’s name, which the hello animation tutorial usually attempts to do in the onAction method before setting the animation to “walk”. Any ideas on the proper way to compare a layer’s current Action to its String based name? -
I can’t seem to find a direct replacement for AnimEventListener with the new system. Does one exist, or is it not necessary with the new anim system? (I personally don’t use listeners to check when anims are done so I’m unsure about this)
-
Also not entirely sure how to handle loop mode with the new system. I personally use timers for animations that aren’t supposed to loop so that another looping walk/stand anim resume when the timer is up, but IDK what the optimal way to do non-looping is with the new system.
import com.jme3.anim.AnimComposer;
import com.jme3.anim.Armature;
import com.jme3.anim.ArmatureMask;
import com.jme3.anim.SkinningControl;
import com.jme3.anim.util.AnimMigrationUtils;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
/** Sample 7 - how to load an OgreXML model and play an animation,
* using armatureMasks, an animComposer, a skinningControl, an armature, and an (insert new version of AnimEventListener here if exists) */
public class HelloAnimation extends SimpleApplication implements AnimEventListener {
private ArmatureMask armatureMask;
private AnimComposer animComposer;
private SkinningControl skinningControl;
private Armature armature;
Node player;
public static void main(String[] args) {
HelloAnimation app = new HelloAnimation();
app.start();
}
@Override
public void simpleInitApp() {
viewPort.setBackgroundColor(ColorRGBA.LightGray);
initKeys();
DirectionalLight dl = new DirectionalLight();
dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
rootNode.addLight(dl);
player = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
player = (Node) AnimMigrationUtils.migrate(player); //convert the model to the new animation system, if necessary. This is only necessary if your model has an AnimControl and SkeletonControl instead of an AnimComposer and SkinningControl
player.setLocalScale(0.5f);
rootNode.attachChild(player);
animComposer = player.getControl(AnimComposer.class);
skinningControl = player.getControl(SkinningControl.class);
armature = skinningControl.getArmature();
// composer.addListener(this); //does an equivelent exist in new system? (question 2)
armatureMask = new ArmatureMask(armature); //passing armature into constructor will add all of the Armature's joints to the mask
// armatureMask.addBones(armature, jointNames); //alternate way to add specific joints
// armatureMask.addFromJoint(armature, jointName); //alternate way to add all joints descending from a designated parent joint
animComposer.makeLayer("rootLayerMask", armatureMask);
animComposer.setCurrentAction("stand", "rootLayerMask");
}
public void onAnimCycleDone(AnimComposer composer, ArmatureMask mask, String animName) {
if (animName.equals("Walk")) {
animComposer.setCurrentAction("stand", "rootLayerMask");
// channel.setLoopMode(LoopMode.DontLoop); // how do non-looping now? ( question 3)
animComposer.getCurrentAction("rootLayerMask").setSpeed(1.0f);
}
}
public void onAnimChange(AnimComposer composer, ArmatureMask mask, String animName) {
// unused
}
/** Custom Keybinding: Map named actions to inputs. */
private void initKeys() {
inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(actionListener, "Walk");
}
private ActionListener actionListener = new ActionListener() {
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals("Walk") && !keyPressed) {
if (!animComposer.getCurrentAction("rootLayerMask").equals("Walk")) { // how to properly compare a layer's current action to know if its already playing? (question 1)
animComposer.setCurrentAction("stand", "rootLayerMask");
animComposer.getCurrentAction("rootLayerMask").setSpeed(0.5f);
// channel.setLoopMode(LoopMode.Loop);
}
}
}
};
}