2d Framework tutorial 1

Though I would put together a small example/tutorial for using some of the new 2d framework components. It will show how to build the typical 3-star level summary you see on lots of Android/iOS game.

What this covers:

  • The AppState will use a single AnimElement for the 3 star outlines and the 3 extruded stars and a single Emitter for all effects.
  • Will show how to define the TextureRegion and QuadData for each of the pieces added to the AnimElement
  • Will show how to setup reusable TemporalActions to animate each of the Quads as if the were their own Mesh

What this does not cover:

  • How to set up parent linkage for mimicking skeletal animation.
  • How to leverage setOrigin

–MORE TO COME–

First, here is the AppState and a few images needed to run the example. The next couple posts will break the AppState down into sections with (hopefully) understandable explanations.

NOTE: This AppState relies on the latest updates to the 2d framework which will be published this evening. Please keep this in mind :wink:

LevelSummary.java
[java]
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.texture.Texture;
import tonegod.gui.controls.extras.emitter.AlphaInfluencer;
import tonegod.gui.controls.extras.emitter.ColorInfluencer;
import tonegod.gui.controls.extras.emitter.DirectionInfluencer;
import tonegod.gui.controls.extras.emitter.ElementEmitter;
import tonegod.gui.controls.extras.emitter.GravityInfluencer;
import tonegod.gui.controls.extras.emitter.ImpulseInfluencer;
import tonegod.gui.controls.extras.emitter.RotationInfluencer;
import tonegod.gui.controls.extras.emitter.SizeInfluencer;
import tonegod.gui.controls.extras.emitter.SpriteInfluencer;
import tonegod.gui.controls.extras.emitter.animation.EmitterEmitAction;
import tonegod.gui.controls.extras.emitter.animation.EmitterEnableAction;
import tonegod.gui.controls.extras.emitter.animation.EmitterInfluencerAction;
import tonegod.gui.core.Screen;
import tonegod.gui.framework.animation.Interpolation;
import tonegod.gui.framework.animation.MoveToAction;
import tonegod.gui.framework.animation.RotateByAction;
import tonegod.gui.framework.animation.ScaleByAction;
import tonegod.gui.framework.core.AnimElement;

