Aug 28, 2014

33. Animation

There are two parts to creating Animation for our characters and other objects, within jMonkeyEngine. The first part involves design using Blender or similar software. This might take some time. However, the end result is a few text files and images. We will cover the second part, importing the design files into jMonkeyEngine.




The design files for a few models and animations are included in jMonkeyEngine, inside a compressed jar file. We just have to add the jar file containing the models to the project Library. This is done by right-clicking on the Project, select Properties, and then Libraries. After clicking on Add Library, we find jme3-test-data.




Now, if we expand Libraries in our project, we can find the relevant Model.




We have to have animation object variables.


    // *** 1. Start (Animation variables)
    private AnimChannel channel;
    private AnimControl control;
    // *** 1. End



We also have a ArrayList of strings to hold the list of animations. We will create a Node for the model. There will be variables for current animation, as well as text that will be outputted.


    // *** 2. Start (variables)
    ArrayList<String> animNames; // list of animations
    Node sinbad; // Model
    private boolean finished = true; // Current Animation Finished
    private int animNum; // Points to random animation
    private float playTime; // Current Animation Duration
    private String stringOut; // Text that will written
    BitmapText outText; // Bitmap Text for guiNode
    // *** 2. End



Some of the initialization code will be used to create Bitmap Text object so it can be used later to print status text. The Node is loaded with the model. Now the Animation Control can be set and we can find the list of Animations, using methods on control.


        // *** 3. Start (Initialization)
        guiFont = assetManager.loadFont(
                    "Interface/Fonts/Default.fnt"); // font
        outText = new BitmapText(guiFont, false); // test Bitmap
        outText.setSize(guiFont.getCharSet().getRenderedSize());
        sinbad = (Node)assetManager.loadModel(
                "Models/Sinbad/Sinbad.mesh.xml"); // model
        sinbad.setLocalScale(0.5f); // scale
        rootNode.attachChild(sinbad); // visual
        control = sinbad.getControl(AnimControl.class); // anim control
        animNames = new ArrayList<String>(control.getAnimationNames());
        // animNames is ArrayList String array holding all Animations.
        // *** 3. End



New animation is started, inside the simpleUpdate(), provided the last animation has finished.


        // *** 4. Start (New Animation, Write Text)
        if (finished) {
            animNum = FastMath.nextRandomInt(0, animNames.size()-1);
            channel.setAnim(animNames.get(animNum));
            playTime = channel.getAnimMaxTime();
            channel.setLoopMode(LoopMode.DontLoop);
            stringOut = "Starting (" +
                    animNum + ")\t" + animNames.get(animNum) +
                    "\tplay time = " + playTime;
            System.out.println(stringOut);
            if (playTime>tpf) finished = false;
            guiNode.detachAllChildren();
            outText.setText(stringOut);
            outText.setLocalTranslation(50, 50, 0);
            guiNode.attachChild(outText);
        }
        // *** 4. End



Once any animation is finished it will call this method, since the animation listener was set, and our class implements the animation listener. This will set our finished boolean to true, so in the next simpleUpdate(), a new animation may be started.


        // *** 5. Start (Called when current Animation finishes)
        finished = true;
        // *** 5. End



package mygame;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapText;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Node;
import com.jme3.system.AppSettings;
import java.util.ArrayList;

public class JMonkey33 extends SimpleApplication 
        implements AnimEventListener {

    public static void main(String[] args) {
        AppSettings cfg = new AppSettings(true);
        cfg.setFrameRate(60);
        JMonkey33 app = new JMonkey33();
        app.setSettings(cfg);
        app.setDisplayFps(false);
        app.setDisplayStatView(false);
        app.start();
    }
    
    // *** 1. Start (Animation variables)
    private AnimChannel channel;
    private AnimControl control;
    // *** 1. End
    
    // *** 2. Start (variables)
    ArrayList<String> animNames; // list of animations
    Node sinbad; // Model
    private boolean finished = true; // Current Animation Finished
    private int animNum; // Points to random animation
    private float playTime; // Current Animation Duration
    private String stringOut; // Text that will written
    BitmapText outText; // Bitmap Text for guiNode
    // *** 2. End

    @Override
    public void simpleInitApp() {
        viewPort.setBackgroundColor(ColorRGBA.DarkGray);
        flyCam.setEnabled(false);
        // *** 3. Start (Initialization)
        guiFont = assetManager.loadFont(
                    "Interface/Fonts/Default.fnt"); // font
        outText = new BitmapText(guiFont, false); // test Bitmap
        outText.setSize(guiFont.getCharSet().getRenderedSize());
        sinbad = (Node)assetManager.loadModel(
                "Models/Sinbad/Sinbad.mesh.xml"); // model
        sinbad.setLocalScale(0.5f); // scale
        rootNode.attachChild(sinbad); // visual
        control = sinbad.getControl(AnimControl.class); // anim control
        animNames = new ArrayList<String>(control.getAnimationNames());
        // animNames is ArrayList String array holding all Animations.
        // *** 3. End
        System.out.println("There are " + animNames.size()
                + " animations!");
        for (String anim : animNames) {
            System.out.printf("%20s\n",anim);
        }
        // ***
        /** A white, directional light source */
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);
        control.addListener(this); // Listen
        channel = control.createChannel(); // Animation control
    }

    @Override
    public void simpleUpdate(float tpf) {
        // *** 4. Start (New Animation, Write Text)
        if (finished) {
            animNum = FastMath.nextRandomInt(0, animNames.size()-1);
            channel.setAnim(animNames.get(animNum));
            playTime = channel.getAnimMaxTime();
            channel.setLoopMode(LoopMode.DontLoop);
            stringOut = "Starting (" +
                    animNum + ")\t" + animNames.get(animNum) +
                    "\tplay time = " + playTime;
            System.out.println(stringOut);
            if (playTime>tpf) finished = false;
            guiNode.detachAllChildren();
            outText.setText(stringOut);
            outText.setLocalTranslation(50, 50, 0);
            guiNode.attachChild(outText);
        }
        // *** 4. End
    }

    @Override
    public void simpleRender(RenderManager rm) {
    }

    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
        // *** 5. Start (Called when current Animation finishes)
        finished = true;
        // *** 5. End
    }

    public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
    }
}


Output:


No comments:

Post a Comment