Ray not colliding with Node

Hi there!

I am making a game which involves a gun which shoots Rays (linearly). I am trying to make it so that when a Ray collides with a certain ‘collection’ (group) of objects, it will create some particles on the ground which look like a bullet has hit there.

To do this, I was looking at the TestMousePick.java example, and noticed that they use a Node to group spatials together. So, I created a node called groundShootables which I attach to the rootNode. So far, to test it, I have attached the scene and a rigid body cube to the groundShootables.

The Problem Is:
Shooting the rigid body cube works fine - particles appear as intended. However, shooting the scene (terrain created in the terrain editor - Spatial) doesn’t work. The ray (indicated by the red line which is drawn in the “void shoot(…)” code below) goes straight through the ground without recording any collision at all.

I’m not entirely sure what I have done wrong! I would appreciate some help :lol: !

The “Shoot” Method:

[java]
@Override
public void shoot(float tpf, Vector3f origin, Vector3f direction)
{
time += tpf;
if(emittingParticles == false){
muzzleEmitter.setParticlesPerSec(15);
shellsEmitter.setParticlesPerSec(5);
emittingParticles = true;
}

    //Shoot a round every 0.2 seconds.
    if(time >= 0.2f)
    {
        System.out.println("Shooting!");
        Ray ray = new Ray(origin, direction);
        
        Line line = new Line(origin, direction); //FOR VISUALZING THE RAY
        Geometry rayLine = new Geometry("ray",line); //FOR TESTING
        Material lineMat = new Material(aM, "Common/MatDefs/Misc/Unshaded.j3md");
        lineMat.setColor("Color", ColorRGBA.Red);
        rayLine.setMaterial(lineMat);
        groundShootables.attachChild(rayLine);
        
        
        CollisionResults terrainResults = new CollisionResults();
        int numCollisions = groundShootables.collideWith(ray , terrainResults);
        if (numCollisions > 0)
        {
            CollisionResult hit = terrainResults.getClosestCollision();
            System.out.println("Hit the ground!");
            
            
            //Create ground hit particle effect
            groundHit.setLocalTranslation(terrainResults.getClosestCollision().getContactPoint());
            groundShootables.getParent().attachChild(groundHit);
            groundHit.emitAllParticles();
            fxTime = 0;
            

        }
        time = 0;
    }
    
    
    
}

[/java]

Code where the rigid body box is created and attached to the groundShootables Node

[java]
private void initBox()
{
Box b = new Box(Vector3f.ZERO, 1, 1, 1);

    Geometry geom = new Geometry("Box", b);

    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setColor("Color", ColorRGBA.Blue);
    geom.setMaterial(mat);
    geom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
    geom.setLocalTranslation(0, 35, 0);
    RigidBodyControl boxPC = (new RigidBodyControl(2f));
    geom.addControl(boxPC);
    bulletAppState.getPhysicsSpace().add(geom);
   
    
    
    groundShootables.attachChild(geom);
    //rootNode.attachChild(geom);
    
}

[/java]

Code where the scene is attached to the groundShootables Node

[java]

private void initScene()
{

    sceneModel = assetManager.loadModel("Scenes/new_mexico_desert.j3o");
    sceneModel.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
    //sceneNode.attachChild(sceneModel);  //Commenting/uncommenting this makes no difference
    
    sceneNode.attachChild(SkyFactory.createSky(assetManager, assetManager.loadTexture("Textures/E.png"), assetManager.loadTexture("Textures/W.png"), assetManager.loadTexture("Textures/N.png"), assetManager.loadTexture("Textures/S.png"), assetManager.loadTexture("Textures/T.png"), assetManager.loadTexture("Textures/B.png")));
    sceneRigidBody = new RigidBodyControl(0.0f);
    sceneModel.addControl(sceneRigidBody);
    bulletAppState.getPhysicsSpace().add(sceneRigidBody);
    
    groundShootables.attachChild(sceneModel);

    rootNode.attachChild(groundShootables);
    rootNode.attachChild(sceneNode);
    

}

Well you pick the groundShootables node but you attached the terrain to the rootNode, everything is working as it is supposed to. Attach the terrain to the groundShootables node or pick the terrain itself.

Hey Normen,

Thank you very much for your reply - However I’m afraid I don’t quite follow it.

You mention “Attach the terrain to the groundShootables node…” - is that not what I have already done with groundShootables.attachChild(sceneModel); ?
[java]

private void initScene()
{

    sceneModel = assetManager.loadModel("Scenes/new_mexico_desert.j3o");
    sceneModel.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);

    sceneNode.attachChild(SkyFactory.createSky(assetManager, assetManager.loadTexture("Textures/E.png"), assetManager.loadTexture("Textures/W.png"), assetManager.loadTexture("Textures/N.png"), assetManager.loadTexture("Textures/S.png"), assetManager.loadTexture("Textures/T.png"), assetManager.loadTexture("Textures/B.png")));
    sceneRigidBody = new RigidBodyControl(0.0f);

    
    groundShootables.attachChild(sceneModel);
    sceneModel.addControl(sceneRigidBody);
    bulletAppState.getPhysicsSpace().add(sceneRigidBody);

    rootNode.attachChild(groundShootables);
    rootNode.attachChild(sceneNode);

}