/**
*

  • @author t0neg0d
    */
    public class LevelSummary extends AbstractAppState {
    Screen screen;
    Texture texStars;
    AnimElement stars;
    ElementEmitter emitter;
    private int showStars = 3;

    // Emitter influencers per star
    DirectionInfluencer di1, di2, di3;

    // Emitter actions
    EmitterEnableAction enable, disable;
    EmitterInfluencerAction dir1, dir2, dir3;
    MoveToAction move1, move2, move3;
    EmitterEmitAction emit1, emit2, emit3;

    // Star actions
    ScaleByAction scaleOL1, scaleOL2, scaleOL3, scaleEX1, scaleEX2, scaleEX3;
    RotateByAction rotOL1, rotOL2, rotOL3;

    public LevelSummary(Screen screen) {
    this.screen = screen;
    createStarTexture();
    createEmitter();
    setupEmitterActions();
    setupStarActions();
    initScreen();
    }

    private void createStarTexture() {
    texStars = screen.getApplication().getAssetManager().loadTexture(“mygame/textures/gui/Stars.png”);
    texStars.setMagFilter(Texture.MagFilter.Bilinear);
    texStars.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
    texStars.setWrap(Texture.WrapMode.BorderClamp);
    }

    private void createEmitter() {
    emitter = new ElementEmitter(screen, new Vector2f(screen.getWidth()/2-64-153,screen.getHeight()/2-64), 80, 80);
    emitter.setEmitterShape(“mygame/textures/gui/EmitterShape2.png”);
    emitter.setSprite(“mygame/textures/gui/Sprite.png”, 2, 2, 1);
    emitter.setMaxParticles(210);
    emitter.setLowHighLife(1.15f,1.85f);
    emitter.setMinMaxForce(3f,12f);
    emitter.setParticlesPerSecond(0);

     setupInfluencers();
    

    }

    private void setupInfluencers() {
    // Configure default influencers
    emitter.removeInfluencer(SpriteInfluencer.class);
    emitter.removeInfluencer(ImpulseInfluencer.class);
    emitter.getInfluencer(GravityInfluencer.class).setGravity(new Vector2f(0,3));
    emitter.getInfluencer(SizeInfluencer.class).setStartSize(0.25f);
    emitter.getInfluencer(SizeInfluencer.class).setEndSize(.25f);
    emitter.getInfluencer(RotationInfluencer.class).setMaxRotationSpeed(60);
    emitter.getInfluencer(ColorInfluencer.class).setStartColor(new ColorRGBA(1,0,0,1));
    emitter.getInfluencer(ColorInfluencer.class).setEndColor(new ColorRGBA(1,1,0,1));
    emitter.getInfluencer(ColorInfluencer.class).setInterpolation(Interpolation.exp5Out);
    emitter.getInfluencer(AlphaInfluencer.class).setStartAlpha(3);
    emitter.getInfluencer(AlphaInfluencer.class).setEndAlpha(.25f);
    emitter.getInfluencer(AlphaInfluencer.class).setInterpolation(Interpolation.exp5In);

     // Create directional influencers for each star
     di1 = new DirectionInfluencer();
     di1.setDirection(new Vector2f(-1f,0f));
     di1.setStrength(.65f);
     
     di2 = new DirectionInfluencer();
     di2.setDirection(new Vector2f(0f,0f));
     di2.setStrength(0f);
     
     di3 = new DirectionInfluencer();
     di3.setDirection(new Vector2f(1f,0f));
     di3.setStrength(.65f);
    

    }

    private void setupEmitterActions() {
    enable = new EmitterEnableAction();
    enable.setDuration(1);
    enable.setEnableEmitter();

     disable = new EmitterEnableAction();
     disable.setDuration(1);
     disable.setDisableEmitter();
     
     move1 = new MoveToAction();
     move1.setPosition(screen.getWidth()/2-62-140,screen.getHeight()/2-62);
     move2 = new MoveToAction();
     move2.setPosition(screen.getWidth()/2-62,screen.getHeight()/2-62);
     move3 = new MoveToAction();
     move3.setPosition(screen.getWidth()/2-62+140,screen.getHeight()/2-62);
     
     dir1 = new EmitterInfluencerAction();
     dir1.setInfluencer(di1);
     dir2 = new EmitterInfluencerAction();
     dir2.setInfluencer(di2);
     dir3 = new EmitterInfluencerAction();
     dir3.setInfluencer(di3);
     
     emit1 = new EmitterEmitAction();
     emit1.setEmitNumParticles(70);
     emit2 = new EmitterEmitAction();
     emit2.setEmitNumParticles(70);
     emit3 = new EmitterEmitAction();
     emit3.setEmitNumParticles(70);
    

    }

    private void setupStarActions() {
    // Star outline center
    scaleOL1 = new ScaleByAction();
    scaleOL1.setAmount(-4f);
    scaleOL1.setDuration(1);
    scaleOL1.setInterpolation(Interpolation.exp5Out);

     rotOL1 = new RotateByAction();
     rotOL1.setAmount(360);
     rotOL1.setDuration(1);
     rotOL1.setInterpolation(Interpolation.exp5Out);
     
     // Star outline left
     scaleOL2 = new ScaleByAction();
     scaleOL2.setAmount(-4f);
     scaleOL2.setDuration(1);
     scaleOL2.setInterpolation(Interpolation.exp5Out);
    
     rotOL2 = new RotateByAction();
     rotOL2.setAmount(360);
     rotOL2.setDuration(1);
     rotOL2.setInterpolation(Interpolation.exp5Out);
     
     // Star outline right
     scaleOL3 = new ScaleByAction();
     scaleOL3.setAmount(-4f);
     scaleOL3.setDuration(1);
     scaleOL3.setInterpolation(Interpolation.exp5Out);
    
     rotOL3 = new RotateByAction();
     rotOL3.setAmount(360);
     rotOL3.setDuration(1);
     rotOL3.setInterpolation(Interpolation.exp5Out);
     
     // Star extrude left
     scaleEX1 = new ScaleByAction();
     scaleEX1.setAmount(.9f);
     scaleEX1.setDuration(.5f);
     scaleEX1.setInterpolation(Interpolation.bounceOut);
     
     // Star extrude center
     scaleEX2 = new ScaleByAction();
     scaleEX2.setAmount(.9f);
     scaleEX2.setDuration(.5f);
     scaleEX2.setInterpolation(Interpolation.bounceOut);
     
     // Star extrude right
     scaleEX3 = new ScaleByAction();
     scaleEX3.setAmount(.9f);
     scaleEX3.setDuration(.5f);
     scaleEX3.setInterpolation(Interpolation.bounceOut);
    

    }

    private void initScreen() {
    // Create anim element
    stars = new AnimElement(screen.getApplication().getAssetManager()) {
    @Override
    public void animElementUpdate(float tpf) { }
    };
    // Set texture
    stars.setTexture(texStars);

     String trKey = "outline";
     // Add texture region for star outline
     stars.addTextureRegion(trKey,130,234,124,124);
     stars.getTextureRegion(trKey).flip(false, true);
     
     // Add center outline quad
     // Define star outline center
     String key = "outline1";
     stars.addQuad(key, trKey, new Vector2f(screen.getWidth()/2-62,screen.getHeight()/2-62), new Vector2f(62,62));
     // Define star outline left
     key = "outline2";
     stars.addQuad(key, trKey, new Vector2f(screen.getWidth()/2-62-140,screen.getHeight()/2-62), new Vector2f(62,62));
     // Define star outline right
     key = "outline3";
     stars.addQuad(key, trKey, new Vector2f(screen.getWidth()/2-62+140,screen.getHeight()/2-62), new Vector2f(62,62));
     
     // Define extruded star left
     key = "extrude1";
     stars.addTextureRegion(key,2,361,149,149);
     stars.getTextureRegion(key).flip(false, true);
     stars.addQuad(key, key, new Vector2f(screen.getWidth()/2-75-153,screen.getHeight()/2-60), new Vector2f(105,25));
     // Define extruded star center
     key = "extrude2";
     stars.addTextureRegion(key,153,361,149,149);
     stars.getTextureRegion(key).flip(false, true);
     stars.addQuad(key, key, new Vector2f(screen.getWidth()/2-75,screen.getHeight()/2-60), new Vector2f(75,25));
     // Define extruded star right
     key = "extrude3";
     stars.addTextureRegion(key,304,361,149,149);
     stars.getTextureRegion(key).flip(false, true);
     stars.addQuad(key, key, new Vector2f(screen.getWidth()/2-75+153,screen.getHeight()/2-60), new Vector2f(44,25));
     
     stars.setScale(new Vector2f(1f,1f));
     // Initialize anim element
     stars.initialize();
     
     // Hide quads
     stars.getQuads().get("outline1").hide();
     stars.getQuads().get("outline2").hide();
     stars.getQuads().get("outline3").hide();
     stars.getQuads().get("extrude1").hide();
     stars.getQuads().get("extrude2").hide();
     stars.getQuads().get("extrude3").hide();
    

    }

    public void setStarCount(int stars) {
    this.showStars = stars;
    }

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    emitter.startEmitter();
    emitter.setIsActive(false);
    loadScreen();
    }

    private void resetQuads() {
    // Reset quads
    stars.getQuads().get(“outline1”).scaleX = 5;
    stars.getQuads().get(“outline1”).scaleY = 5;
    stars.getQuads().get(“outline1”).rotation = 0;
    stars.getQuads().get(“outline1”).hide();

     stars.getQuads().get("outline2").scaleX = 5;
     stars.getQuads().get("outline2").scaleY = 5;
     stars.getQuads().get("outline2").rotation = 0;
     stars.getQuads().get("outline2").hide();
     
     stars.getQuads().get("outline3").scaleX = 5;
     stars.getQuads().get("outline3").scaleY = 5;
     stars.getQuads().get("outline3").rotation = 0;
     stars.getQuads().get("outline3").hide();
     
     stars.getQuads().get("extrude1").scaleX = .1f;
     stars.getQuads().get("extrude1").scaleY = .1f;
     stars.getQuads().get("extrude1").hide();
     
     stars.getQuads().get("extrude2").scaleX = .1f;
     stars.getQuads().get("extrude2").scaleY = .1f;
     stars.getQuads().get("extrude2").hide();
     
     stars.getQuads().get("extrude3").scaleX = .1f;
     stars.getQuads().get("extrude3").scaleY = .1f;
     stars.getQuads().get("extrude3").hide();
    

    }

    private void resetActions() {
    enable.restart();
    disable.restart();
    move1.restart();
    move2.restart();
    move3.restart();
    dir1.restart();
    dir2.restart();
    dir3.restart();
    emit1.restart();
    emit2.restart();
    emit3.restart();

     scaleEX1.restart();
     scaleEX2.restart();
     scaleEX3.restart();
     
     scaleOL1.restart();
     rotOL1.restart();
     scaleOL2.restart();
     rotOL2.restart();
     scaleOL3.restart();
     rotOL3.restart();
    

    }

    private void loadScreen() {
    resetQuads();
    resetActions();

     // Attach anim element
     screen.getGUINode().attachChild(stars);
     
     screen.getAnimManager().addQueuedAction(enable, emitter, 1f);
     
     if (showStars >= 1) {
     	// Add extruded star left actions
     	screen.getAnimManager().addQueuedAction(scaleEX1, stars.getQuads().get("extrude1"), 2f);
     	// Emitter reposition, update influence and set emit command for left star
     	screen.getAnimManager().addQueuedAction(move1, emitter, 1.1f);
     	screen.getAnimManager().addQueuedAction(dir1, emitter, 1.2f);
     	screen.getAnimManager().addQueuedAction(emit1, emitter, 1.3f);
     }
     if (showStars >= 2) {
     	// Add extruded star center actions
     	screen.getAnimManager().addQueuedAction(scaleEX2, stars.getQuads().get("extrude2"), 2.1f);
     	// Emitter reposition, update influence and set emit command for center star
     	screen.getAnimManager().addQueuedAction(move2, emitter, 1.5f);
     	screen.getAnimManager().addQueuedAction(dir2, emitter, 1.55f);
     	screen.getAnimManager().addQueuedAction(emit2, emitter, 1.6f);
     }
     if (showStars == 3) {
     	// Add extruded star right actions
     	screen.getAnimManager().addQueuedAction(scaleEX3, stars.getQuads().get("extrude3"), 2.2f);
     	// Emitter reposition, update influence and set emit command for right star
     	screen.getAnimManager().addQueuedAction(move3, emitter, 1.8f);
     	screen.getAnimManager().addQueuedAction(dir3, emitter, 1.85f);
     	screen.getAnimManager().addQueuedAction(emit3, emitter, 1.9f);
     }
     
     // Add disable emitter action
     screen.getAnimManager().addQueuedAction(disable, emitter, 4f);
     
     // Add scale and rotate actions for center star outline
     screen.getAnimManager().addQueuedAction(scaleOL1, stars.getQuads().get("outline1"), .3f);
     screen.getAnimManager().addQueuedAction(rotOL1, stars.getQuads().get("outline1"), .3f);
     
     // Add scale and rotate actions for left star outline
     screen.getAnimManager().addQueuedAction(scaleOL2, stars.getQuads().get("outline2"), .6f);
     screen.getAnimManager().addQueuedAction(rotOL2, stars.getQuads().get("outline2"), .6f);
     
     // Add scale and rotate actions for left star outline
     screen.getAnimManager().addQueuedAction(scaleOL3, stars.getQuads().get("outline3"), .9f);
     screen.getAnimManager().addQueuedAction(rotOL3, stars.getQuads().get("outline3"), .9f);
    

    }

    @Override
    public void update(float tpf) {
    stars.update(tpf);
    }

    @Override
    public void cleanup() {
    super.cleanup();
    emitter.setIsActive(false);
    emitter.stopEmitter();
    screen.getGUINode().detachChild(stars);
    }
    }

