Collision with Spatials

So here’s the problem. This is all part of a tower defense game. I have code in the GamePlayAppState class that generates towers:
[java]
public void generateTowers(int num){
for(int index = 0; index < num; index++){
Spatial tower_geo = assetManager.loadModel(“Models/tower.j3o”);
int leftOrRight = (index % 2 == 0 ? 1 : -1); // -1 or +1
float offset_x = leftOrRight * 2.5f;
float offset_y = TOWER_HEIGHT * .5f;
float offset_z = index + 2;
Vector3f loc = new Vector3f(offset_x, offset_y, offset_z);
tower_geo.setLocalScale(0.66f,2.0f,0.66f);
tower_geo.setLocalTranslation(loc);
tower_geo.setUserData(“towerHeight”, TOWER_HEIGHT);
tower_geo.setUserData(“towerammo”, chargesNum);
tower_geo.setUserData(“index”, index);
tower_geo.addControl(new TowerControl(this));
towerNode.attachChild(tower_geo);

    }
}

[/java]

I recently switched over from Cubes to tower.j3o

So then I have code in the main class that allows the player to left click a tower and hit the ‘G’ key to load a charge/bullet. I have this all in an actionlistener and intialized so everything works. I tested it before with cubes geometries.

[java]
if(mapping.equals(“Select”) && !keyDown){
System.out.println(“Check 1”);
//TODO:deselects previous tower
CollisionResults results = new CollisionResults();
Vector2f click2d = inputManager.getCursorPosition();
Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.getX(), click2d.getY()), 0f);//maybe add clone()
Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d);
//cast the ray
Ray ray = new Ray(click3d, dir);
//check for collisions with towers(collideWith check for collisions)
rootNode.getChild(“tower node”).collideWith(ray, results);
System.out.println(“Check 2”);
//determine what user selected
if(results.size() > 0){
// ray collided with a tower
CollisionResult closest = results.getClosestCollision();
//selected tower number
Spatial spatial = closest.getGeometry();
TowerControl control = spatial.getControl(TowerControl.class);
selected = control.getIndex();//BUG
System.out.println(“Check 4”);
//tower is selected. Load charge
//check budget

                }else{
                    //ray doesn't hit anything
                    selected = -1;
                }
                
                 
            }[/java] 

The problem comes in on the line with :
selected = control.getIndex();//BUG

It seems like as soon as I switch from cubes to models this line just doesn’t work and throws an error. The getIndex method is in the TowerControl control class and each tower that is generated has an index. This all worked when I had Geometry cubes.

getIndex method in towercontrol class:
public int getIndex() {
return (Integer) spatial.getUserData(“index”);
}

I have no idea what is going on. If anyone can help me then I would appreciate it. Thanks!

Maybee the j3o has no indes stored? or do you store them on each start? can you show the code for that?

Sorry, what do you mean by indes. If you accidentally meant indexes: the indexes are generated in the generateTower method. I have been told this is a Spatial, parent, child node problem. A spatial can be a Geometry or a node. I’ve tried .getParent() two times after getGeometry on line 18 in the key input code. Why is this?

Ah ok it could be deeper nested,

do recursive method

-> does hav index? if yes return it (if controll != null)
-> if no call method on parent
-> if no parent exists, you and no index was yet found, abort throw exception / return null.

This should really be in the FAQ since it seems we get this one two times a week now instead of just once a week. :slight_smile:
[java]
for( Spatial s = collisions.getClosestGeometry(); s != null; s = s.getParent() ) {
TowerControl control = s.getControl(TowerControl.class);
if( s != null ) {
// do something with s or control
break;
}
}
[/java]

1 Like

@pspeed Apparently this has got something to do with Spatials and nodes. When I call
System.out.println(closest.getGeometry().getParent().getParent()); it prints out tower.blend which is the blend file i used to convert to j3o.

So now I have
[java]selected = closest.getGeometry().getParent().getParent();[/java]

How would I go down from the rootNode to this. Apparently one of the node is Textures/tower.blend.

