Multiple Animation

Hello,

I tried the helloAnimations Tutorial, and that went fine.



Now, i try to create some “Monsters” and use the Animations on them as well.

So, i have this Monster class:



[java]

package mygame;

import com.jme3.scene.Spatial;

import com.jme3.math.Vector3f;

import com.jme3.bullet.control.CharacterControl;

import com.jme3.animation.AnimChannel;

import com.jme3.animation.AnimControl;

import com.jme3.animation.AnimEventListener;

import com.jme3.animation.LoopMode;

import com.jme3.scene.Node;



/**

*

  • @author skee

    */

    public class Monster implements AnimEventListener {



    public int Id;

    public String Name;

    public CharacterControl Grid;

    public boolean Aggro=false;

    public AnimChannel Ani;

    public Node Mesh;



    public Main Core;



    public void run() {

    AnimControl control= Mesh.getControl(AnimControl.class);

    control.addListener(this);

    AnimChannel channel1 = control.createChannel();

    channel1.setAnim("stand");

    channel1.setLoopMode(LoopMode.DontLoop);

    this.Ani=channel1;



    }









    private void LogOut(String text) {

    System.out.println(""+(System.currentTimeMillis() / 1000)+" "+this.Id+" [" + this + "]" + text);

    }



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



    LogOut("channels_done: "+this+" - "+control+" - "+channel);



    }

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

    // unused

    }

    }

    [/java]



    Now i have the following problem:



    If i create 20 monsters and i start the animation of one of them, the onAnimCycleDone is triggered 20 times.

    This is the output:

    1307821168 11471 [mygame.Monster@a010ba]channels_done: mygame.Monster@a010ba - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11475 [mygame.Monster@ae1cf]channels_done: mygame.Monster@ae1cf - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11476 [mygame.Monster@13c53a8]channels_done: mygame.Monster@13c53a8 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11477 [mygame.Monster@109da93]channels_done: mygame.Monster@109da93 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11478 [mygame.Monster@10df4e2]channels_done: mygame.Monster@10df4e2 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11479 [mygame.Monster@145e5a6]channels_done: mygame.Monster@145e5a6 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11483 [mygame.Monster@dc3b82]channels_done: mygame.Monster@dc3b82 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11489 [mygame.Monster@1c7f37d]channels_done: mygame.Monster@1c7f37d - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11495 [mygame.Monster@fc8e75]channels_done: mygame.Monster@fc8e75 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11496 [mygame.Monster@c26ede]channels_done: mygame.Monster@c26ede - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11497 [mygame.Monster@59cbda]channels_done: mygame.Monster@59cbda - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11499 [mygame.Monster@a0e990]channels_done: mygame.Monster@a0e990 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11500 [mygame.Monster@72d873]channels_done: mygame.Monster@72d873 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11501 [mygame.Monster@705d28]channels_done: mygame.Monster@705d28 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11502 [mygame.Monster@ec115b]channels_done: mygame.Monster@ec115b - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11503 [mygame.Monster@1fbc355]channels_done: mygame.Monster@1fbc355 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11504 [mygame.Monster@1ef3d1b]channels_done: mygame.Monster@1ef3d1b - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11505 [mygame.Monster@932fe]channels_done: mygame.Monster@932fe - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11506 [mygame.Monster@a33ce2]channels_done: mygame.Monster@a33ce2 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11507 [mygame.Monster@166c114]channels_done: mygame.Monster@166c114 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db

    1307821168 11513 [mygame.Monster@509df8]channels_done: mygame.Monster@509df8 - com.jme3.animation.AnimControl@1984a9d - com.jme3.animation.AnimChannel@14aa2db



    Any ideas, what i am doing wrong?



    Thanks for help



    Skee

It looks you are starting the all them animation. Show the class where you’re instancing the monsters.

Hello, here is the whole MainClass, where the Monsters are created.

The Method/Function is called initMob



[java]

package mygame;



import com.jme3.app.SimpleApplication;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.renderer.RenderManager;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Box;

import com.jme3.texture.Texture;

import com.jme3.texture.Texture.WrapMode;

