Game Collectables and collision, like sonic rings or crash crates or mario coins

ok im still new to jme3 and as i go ive been trying to keep track and document my experience learning so hopefully it could be of help to others,

the current issue ive come across is exactly how do i populate my level with items like sonics rings or marios coins?

ive made a quick method for creating and using my models that are to be static, non moving, i give to it the model its to load, its scale and xyz co ordinates

[java]public void mesh(AssetManager assetManager,String model, float scale, float locX,float locY, float locZ) //pass model mesh, scale in world, and x y z position in world
{
mesh = assetManager.loadModel(model); //loads 3d mesh
mesh.setLocalTranslation(locX + i , locY, locZ); //sets position
mesh.setLocalScale(scale); //sets scale
crateNode = new Node();
crateNode.attachChild(mesh);
smash = new AudioNode(assetManager, “Sounds/Boxbreak.WAV”, false);
smash.attachChild(mesh);
smash.setLooping(false);
smash.setVolume(0.2f);
smash.setPositional(true);
smash.setLocalTranslation(mesh.getLocalTranslation());
crateNode = new Node();
crateNode.attachChild(mesh);
rootNode.attachChild(crateNode);
}[/java]

in simpleinitapp() i just type mesh(assetManager,“where/ever/model/is.j3o”,2,10,0,10);

i also have a collision check method that will check for a collision with my player (a car node) and the crateNode to my mesh,

[java]private void colCheck(Node a, Node b)
{

 CollisionResults results = new CollisionResults();  //creates new collision results
 BoundingVolume geomBounding = b.getWorldBound();   //creates bounding volume from car chasis for testing collision
 a.collideWith(geomBounding, results);  //check collision,  spatial must be first and volume after ie spatial.collideWith(boundingVolume, results);
 if (results.size() != 18)  
 {
     if(i != 0)
     {
         i = 0;
         
     }
 } 
 if (results.size() >= 18 && results.size() < 19)  //if theres a collision results bigger than 0 do whats inside brackets
    { 
          System.out.println("Number of Collisions between" + 
          a.getName()+ " and " + b.getName() + ": " + results.size());
          CollisionResult closest  = results.getClosestCollision();    //get closest collision resultd for further collision testing
      
      if(closest.getDistance() <= 0 )
        {
            if(closest.getGeometry().getName().equals("crate"))
            {
                System.out.println("its a crate");
               
              if(i !=  1)  //on first collision detection i = 0 whats in this loop runs, and i becomes = to 0, for all other collisions detected that are unneeded whats in here wont run again
                {
                     crateNode.detachChild(mesh);           //  remove crate node from visibility
                     smash.play();
                 //  bulletAppState.getPhysicsSpace().remove(rigA);  //remove its body so an invisible force wont remain

                     ++number;  // increases box count by 1
                     hudText.setText("Boxes Broken: " + number);
                     ++i;
               
                }
            }
            
            if(closest.getGeometry().getName().equals("life"))
            {
                System.out.println("its a life");
                if(i !=  1)  //on first collision detection i = 0 whats in this loop runs, and i becomes = to 0, for all other collisions detected that are unneeded whats in here wont run again
                {
                     crateNode.detachChild(mesh);           //  remove crate node from visibility
                     smash.play();
                 //  bulletAppState.getPhysicsSpace().remove(rigA);  //remove its body so an invisible force wont remain

                     ++number;  // increases box count by 1
                     hudText.setText("Boxes Broken: " + number);
                     ++i;
               
                }
                
            }
            
        
        }
        
    }  
   
    else
    {
      //  i=0;
    }

} [/java]

  i have it working with 1 box and depending on what model ive set to be my mesh it will do different things if i want it to like raise a counter or lower it,  but my issue is how do i populate my game with a bunch of boxes coins or rings or items of such,  i can get 1 object loaded and trying 

[java]mesh(“model 1”,1,10,0,10);
mesh(“model 2”,1,20,0,10);
mesh(“model 3”,1,30,0,10);
mesh(“model 4”,1,40,0,10);[/java]