[/java]

Rename this to EmitterShape2.png

Rename this to Sprite.png

Rename this to Stars.png

The texture paths will need to be updated in your local copy… but I’m sure you are aware of how to do this.

1 Like

Placeholder for starting to break down the app state above…

EDIT: Update pushed to both the library repo and the plugin repo. Hopefully that is all working again.

Seeew… where to begin. I guess a bit about how the AnimElement class works would be a good place to start. The AnimElement is one or more sets of 4 vertices (two faces) making up textured quads that can be animated using TemporalActions. To define one of these quads, you’ll first need to supply a texture atlas and define TextureRegions, like so:

[java]
// Create anim element
stars = new AnimElement(screen.getApplication().getAssetManager()) {
@Override
public void animElementUpdate(float tpf) { }
};
// Set texture
stars.setTexture(texStars);

// Create a new texture region key
String trKey = “outline”;
// Add texture region for star outline
stars.addTextureRegion(trKey,130,234,124,124);
// Use the flip method to invert the image along the y axis for this region
stars.getTextureRegion(trKey).flip(false, true);
[/java]

Now this texture region can be used for one or more quads within the AnimElement: The quads initial width and height are defined by the texture region’s width and height. This can later be adjusted using scaling. The parameters for the addQuad method are listed above the first addQuad call below.