import com.jme3.terrain.geomipmap.TerrainQuad;

import com.jme3.terrain.geomipmap.TerrainLodControl;

import java.util.ArrayList;

import java.util.List;

import com.jme3.renderer.Camera;

import com.jme3.bullet.control.RigidBodyControl;

import com.jme3.bullet.BulletAppState;

import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;

import com.jme3.bullet.control.CharacterControl;

import java.sql.;

import com.jme3.scene.Node;

import com.jme3.scene.Spatial;

import com.jme3.light.DirectionalLight;

import com.jme3.renderer.queue.RenderQueue.ShadowMode;

import com.jme3.shadow.BasicShadowRenderer;

import com.jme3.util.SkyFactory;

import com.jme3.animation.AnimChannel;

import com.jme3.animation.AnimControl;

import com.jme3.animation.AnimEventListener;

import com.jme3.animation.LoopMode;

import com.jme3.math.Ray;

import com.jme3.collision.CollisionResult;

import com.jme3.collision.CollisionResults;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.MouseButtonTrigger;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.input.KeyInput;

import java.util.
;

import com.jme3.font.BitmapText;



public class Main extends SimpleApplication implements AnimEventListener {



public static void main(String[] args) {

Main app = new Main();

app.start();

}

String sql_url = "jdbc:mysql://xxx";

String sql_user = "xxx";

String sql_pass = "xxx";

Statement stmt;

Connection conn = null;



RigidBodyControl floor_phy;

BulletAppState bulletAppState;

public Player player;

CapsuleCollisionShape capsule = new CapsuleCollisionShape(5f, 1);

CharacterControl character = new CharacterControl(capsule, 0.01f);

Boolean walk_stat=false;

TerrainQuad terrain;

AnimChannel channel;

AnimControl control;

DirectionalLight dl;

Vector3f last_pos=null;

Node mobs_all;

int[] map_tiles=new int[1600];

int walk_dir=1;



BitmapText hudText;



public HashMap<Integer, Monster> monster_all = new HashMap<Integer, Monster>();

@Override

public void simpleInitApp() {

try {

Class.forName("com.mysql.jdbc.Driver").newInstance();

conn = DriverManager.getConnection(sql_url, sql_user, sql_pass);

LogOut("verbunden");

stmt = conn.createStatement();

} catch (SQLException sqle) {

LogOut("FEHLER:SQLException: " + sqle.getMessage());

LogOut("FEHLER:SQLState: " + sqle.getSQLState());

LogOut("FEHLER:VendorError: " + sqle.getErrorCode());

sqle.printStackTrace();

} catch (Exception e) {

LogOut("FEHLER:" + e);

LogOut("FEHLER:" + e.getMessage());

// PlayerQuit(conn);

}finally {



}

bulletAppState = new BulletAppState();

stateManager.attach(bulletAppState);

inputManager.addMapping("walk", new KeyTrigger(KeyInput.KEY_W)); // trigger 2: left-button click

inputManager.addListener(actionListener, "walk");

inputManager.addMapping("walk_back", new KeyTrigger(KeyInput.KEY_S)); // trigger 2: left-button click

inputManager.addListener(actionListener, "walk_back");



initLandscape();

initSky();

//initTrees();



initMobs();

initPlayer(2);

initCams();

initLights();

initShadows();

initHud();

}



public void checkWalk(float tfp){

Vector3f walkDirection = new Vector3f();

Vector3f camDir = cam.getDirection().clone().multLocal(0.2f);

camDir.y = 0;

camDir.x = camDir.x *2;

camDir.z = camDir.z 2;

walkDirection.set(0, 0, 0);

walkDirection.addLocal(camDir);

character.setViewDirection(walkDirection);

if(walk_stat==true){

walkDirection=walkDirection.clone().mult(walk_dir);

Vector3f step=walkDirection.mult(4);

//Vector3f step=character.getViewDirection().add(character.getViewDirection()).mult(3);

Vector3f next_pos=character.getPhysicsLocation().add(step);

int next_tile=map_tiles[KoordToTile(next_pos)];

//LogOut("next_tile: "+next_tile);

if(next_tile==5 || next_tile==4){



}else{

character.setPhysicsLocation(next_pos);

}



/


int tile=KoordToTile(character.getPhysicsLocation());

int tile_id=map_tiles[tile];

//LogOut("CharPos: "+character.getPhysicsLocation()+" - "+tile+" - "+tile_id);

if(tile_id==5 || tile_id==4){

character.setWalkDirection(new Vector3f(0,0,0));

LogOut("Tree1: "+character.getPhysicsLocation());

character.setPhysicsLocation(last_pos);

LogOut("Tree2: "+character.getPhysicsLocation());

}else{

last_pos=character.getPhysicsLocation().clone();

character.setViewDirection(walkDirection);

character.setWalkDirection(walkDirection.mult(walk_dir));

}



/



}

}

protected void setCharCam(){

cam.setLocation(character.getPhysicsLocation().add(cam.getDirection().mult(-38f).add(new Vector3f(0, 15f, 0))));

dl.setDirection(cam.getDirection().normalizeLocal());

// System.out.println("111 " +character.getPhysicsLocation()+" - "+cam.getLocation());

}

protected void initLandscape(){



/
1. Create terrain material and load four textures into it. /

Material mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");

Texture grass = assetManager.loadTexture("Textures/map_bg_grass.png");

grass.setWrap(WrapMode.Repeat);

mat_terrain.setTexture("m_Tex1", grass);

mat_terrain.setFloat("m_Tex1Scale", 64f);



terrain = new TerrainQuad("my terrain", 65, 513, null);

terrain.setMaterial(mat_terrain);

terrain.setLocalTranslation(256, 0, 256);



rootNode.attachChild(terrain);



/
5. The LOD (level of detail) depends on were the camera is: */

List<Camera> cameras = new ArrayList<Camera>();

cameras.add(getCamera());

TerrainLodControl control = new TerrainLodControl(terrain, cameras);

terrain.addControl(control);





floor_phy = new RigidBodyControl(0.0f);

terrain.addControl(floor_phy);

bulletAppState.getPhysicsSpace().add(floor_phy);



}



public void initCams(){

flyCam.setMoveSpeed(50);

cam.setDirection(character.getViewDirection());

cam.setLocation(character.getPhysicsLocation().add(cam.getDirection().mult(2).add(new Vector3f(0, 0, 0))));

}

public void initLights(){

dl = new DirectionalLight();

dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());

rootNode.addLight(dl);

}