doesnt work, it will only recognise collision with mesh(“model 4”,1,40,0,10) and none of the others, so how do i populate the world? ive spent a week looking up the forum with no luck and cant find any videos or tutorials highlighting this particular topic. any help is appreciated and i thank the developers for such a great game engine.

For making collectible coins you need two things:

  1. For populating your world with coins.
    [java]
    Node teapotNode = (Node) assetManager.loadModel(“Models/Teapot/Teapot.mesh.xml”);
    Geometry teapot = (Geometry) teapotNode.getChild(0);

// Sphere sph = new Sphere(16, 16, 4);
// Geometry teapot = new Geometry(“teapot”, sph);

    Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
    mat.setFloat("Shininess", 16f);
    mat.setBoolean("VertexLighting", true);
    teapot.setMaterial(mat);
    
   // show normals as material
    //Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");

    for (int y = -10; y < 10; y++){
        for (int x = -10; x < 10; x++){
            Geometry clonePot = teapot.clone();
            
            //clonePot.setMaterial(mat);
            clonePot.setLocalTranslation(x * .5f, 0, y * .5f);
            clonePot.setLocalScale(.15f);
            
            LodControl control = new LodControl();
            clonePot.addControl(control);
            rootNode.attachChild(clonePot);
        }
    }

[/java]

  1. For checking collisions with models. https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:collision_and_intersection

ok ive gotten the cloning working, i couldnt get it to work with geometry it kept throwing ClassCastException: com.jme3.scene.Node cannot be cast to com.jme3.scene.Geometry

but i could use my crateNode and have it cloned if thats at all right to do, the code is bellow here, ive commented out my old code that was there,

[java]public void mesh(AssetManager assetManager,float scale, float locX,float locY, float locZ) //pass model mesh, scale in world, and x y z position in world
{

// crateNode = (Node) assetManager.loadModel(“Models/crates/crate/myscene.j3o”);
crateNode = (Node) assetManager.loadModel(“Models/crates/crate/myscene.j3o”);
// mesh = (Geometry) crateNode.getChild(0);

    for (int y = -10; y < 10; y++){
        for (int x = -10; x < 10; x++){
            cloneCrate = crateNode.clone();

            cloneCrate.setLocalTranslation(x * locX, locY, y * locZ);
            cloneCrate.setLocalScale(scale);

            LodControl control = new LodControl();
          //  cloneCrate.addControl(control);
            rootNode.attachChild(cloneCrate);
        }
    }

// mesh = assetManager.loadModel(model); //loads 3d mesh
// mesh.setLocalTranslation(locX + i , locY, locZ); //sets position
// mesh.setLocalScale(scale); //sets scale
// crateNode = new Node();
// crateNode.attachChild(mesh);
// smash = new AudioNode(assetManager, “Sounds/Boxbreak.WAV”, false);
// smash.attachChild();
// smash.setLooping(false);
// smash.setVolume(0.2f);
// smash.setPositional(true);
// smash.setLocalTranslation(mesh.getLocalTranslation());
// mesh.addControl(body);
// crateNode = new Node();
// crateNode.attachChild(mesh);
// rootNode.attachChild(crateNode);

}[/java]

i still cant get the collision detection working, well technically it works but it keeps saying there is collisions even though i havent hit anything yet,
collision detection code is this