Whenever I attempt to access it:
rootnode.getChild(“tower node”).getChild(“Textures/tower.blend”);

it gives me an red underlined error.

getChild does not work like this. it uses and index, not the name.

@Empire Phoenix

So how does it actually work. getChild(“tower node”) works but .getChild(“Textures/tower.blend”) doesn’t. What is the proper way of using it?

I strongly suggest to first read the wiki tutorials,
then take a look at the javadoc it usually explains each method quite good.

Yea but the problem is that I’m sort of confused right now. I know how to go up using getParent() but I have problem when coming down the scene graph from rootNode. If you could just explain it to me how to do that. I’ve read tutorials and other forums topics and so far none of them were helpful.

getChild() returns a spatial which then you will have to case to Node because only nodes have children… but you don’t really need to do it because getChild() will already check for children so unless you really need only the towers under a specific subnode then you could have just put the tower name in.

Note: if casting is unfamiliar to you then you might consider learning some more Java until that kind of thing becomes easier. JME is tough to learn if you are also still learning Java.

@pspeed
So basically I have this(ray casting for clicks on towers):
[java]
CollisionResult closest = results.getClosestCollision();
selected = closest.getGeometry().getParent().getParent().getControl(TowerControl.class).getIndex();
[/java]

selected will be -1 if the ray casting did not pick up a tower. getIndex is a custom method I made in towercontrol class.

So then I have the code that loads the charges into the selected tower(determined by index):

[java]

if(selected != -1 && state.getBudget() > 0 && !keyDown){
TowerControl tower = rootNode.getChild(“tower”+selected).getControl(TowerControl.class); //ERROR
//code to load charges
//blablabla
}
[/java]

When I run it it gives me an error on the TowerControl tower =… line. How do I go down the scenegraph from the rootnode so it is equivalent to two getparents on the geometry.

If getChild returns a Spatial, which is a node or geometry and the Textures/tower.blend is a node(I verified by printing in the console), then why doesn’t getChild(“Textures/tower.blend”) work?

closest.getGeometry() doesn’t work and closest.getGeometry().getParent() also doesn’t work.

I remember the hierarchy is like this:
rootnode -> tower node -> Textures/tower.blend (Node) -> tower -> mesh1

First, can I ask why you need to come down from the root?

As to the other, I’m still not sure why the loop I posted doesn’t work for finding the tower from the Geometry. getParent().getParent().etc. will only work until the model changes the next time you edit it and now it has a different hierarchy, where as searching up until TowerControl exists is guaranteed to always work to find the tower with the control in it.

Isn’t everything on the scenegraph a subnode of the rootnode. I’m actually following a tutorial Jmonkeyengine 3.0 for beginners
[link removed - normen]

on pg 122-123 where it asks you to load models into the tower defense game.

What I’m basically doing is:

  1. getting the closest tower and getting its index/tower number
  2. Load a charge into Tower X

@pspeed So the loop basically asking me to check through the entire chain/hierarchy until i find the one with the TowerControl?
I’ll try that actually?

EDIT: Not sure if this will help but I opened tower.j3o(the model I’m using for towers in the game) in the SceneExplorer view so I can get its hierarchy. Textures/tower.blend is a node. tower is a subnode of that.

@kevin.shen18 said: Isn't everything on the scenegraph a subnode of the rootnode. I'm actually following a tutorial Jmonkeyengine 3.0 for beginners [link removed - normen]

on pg 122-123 where it asks you to load models into the tower defense game.

What I’m basically doing is:

  1. getting the closest tower and getting its index/tower number
  2. Load a charge into Tower X

@pspeed So the loop basically asking me to check through the entire chain/hierarchy until i find the one with the TowerControl?
I’ll try that actually?

EDIT: Not sure if this will help but I opened tower.j3o(the model I’m using for towers in the game) in the SceneExplorer view so I can get its hierarchy. Textures/tower.blend is a node. tower is a subnode of that.