[java]
// Add center outline quad
// Define star outline center
String key = “outline1”;
/**

  • quadKey : The hash map key for the new quad
  • regionKey : The hash map key for the texture region to apply to this quad
  • position : The initial position of the quad
  • origin : The origin around which all transforms should be applied
    */
    stars.addQuad(key, trKey, new Vector2f(screen.getWidth()/2-62,screen.getHeight()/2-62), new Vector2f(62,62));

// Define star outline left
key = “outline2”;
stars.addQuad(key, trKey, new Vector2f(screen.getWidth()/2-62-140,screen.getHeight()/2-62), new Vector2f(62,62));

// Define star outline right
key = “outline3”;
stars.addQuad(key, trKey, new Vector2f(screen.getWidth()/2-62+140,screen.getHeight()/2-62), new Vector2f(62,62));
[/java]

Once you have established all quads for the animation element you can finalize it by calling:

[java]
stars.initialize();
[/java]

  • AnimElements implements the interface Transformable, as do each of the QuadData’s contained by the AnimElement.
  • Transforms can be applied to the individual quads as well as the AnimElement as a whole.
  • AnimElements can contain other AnimElement, though, this semi-defeats the purpose as an AnimElement is a single mesh that can be manipulated as individual meshes.

To add an AnimElement to the Screen or a contained Element, you should use the standard JME method:

[java]
screen.attachChild(stars);
// or
someElement.attachChild(stars);
[/java]

It is also worth mentioning that Elements can be used as static layers, by defining them like so:

[java]
Element bg = new Element(screen, Vector2f.ZERO, new Vector2f(screen.getWidth(), screen.getHeight()));
bg.setEffectZOrder(false);
bg.setIsMovable(false);
bg.setIsResizable(false);
bg.setIgnoreMouse(true);
screen.addElement(bg);
[/java]

NOTE: Each quad within an AnimElement is rendered in the order it is added. This is something to keep in mind when creating characters you wish to animate.

–MORE TO FOLLOW LATER–

On to TemporalActions:

There are a number of TemporalActions predefined in the library and I’ll be adding a few more before summing this up. These are:

Over-time actions:

  • MoveByAction - moves the transformable from it’s current position to a specified location using the defined interpolation over a specified duration.
  • RotateByAction - rotates the transformable from it’s current angle to the specified angle using the defined interpolation over a specified duration.
  • ScaleByAction - scales the transformable from it’s current x/y scale to the specified x/y scale individually using the defined interpolation over a specified duration.
  • SplineAction - Allows you to specify X number of points and creates a smooth arced path which is traversed using the defined interpolation over a specified duration.

Instant actions:

  • MoveToAction - instantly moves the transformable from it’s current location to the specified location.
  • RotateToAction - instantly rotates the transformable from it’s current angle to the specified angle. (not added yet)
  • ScaleToAction - instantly scales the transformable from it’s current x/y scale to the specified x/y scale. (not added yet)

NOTE: The ElementEmitter is also a Transformable and has specific instant actions that can be applied:

  • EmitterEnableAction - Allows for enabling/disabling the emitter
  • EmitterInfluencerAction - Allows you to replace a given influencer with another.
  • EmitterEmitAction - Allows you to either emit a specified number of particles or all particles.

You can also apply the standard TemporalActions to an ElementEitter, such as MoveByAction, RotateByAction, etc, etc.

To create a new instance of a TemporalAction is rather simple:

[java]
RotateByAction rot = new RotateByAction();
rot.setAmount(360);
rot.setDuration(1);
rot.setInterpolation(Interpolation.exp5Out);
[/java]

To apply the TemporalAction to a Transformable, you simply:

[java]
stars.getQuads().get(“outline1”).addAction(rot);

// or apply directly to the AnimElement
stars.addAction(rot);
[/java]

You can reset a TemporalAction for reuse by calling:

[java]
// Reset the action
rot.restart();

// Add the action to the transformables queue again
stars.addAction(rot);
[/java]

All over-time actions can be auto-reversed by calling the following… This will half the duration, applying the action as defined over the first half of the duration and then applying the reverse of the action over the second half of the duration, returning the transformable to it’s original state.

[java]
rot.setAutoReverse(true);
[/java]

You can also queue these actions via the AnimManager which can be accessed via the Screen. To queue a TemporalAction, you supply a TemporalAction, Transformable and a time offset like so:

[java]
screen.getAnimManager().addQueuedAction(rot, stars.getQuads().get(“outline1”), 12.5f);
[/java]

This would apply the RotateByAction defined above to the QuadData “outline1” of the stars AnimElement, 12.5 seconds from the time the queued action was added.