[/java]

Ah right, I got sceneNode and sceneModel mixed up there. Anyway picking terrain generally works, if you think theres an actual problem make a simple test case so you make sure theres no other issues and we can actually test it.

I should consider renaming them - I’ve confused them before too!! When you say “picking terrain”, what exactly do you mean? Is that just what I’m doing anyway by attaching a Spatial to a Node and then checking collisions on it?

Yes

This is strange - whilst testing it, I notice that if I create a Ray at a near vertical angle (such as almost straight downwards), collision with the ground is detected just fine. But it has to be pretty much vertical to do that. It also only works in some localized areas.

I still haven’t worked out what’s wrong.

Here’s a video of what’s happening… The [red] boxes are created when a collision between the ground and the ray is detected, in the location of the collision. The red lines are a visualization of the ray (see code in original post)

[video]JME3 Ray collision problem - YouTube

Even if I simply pass it sceneModel instead of a node containing multiple Spatials, the problem still occurs.

Having tested it a bit more, it seems as though everything (the Ray and it’s collision point) is drastically offset from the actual location of the ray as visualized by the line. I have been testing to check that the Ray and Line are both starting and ending in the same places - which they are.

If you have a look at this image, the lines which originate from the bottom left of the screen, shot the metal sign where the red cubes are. The cubes certainly don’t line up with the lines.

shoot(float tpf, Vector3f origin, Vector3f direction)

…where does direction come from? Is it a unit vector? Are you sure?

In the PlayerTukTuk class:

[java]
@Override
public void update(float tpf)
{
if(weapon != null)
{
//Make sure the weapon is rotated correctly:
updateWeapon(weaponMount, weapon.getWeapon(), gameInputManager, cam);
//Make sure the weapon is firing only when it’s supposed to:
if(shooting == true)
{
weapon.shoot(tpf, weaponMount.getWorldTranslation(), weapon.getShootTargetLoc());
}
else
{
weapon.stopShooting();
}
}
// … //
}

[/java]

…And in the PlayerWeapon class:

[java]
public Vector3f getShootTargetLoc()
{
if(shootTarget != null)
{
return shootTarget.getWorldTranslation();
}
else
{
return new Vector3f(0,0,0);
}
}
[/java]

These two are unit vectors as far as I’m aware…?

@HopelessHyena said: In the PlayerTukTuk class:

[java]
@Override
public void update(float tpf)
{
if(weapon != null)
{
//Make sure the weapon is rotated correctly:
updateWeapon(weaponMount, weapon.getWeapon(), gameInputManager, cam);
//Make sure the weapon is firing only when it’s supposed to:
if(shooting == true)
{
weapon.shoot(tpf, weaponMount.getWorldTranslation(), weapon.getShootTargetLoc());
}
else
{
weapon.stopShooting();
}
}
// … //
}

[/java]

…And in the PlayerWeapon class:

[java]
public Vector3f getShootTargetLoc()
{
if(shootTarget != null)
{
return shootTarget.getWorldTranslation();
}
else
{
return new Vector3f(0,0,0);
}
}
[/java]

These two are unit vectors as far as I’m aware…?

[java]getWorldTranslation();[/java]
is anything but not a unit vector…it represents the position of the gameObject in the world space.
You need to to something like this to get the direction of the projectile :
[java]target.getWorldTranslation().substract(playerPosOrWeaponPos).normalizeLocal();[/java]

Edit: For info, a unit vector is a vector which has a length of one. Try System.out.println(target.getWorldTranslation().length()); ^^.

You need to to something like this to get the direction of the projectile : [java]target.getWorldTranslation().substract(playerPosOrWeaponPos).normalizeLocal();[/java]

Edit: For info, a unit vector is a vector which has a length of one. Try System.out.println(target.getWorldTranslation().length()); ^^.

Thank you haze! This worked perfectly! Although, I’m not sure why it works and my version didn’t!

Also, now the ray visualization (Line) doesn’t match up. Not that it matters, but are you able to explain why this works/happens? :slight_smile:

Because you weren’t passing a unit vector. Direction vectors need to be unit vectors or it messes up the math… well, because the length of the vector is not 1.

The size of things is very important in math.

Let’s say you were doing a for loop:
for( int i = 0; i < 10; i = i + 1 )…

Now, instead of adding 1, if we add a random value…
for( int i = 0; i < 10; i = i + 27 )…

…then the loop doesn’t behave the same.

Not sure how better to explain that math that expects something to be size 1 will get messed up if that thing is not size 1.

https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:math_for_dummies