[java]private void colCheck(Spatial a, Node b)
{

 CollisionResults results = new CollisionResults();  //creates new collision results
 BoundingVolume geomBounding = b.getWorldBound();   //creates bounding volume from car chasis for testing collision
 a.collideWith(geomBounding, results);  //check collision,  spatial must be first and volume after ie spatial.collideWith(boundingVolume, results);
 if (results.size() != 0)  
 {
     if(i != 0)
     {
         i = 0;
         
     }
 } 
 if (results.size() >= 0)  //if theres a collision results bigger than 0 do whats inside brackets
    { 
          System.out.println("Number of Collisions between" + 
          a.getName()+ " and " + b.getName() + ": " + results.size());
          CollisionResult closest  = results.getClosestCollision();    //get closest collision resultd for further collision testing

// if(closest.getDistance() <= 0 )
// {
// if(closest.getGeometry().getName().equals(“crate”))
// {
// System.out.println(“its a crate”);
//
// if(i != 1) //on first collision detection i = 0 whats in this loop runs, and i becomes = to 0, for all other collisions detected that are unneeded whats in here wont run again
// {
// rootNode.detachChild(mesh); // remove crate node from visibility
// smash.play();
// // bulletAppState.getPhysicsSpace().remove(rigA); //remove its body so an invisible force wont remain
//
// ++number; // increases box count by 1
// hudText.setText("Boxes Broken: " + number);
// ++i;
//
// }
// }
//
// if(closest.getGeometry().getName().equals(“life”))
// {
// System.out.println(“its a life”);
// if(i != 1) //on first collision detection i = 0 whats in this loop runs, and i becomes = to 0, for all other collisions detected that are unneeded whats in here wont run again
// {
// crateNode.detachChild(mesh); // remove crate node from visibility
// smash.play();
// // bulletAppState.getPhysicsSpace().remove(rigA); //remove its body so an invisible force wont remain
//
// ++number; // increases box count by 1
// hudText.setText("Boxes Broken: " + number);
// ++i;
//
// }
//
// }
//
//
// }
//
// }
//
// else
// {
// // i=0;
}

} [/java]

and its being called in simpleupdate() like this

[java]@Override
public void simpleUpdate(float tpf)
{

    colCheck(cloneCrate,carNode); 
   
}[/java]

it should only print this out if theres a collision

Number of Collisions betweenModels/crates/crate/myscene-scene_node and Models/Ferrari/Car-scene_node: 0

but it prints it infinitely even though there is no collision ???

“results.size() >= 0”

Read that to yourself and let me know what you think it says.

ok woops i can see why [java] “results.size() >= 0″[/java] causes what it does, ok i have that fixed back to [java]results.size() > 0[/java] but hitting any of the clone crates still produces nothing, the collision check is just simplified to print out info for now when theres a collision between the cloneCrate and carNode, the first a spatial and second a node where a bounding volume is gotten from carNode for testing, i know the issue is something stupid or wrong im doing, i just cant see it just yet.

[java]private void colCheck(Spatial a, Node b)
{

 CollisionResults results = new CollisionResults();  //creates new collision results
 BoundingVolume geomBounding = b.getWorldBound();   //creates bounding volume from car chasis for testing collision
 a.collideWith(geomBounding, results);  //check collision,  spatial must be first and volume after ie spatial.collideWith(boundingVolume, results);
 
 if(results.size() &gt; 0)
 { 
           System.out.println("Number of Collisions between" + 
           a.getName()+ " and " + b.getName() + ": " + results.size());
}

}
[/java]

ok ive fixed collision detection it gives detection now for all the clones, again it was something i was doing, i only tested for the spatial that was being cloned i changed it to test the node all the clones got attached to, that last and final thing is how do i identify the clones so that say if clone1 is hit it removes clone1 and not every other clone on the map?

I haven’t done much coding in JME, but in any other game I would keep a list of some kind (linked list for example) with pointers to the game objects. Then iterate over them and check collision with each one vs the player.

That way you know which object you are colliding with and you can then remove it from the list (and from the scene) without affecting the others.

We didn’t have any coins in our game but powerups, I could go and check how they are collided.

How do you do the collision checking for each object? Do you iterate through the nodes in the scene, and pass each one to the collision check individually? I suppose that must be possible and would be a replacement for my idea of a linked list above.

@ettavsdro

i use this code to make a clone of my collectable