Yes, but as a general rule your model could have any number of Geometries and any number of indirections in the hierarchy. The for loop I showed walks up from the geometry to the first parent that has a TowerControl.

Yes, all attached things in the scene graph are available from the root node. But if I knew why you were trying to traverse down then I could potentially show you a better way.

Errr… wait. Did you just link a pirated copy of our book?

removes links, gives reproachful stare

@pspeed
Would you prefer it if I post the entire class and code?

@normen
Sorry about that. That wasn’t the link I used. I got the book off bookboon or something, but its a free ebook tutorial.

@pspeed

Main class with the ray casting. The problem is down below in the selection.
GamePlayAppState is an appstate class.
[java]
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.system.AppSettings;

/**

  • test

  • @author normenhansen
    */
    public class Main extends SimpleApplication {

    public static void main(String[] args) {
    AppSettings settings = new AppSettings(true);
    settings.setTitle(“BasicTowerDefense”);
    settings.useInput();
    Main app = new Main();
    app.setSettings(settings);
    app.start();

    }
    //private GamePlayAppState game;
    private int selected = -1;
    private GamePlayAppState state = new GamePlayAppState();

    @Override
    public void simpleInitApp() {
    // GamePlayAppState state = new GamePlayAppState();
    stateManager.attach(state);

    flyCam.setDragToRotate(true);
     inputManager.setCursorVisible(true);
    inputManager.addMapping("Select", new MouseButtonTrigger(0));//click select
    inputManager.addMapping("LoadCharge", new KeyTrigger(KeyInput.KEY_G));
    inputManager.addListener(actionListener, "Select", "LoadCharge");
    

    System.out.println(“start”);
    }

    @Override
    public void simpleUpdate(float tpf) {
    //TODO: add update code
    }

    @Override
    public void simpleRender(RenderManager rm) {
    //TODO: add render code
    }
    private ActionListener actionListener = new ActionListener(){
    @Override
    public void onAction(String mapping, boolean keyDown, float tpf){
    System.out.println(“actionlistener”);
    //use ray casting to identify clicked tower
    if(stateManager.hasState(state)){
    System.out.println(“hasgame”);
    //left click to select tower
    if(mapping.equals(“Select”) && !keyDown){
    System.out.println(“Check 1”);

                 CollisionResults results = new CollisionResults();
                 Vector2f click2d = inputManager.getCursorPosition();
                 Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.getX(), click2d.getY()), 0f);//maybe add clone()
                 Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d);
                 //cast the ray
                 Ray ray = new Ray(click3d, dir);
                 //check for collisions with towers(collideWith check for collisions)
                 rootNode.getChild("tower node").collideWith(ray, results);
                 System.out.println("Check 2");
                
                 //determine what user selected
                 if(results.size() &gt; 0){
                     // ray collided with a tower
                     CollisionResult closest = results.getClosestCollision();
                     
                     //selected tower number
                     selected = closest.getGeometry().getParent().getParent().getControl(TowerControl.class).getIndex();
                   
                                                                                                       
                    System.out.println("Check 4");
                    //tower is selected. Load charge
             //check budget 
            
                   
                 }else{
                     //ray doesn't hit anything
                     selected = -1;
                 }
                 
                  
             }
              if(selected != -1 &amp;&amp; state.getBudget() &gt; 0 &amp;&amp; !keyDown){
                 TowerControl tower = rootNode.getChild("tower"+selected).getControl(TowerControl.class);
                 if(tower.getChargeNum() &lt; 5){
                     //tower not full. load a charge
                     if(mapping.equals("LoadCharge")){
                         tower.addCharge(state.getLoadedCharge());
                         System.out.println("Check 4");
                     }
                 }
             }
             
         }
     }
    

    };
    }
    [/java]

If there’s anything you need just ask.

The error is on this line: TowerControl tower = rootNode.getChild(“tower”+selected).getControl(TowerControl.class);

The variable “selected” holds the tower index. I gave each tower an index in a generateTower method in Gameplayappstate class.

No, the book is not free, thats all pirated copies.