public void initSky(){

rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));

}

public void initPlayer(int Id){



player= new Player();

player.Id=Id;

player.run();



initChar(TileToKoord(player.Pos));

}

protected void initChar(Vector3f loc){

Node char_all =new Node("char_all");



loc.x+=6;

loc.z+=6;

loc.y=10;

char_all.setLocalTranslation(loc);

LogOut("charpos: "+loc);

Spatial player=assetManager.loadModel("Models/Oto/Oto.mesh.xml");



//player.setLocalTranslation(loc);

player.setShadowMode(ShadowMode.CastAndReceive);

char_all.attachChild(player);



control = player.getControl(AnimControl.class);

control.addListener(this);

channel = control.createChannel();

channel.setAnim("stand");



char_all.addControl(character);

bulletAppState.getPhysicsSpace().add(character);

//character.setPhysicsLocation(loc);



rootNode.attachChild(char_all);



}

protected void initTrees(){

Node trees_all =new Node("trees_all");

trees_all.setLocalTranslation(0, 0, 0);

String sqlCommand="SELECT * FROM v2_maps WHERE karte=5000";

try {

int count=0;

int count2=0;

int x=0;

int y=0;

Statement stmt2 = conn.createStatement();

ResultSet rs = stmt2.executeQuery(sqlCommand);

while (rs.next()) {



String[] tile_tmp=rs.getString("gfx").split("*");

for(int i=0;i<tile_tmp.length;i++){

count++;

count2++;

String[] tile_tmp2=tile_tmp.split("-");

map_tiles=Integer.parseInt(tile_tmp2[0]);

if(tile_tmp2[0].compareTo("4")==0 || tile_tmp2[0].compareTo("5")==0){

trees_all.attachChild(makeTree(new Vector3f(x+6, 0, y+6)));

}



x+=12;

if(count==40){

count=0;

x=0;

y+=12;

}



}



}

rootNode.attachChild(trees_all);

}catch (SQLException sqle){

//LogOut("FEHLER:SQLException: " + sqle.getMessage());

//LogOut("FEHLER:SQLState: " + sqle.getSQLState());

//LogOut("FEHLER:VendorError: " + sqle.getErrorCode());

}



}