[java]public void mesh(AssetManager assetManager,float scale, float locX,float locY, float locZ) //pass model mesh, scale in world, and x y z position in world
{
crateParent = new Node();
crateNode = (Node) assetManager.loadModel(“Models/crates/crate/myscene.j3o”);

  //  for (int y = -10; y &lt; 5; y++)
        for (int x = -10; x &lt; 0; x = x+2){
            cloneCrate = crateNode.clone();
            cloneCrate.setLocalTranslation(locX + x + x + x + x + x + x, locY, locZ);
            cloneCrate.setLocalScale(scale);
          
            crateParent.attachChild(cloneCrate);
            rootNode.attachChild(crateParent);
        }[/java]

and this to check collision

[java]private void colCheck(Node a, Geometry b)
{

 CollisionResults results = new CollisionResults();  //creates new collision results
 BoundingVolume geomBounding = b.getWorldBound();   //creates bounding volume from car chasis for testing collision
 
 a.collideWith(geomBounding, results);  //check collision,  spatial must be first and volume after ie spatial.collideWith(boundingVolume, results);

// CollisionResult closest = results.getClosestCollision();
if(results.size() > 13)
{ System.out.println("Number of Collisions between " +
a.getName()+ " and " + b.getName() + ": " + results.size() + "CRATE HIT " + ## various code ive tried to identify one clone ##;

}

}[/java]

the collision check is between my crateParent node to which all clones are attached and the chasis geometry of my players car, im able to get it to register collisions and where but i cant get a unique name for any clone so that if i hit one clone only is is removed and all others left in tact.

crateParent is a and chasis is b ive tried

[java]a.getName();
results.getClosestCollision().getGeometry();
results.getClosestCollision().getGeometry().getName();
results.getClosestCollision().getGeometry().getKey();
a.hashCode();[/java]

so far ive only gotten the same name or numbers for each crate or ive gotten null

ok this gets me a unique code per box/crate

[java]results.getClosestCollision().getGeometry().hashCode()[/java]

except i had hoped for a name of somesort that could have been used with

[java]crateParent.detachChild( ## means of identifying the clone hit ## );[/java]

i dont see a way of using the long hash code yet to tell which clone to remove

When you use the assetmanager to load a j3o file it most of the time is allready a node (haven’t seen or made any other so far where the parent element is anything else then a node), so naming the loaded model “mesh” might cause some confusion, just cast it to a node and you don’t need to wrap it in another node before adding it to the rootnode.

It is a good idea to create a node in the rootnode that contains all your collectibles you want to check collision on, that way you can get the collision result from that node and only this node has to be traversed to do the checking, if you use that node getting the collision result will traverse the childs of this node, so no need on checking every object with each other (e.g. no point in checking if the collision happened with the floor because normaly your physics take care of that).

To identify your cloned nodes you can set their name so something you can recognize, or maybe add a custom attribute to it. Depending on your technique there it might be that you dont even need to identify which object you hit, just how to react (e.g. “i hit a coin” => “get value from coin” => “add value to my score/money” => “reomve coin from scenegraph”, this way it doesnt matter which coin was it, just that it was a coin).

@ryukajiya
the way i have my nodes right now is i have the rootNode and attached to it is my crateParent all my collectables are attached to crateParent so i only check collisions with crate parent, im able to get results saying that ive hit all the clones, only issue i have now is i dont know how to dettach just that one particular clone thats been hit while leaving the rest intact,

im able to get a unique hashcode from each clone now though i dont know what good that is to me as i dont know how to use it to dettach that particular clone from the crateParent node.

   as u say here        “i hit a coin” =&gt; “get value from coin” =&gt; “add value to my score/money” =&gt; “reomve coin from scenegraph”,

i can get the first 3 parts done, i can tell when somethings hit, get it to play sounds or get certain values, add to score its the removing that one clone from the scene thats the issue,

[java]crateParent.detachChild(cloneCrate);[/java]

only removes the one original crate, as in i can hit any clone and itll remove only the first crate, not itself.

This might give you a clue. With my current project i add models to a special node that contains all the scenery:

[java]
public class SSIStatics {

public static final String NODETYPE = "nodetype";
public static final String NODETYPE_ENVIRONMENT = "SSIEnviroment";
public static final String NODETYPE_SPAWNPOINT = "SpawnPoint";
public static final String NODETYPE_INTERACTABLES = "SSIInteractable";
public static final String NODENAME_ENVIRONMENT = "Enviroment";
public static final String NODENAME_SPAWNPOINT = "SpawnPoints";
public static final String NODENAME_INTERACTABLES = "Interactables";

}

// adding nodes that are scenery like this
Node envNode = (Node) assetManager.loadModel(environment.getModel());
envNode.setUserData(SSIStatics.NODETYPE, SSIStatics.NODETYPE_ENVIRONMENT);
envNode.setUserData(SSIStatics.NODETYPE_ENVIRONMENT, environment.getUID());
envNode.move(environment.getPosition());
envNode.setLocalRotation(Quaternion.IDENTITY);
envNode.rotate(environment.getRotation());
env.attachChild(envNode); // env is a node that contains all environment

[/java]

Then when checking collisions i can get the user data and determine what kind of node it was i hit. Because the user data is stored on the node that gets attached to the environment node i can also identify this one as being the one i need to remove if i want to get rid of it.
Take a look at mousepicking with this:

[java]
@Override
public void onAction(String name, boolean isPressed, float tpf) {
if (name.equals(“pick”) && isPressed) {
// Reset results list.
CollisionResults results = new CollisionResults();
// Convert screen click to 3d position
Vector2f click2d = inputManager.getCursorPosition();
Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();
Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
// Aim the ray from the clicked spot forwards.
Ray ray = new Ray(click3d, dir);
// Collect intersections between ray and all nodes in results list.
rootNode.collideWith(ray, results);
if (results.size() > 0) {
Node picked = results.getClosestCollision().getGeometry().getParent();

            while (picked != null &amp;&amp; picked.getUserData(SSIStatics.NODETYPE) == null) {
                picked = picked.getParent();
            }
            if (picked != null) {
                selectNode(picked);
            }
        } else {
            Plane ground = new Plane(Vector3f.UNIT_Y, 0);
            Vector3f groundpoint = new Vector3f();
            ray.intersectsWherePlane(ground, groundpoint);

            groundpoint.x = Math.round(groundpoint.x / 8f) * 8f;
            groundpoint.y = Math.round(groundpoint.y / 8f) * 8f;
            groundpoint.z = Math.round(groundpoint.z / 8f) * 8f;

            setCoordinateAxes(groundpoint);
            selectNode(null);
        }
    }
}

[/java]

As you can see i take the geometry from the collision result and look for its parent, because usualy any geometry is somehow attached to some kind of node. Then i look through the parents till i find a node that contains the user data that i set on loading the models (or select nothing if i can’t find any). I am using a ray here and check against the root node because it is the mouse click event and basicaly in this case i need to be able to click anything.
For your problem you can then use that node ro remove it from your collection node.

Edit: Oh, and the assetmanager can give you clones too, normaly you can just load the model and you get a clone of an allready loaded model if you have loaded that file before allready.

yes yes yes, success, success thank you all for your help adding this

[java]results.getClosestCollision().getGeometry().getParent().removeFromParent();[/java]

now removes whatever ive hit, thank you for the great help, and thank you @ryukajiya because i believe that was what u meant by

For your problem you can then use that node ro remove it from your collection node.

Just keep in mind that this removes the node that is direct parent of the geometry. When using a loaded j3o file this will actualy be the node from that model. Earlier you posted a code where you wrap that node in another node on wich you put a soundnode too. This will not get removed. To remove that one you would have to go a parent further up.

ok so use this then

[java]results.getClosestCollision().getGeometry().getParent().getParent().removeFromParent();[/java]

ive tried it and it get the same result i wanted so thats good,