public Spatial makeTree(Vector3f loc) {

// Spatial tree=assetManager.loadModel("Models/Tree/Tree2.mesh.xml");

Spatial tree=assetManager.loadModel("Models/Baum/Baum.mesh.xml");

tree.setLocalScale(3f);

tree.setLocalTranslation(loc);

return tree;

}

protected void initMobs(){

mobs_all =new Node("mobs_all");

mobs_all.setLocalTranslation(0, 0, 0);

String sqlCommand="SELECT * FROM v2_monster2_map WHERE karte="map-5000"";

try {

int count=0;

Statement stmt2 = conn.createStatement();

ResultSet rs = stmt2.executeQuery(sqlCommand);

while (rs.next()) {



mobs_all.attachChild(initMob(TileToKoord(rs.getInt("tile")),rs.getInt("id")));



}



rootNode.attachChild(mobs_all);

}catch (SQLException sqle){

//LogOut("FEHLER:SQLException: " + sqle.getMessage());

//LogOut("FEHLER:SQLState: " + sqle.getSQLState());

//LogOut("FEHLER:VendorError: " + sqle.getErrorCode());

}

}

protected Node initMob(Vector3f loc, int Id){



//loc.add(new Vector3f(6,50,6));

loc.x+=6;

loc.z+=6;

loc.y=3;

float start1=0;



CapsuleCollisionShape capsule1 = new CapsuleCollisionShape(3f, 2);

CharacterControl mob_ctrl = new CharacterControl(capsule1, 0.01f);

Node mob = (Node) assetManager.loadModel("Models/Wolf/Wolf.mesh.xml");

mob.setLocalTranslation(loc);



mob.addControl(mob_ctrl);



//bulletAppState.getPhysicsSpace().add(mob_ctrl);



Monster monster=new Monster();

monster.Id=Id;

monster.Name="Testmob";

monster.Grid=mob_ctrl;

monster.Core=this;

monster.Mesh=mob;

monster.run();

//monster.start();

monster_all.put(Id,monster);



return mob;

}



private void initShadows() {

BasicShadowRenderer bsr = new BasicShadowRenderer(assetManager, 256);

bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());

viewPort.addProcessor(bsr);

// Default mode is Off – Every node declares own shadow mode!

rootNode.setShadowMode(ShadowMode.Off);

}

public void MobCheck(){

Set set = monster_all.keySet();

Iterator it = set.iterator();

while (it.hasNext()) {

Monster mob=monster_all.get(Integer.parseInt(""+it.next()));

Vector3f char_tmp=character.getPhysicsLocation().clone();

char_tmp.y=0;

Vector3f mark_tmp=mob.Grid.getPhysicsLocation().clone();

mark_tmp.y=0;



float walkDist = new Vector3f(char_tmp).distance(mark_tmp);



if(walkDist<=15){

if(mob.Aggro==false){

mob.Aggro=true;

if (!mob.Ani.getAnimationName().equals("kampf")) {

mob.Ani.setAnim("kampf", 0.50f);

mob.Ani.setLoopMode(LoopMode.Loop);

LogOut("mobani: "+mob.Ani);

}

//mob.Ani.setAnim("kampf");

//mob.Ani.setLoopMode(LoopMode.Loop);

}

Vector3f dir= mob.Grid.getPhysicsLocation().clone().subtract(character.getPhysicsLocation().clone());

dir.y=0;

mob.Grid.setViewDirection(dir);

}else{

if(mob.Aggro==true){

mob.Aggro=false;

mob.Ani.setAnim("stand");

mob.Ani.setLoopMode(LoopMode.DontLoop);



//mob.Grid.

}

}

}



}

public void initHud(){

hudText = new BitmapText(guiFont, false);

hudText.setSize(guiFont.getCharSet().getRenderedSize()); // font size

hudText.setColor(ColorRGBA.Black); // font color

hudText.setText(player.getName()+"nLevel: "+player.getLevel()+"nHP: "+player.getHp());

//hudText.setLocalTranslation(300, hudText.getLineHeight(), 0); // position

hudText.setLocalTranslation(20, hudText.getLineHeight()20, 0); // position

guiNode.attachChild(hudText);

}



@Override

public void simpleUpdate(float tpf) {

//TODO: add update code

dl.setDirection(cam.getDirection());

setCharCam();

checkWalk(tpf);

MobCheck();



}



@Override

public void simpleRender(RenderManager rm) {

//TODO: add render code

}





public int KoordToTile(Vector3f loc){



int x=(int)loc.x;

int y=(int)loc.z;

int x_tile=(int)Math.floor(x/12);

int y_tile=(int)Math.floor(y/12);

//LogOut(x_tile+" - "+y_tile);

int tile= x_tile+(y_tile
40);

return tile;

}

public Vector3f TileToKoord(int tile){

int x=(tile%40)*12;

int y=(tile/40)*12;

Vector3f koord = new Vector3f(x,0,y);

return koord;

}

private void LogOut(String text) {

System.out.println("[" + this + "]" + text);

}



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

//channel.setAnim(animName, 0.0f);

//channel.setSpeed(0.7f);

// channel.setLoopMode(LoopMode.DontLoop);

//channel.setSpeed(1f);

if (animName.equals("stand")) {



}else{

LogOut(animName+" - "+channel);

channel.setAnim(animName);

}

if (animName.equals("kampf")) {

//LogOut("HP: "+this.player);

//player.setHp(player.getHp()-5);

//hudText.setText(player.getName()+"nLevel: "+player.getLevel()+"nHP: "+player.getHp());

}



}

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

// unused

}

}



[/java]

skee said:
If i create 20 monsters and i start the animation of one of them, the onAnimCycleDone is triggered 20 times.


No! You are starting the animation of each one instead! If you are instancing the monster for 20 times, of course the "onAnimCycleDone" is called for 20 times, a call for each monster.

Yeah, the problem is you have only one listener for multiple animations. Either discern them in the onAnimCycleDone or create separate listeners, e.g. as anonymous inner classes.

glaucomardano said:
No! You are starting the animation of each one instead! If you are instancing the monster for 20 times, of course the "onAnimCycleDone" is called for 20 times, a call for each monster.


this sounds to me like all monsters should play their animation. but the animation is only played once. at the monster,at which i want it to be played...
normen said:
Yeah, the problem is you have only one listener for multiple animations. Either discern them in the onAnimCycleDone or create separate listeners, e.g. as anonymous inner classes.

How can i discern them in the onAnimCycleDone? At the moment i dont see (within onAnimCycleDone), which monster played the animation.

But i think, seperate listeners could be better. How can i create these?
Sorry, i am not so good at java yet, to get this by myself...
skee said:
How can i discern them in the onAnimCycleDone? At the moment i dont see (within onAnimCycleDone), which monster played the animation.


I don't know if in AnimControl or AnimChannel there's a method that returns its controller spatial, you could check them, but you also could do something like this:

[java]
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
For(Spatial spat:mobs_all) {
if(spat instanceof Monster) {
Monster monster = (Monster)spat;
Node monsterNode = monster.Mesh;
AnimControl monsterAnimControl = monsterNode.getControl(AnimControl.class);
if(monsterAnimControl.equals(control)) {
//do something here
channel.setAnim(animName);
}
}
}
}
[/java]


skee said:
But i think, seperate listeners could be better. How can i create these?
Sorry, i am not so good at java yet, to get this by myself...


LOL! You don't need to implement the AnimEventListener in both Monster.java and Main.java, rather implement it only in Monster.java. Transfer the AnimEventListener's implementation in Main.java to Monster.java.