Problems with the physicsengine

Hey, I’m quite new on jme3, and I was working through the beginner tutorials. I am at the “HelloCollision” tutorial right now and I have this problem now: Although I have created the physicsengine with the BulletAppState, my controls, e. g my player control, do not fall down. What do I wrong?

Here is my Code:

> package de.tuxchan;
> 
> import com.jme3.animation.AnimChannel;
> import com.jme3.animation.AnimControl;
> import com.jme3.animation.AnimEventListener;
> import com.jme3.app.SimpleApplication;
> import com.jme3.bullet.BulletAppState;
> import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
> import com.jme3.bullet.collision.shapes.CollisionShape;
> import com.jme3.bullet.control.BetterCharacterControl;
> import com.jme3.bullet.control.CharacterControl;
> import com.jme3.bullet.control.RigidBodyControl;
> import com.jme3.bullet.util.CollisionShapeFactory;
> import com.jme3.font.BitmapText;
> import com.jme3.input.KeyInput;
> import com.jme3.input.controls.ActionListener;
> import com.jme3.input.controls.AnalogListener;
> import com.jme3.input.controls.KeyTrigger;
> import com.jme3.light.AmbientLight;
> import com.jme3.math.ColorRGBA;
> import com.jme3.math.Vector3f;
> import com.jme3.scene.Geometry;
> import com.jme3.scene.Node;
> import com.jme3.scene.Spatial;
> 
> public class TerrainTest extends SimpleApplication implements AnimEventListener, ActionListener, AnalogListener {
> 
>     // For physicsengine
>     private BulletAppState bulletAppState;
>     private RigidBodyControl terrainControl;
>     private CollisionShape terrainShape;
>     private Spatial terrain;
>     private CharacterControl player;
>     private CapsuleCollisionShape playerShape;
>     private boolean forward = false, backward = false, left = false, right = false;
>     
>     private Vector3f camDir = new Vector3f();
>     private Vector3f camLeft = new Vector3f();
>     private Vector3f walkDirection = new Vector3f();
>     
>     private BitmapText camLoc;
>     
>     @Override
>     public void simpleInitApp() {
>         bulletAppState = new BulletAppState();
>         stateManager.attach(bulletAppState);
>         
>         bulletAppState.setDebugEnabled(true);
>         
>         flyCam.setMoveSpeed(100);
>                 
>         terrain = assetManager.loadModel("Scenes/Terrain.j3o");
>         terrain.setLocalTranslation(Vector3f.ZERO);
>         terrainShape = CollisionShapeFactory.createMeshShape(terrain);
>         terrainControl = new RigidBodyControl(terrainShape, 0);
>         terrain.addControl(terrainControl);
>         terrain.setLocalTranslation(Vector3f.ZERO);
>         rootNode.attachChild(terrain);
>         
>         playerShape = new CapsuleCollisionShape(1.5f, 6, 1);
>         player = new CharacterControl(playerShape, 0.5f);
>         player.setFallSpeed(20);
>         player.setGravity(new Vector3f(0, -30f, 0));
>         player.setJumpSpeed(20);
>         player.setPhysicsLocation(new Vector3f(0, 30, 0));
>         
>         cam.setLocation(player.getPhysicsLocation().add(0, 5, 0));
>         
>         AmbientLight al = new AmbientLight();
>         al.setColor(ColorRGBA.White);
>         rootNode.addLight(al);
>         
>         bulletAppState.getPhysicsSpace().add(terrain);
>         bulletAppState.getPhysicsSpace().add(player);
>         
>         initKeys();
>         initBitmapText();
>     }
>     
>     public void initBitmapText() {
>         
>         guiNode.detachAllChildren();
>         guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
>         camLoc = new BitmapText(guiFont, false);
>         camLoc.setSize(20);
>         camLoc.setText("(x|y|z) : (" + cam.getLocation().x + "|" + cam.getLocation().y + "|" + cam.getLocation().z + ")");
>         camLoc.setLocalTranslation(settings.getWidth()/2 - camLoc.getLineWidth()/2, settings.getHeight(), 0);
>         guiNode.attachChild(camLoc);
>  
>     }
>     
>     public void initKeys() {
>         inputManager.addMapping("forward", new KeyTrigger(KeyInput.KEY_W));
>         inputManager.addMapping("backward", new KeyTrigger(KeyInput.KEY_S));
>         inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_A));
>         inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_D));
>         inputManager.addMapping("jump", new KeyTrigger(KeyInput.KEY_SPACE));
>         
>         inputManager.addListener(this, "forward", "backward", "left", "right", "jump");
>     }
>     
>     @Override
>     public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
>         // unused
>     }
>     
>     @Override
>     public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
>         
>     }
>     
>     @Override
>     public void onAction(String name, boolean keyPressed, float tpf) {
>         
>     }
>     
>     @Override
>     public void onAnalog(String name, float value, float tpf) {
>         if(name.equals("forward")) {
>             forward = true;
>         }
>         else {
>             forward = false;
>         }
>         
>         if(name.equals("backward")) {
>             backward = true;
>         }
>         else {
>             backward = false;
>         }
>         
>         if(name.equals("left")) {
>             left = true;
>         }
>         else {
>             left = false;
>         }
>         
>         if(name.equals("right")) {
>             right = true;
>         }
>         else {
>             right = false;
>         }
>         
>         if(name.equals("jump")) {
>             player.jump(new Vector3f(0, 20, 0));
>             cam.setLocation(player.getPhysicsLocation().add(0, 5, 0));
>         }
>     }
>     
>     @Override
>     public void simpleUpdate(float tpf) {
>          camLoc.setText("(x|y|z) : (" + cam.getLocation().x + "|" + cam.getLocation().y + "|" + cam.getLocation().z + ")");
>          
>          camDir = cam.getDirection().multLocal(2.3f);
>          camLeft = cam.getDirection().multLocal(2.3f);
>          walkDirection.set(player.getPhysicsLocation());
>          
>          if(forward) {
>              walkDirection = camDir;
>              player.setWalkDirection(walkDirection);
>          }
>          if(backward) {
>              walkDirection = camDir.negate();
>              player.setWalkDirection(walkDirection);
>          }
>          if(left) {
>              walkDirection = camLeft;
>              player.setWalkDirection(walkDirection);
>          }
>          if(right) {
>              walkDirection = camLeft.negate();
>              player.setWalkDirection(walkDirection);
>          }
>          
>          cam.setLocation(player.getPhysicsLocation().add(0, 5, 0));
>     }
>     
>     public static void main (String[] args) {
>         TerrainTest app = new TerrainTest();
>         app.start();
>     }
> }

I think you’re corrupting Vector3f.ZERO. try cloning it. It’s not static. The when you set the localtranslation of your character, the object will be modified, and that’s not good if it’s not meant to change. Cloning it will make a new instance of it (the vector3f object).

I have changed the terrain such as

terrain = assetManager.loadModel(“Scenes/Terrain.j3o”);
terrainShape = CollisionShapeFactory.createMeshShape(terrain);
terrainControl = new RigidBodyControl(terrainShape, 0);
terrain.addControl(terrainControl);
terrainControl.setPhysicsLocation(new Vector3f(0, -30, 0));
rootNode.attachChild(terrain);

You see I removed calling localtranslation and call the setPhysicsLocation of the terrainControl object.

Setting the gravity before adding it to the physics space is pretty meaningless I think. The physics space will force it’s own gravity onto the object when it is added.

So set the gravity on the physics space or set it on the object after it is added, I guess. (At least that’s my recollection from seeing this problem on the forum before.)

I moved my player and my terrain in separate methods.

public void initTerrain() {
terrain = assetManager.loadModel(“Scenes/Terrain.j3o”);
terrainShape = CollisionShapeFactory.createMeshShape(terrain);
terrainControl = new RigidBodyControl(0);
terrain.addControl(terrainControl);
terrainControl.setPhysicsLocation(new Vector3f(0, -30, 0));
rootNode.attachChild(terrain);
bulletAppState.getPhysicsSpace().add(terrain);
}

public void initPlayer() {
    playerShape = new CapsuleCollisionShape(1.5f, 6);
    player = new CharacterControl(playerShape, 0.5f);
    bulletAppState.getPhysicsSpace().add(player);
    player.setFallSpeed(20);
    player.setJumpSpeed(20);
    player.setGravity(new Vector3f(0, -30f, 0));
    player.setPhysicsLocation(new Vector3f(0, 100f, 0));
}

And I set the camera inside the simpleUpdate() method to the player location

@Override
public void simpleUpdate(float tpf) {
cam.setLocation(player.getPhysicsLocation().add(0, 5, 0));
}

If I start the game, my player spawns at point (0, 100, 0) with the camera at the point (0, 105, 0), but it does not fall down, although I have set the physicsSpace to it.

Hi.
The problem is that you didn’t add the CharacterControl to a spatial.

By the way, spatials are located at 0, 0, 0 by default, so you don’t need to set localTranslation for terrain…

… twice. xD

yeah I don’t know how does that came xD. I deleted the calls of terrain.setLocalTranslation and changed it to setPhysicsLocation ^^

I changed the methods initPlayer() and initTerrain as follows:

public void initTerrain() {
terrain = assetManager.loadModel(“Scenes/Terrain.j3o”);
terrainShape = CollisionShapeFactory.createMeshShape((Node)terrain);
terrainControl = new RigidBodyControl(0);
terrain.addControl(terrainControl);
terrainControl.setPhysicsLocation(new Vector3f(50, -100, 0));
rootNode.attachChild(terrain);
bulletAppState.getPhysicsSpace().add(terrain);
}

public void initPlayer() {
    Spatial s = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
    rootNode.attachChild(s);
    playerShape = new CapsuleCollisionShape(1.5f, 6, 1);
    player = new CharacterControl(playerShape, 0.5f);
    player.setSpatial(s);
    player.setFallSpeed(20);
    player.setGravity(new Vector3f(0, -10f, 0));
    player.setJumpSpeed(20);
    player.setPhysicsLocation(new Vector3f(0, 100, 0));
    bulletAppState.getPhysicsSpace().add(player);
}

Moreover I have set some key inputs:

public void initKeys() {
inputManager.addMapping(“forward”, new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping(“backward”, new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping(“left”, new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping(“right”, new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping(“jump”, new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping(“pick”, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));

    inputManager.addListener(this, "forward", "backward", "left", "right", "jump", "pick");
}

@Override
public void onAction(String name, boolean keyPressed, float tpf) {

    if(name.equals("forward")) {
        forward = keyPressed;
    }
    
    if(name.equals("backward")) {
        backward = keyPressed;
    }
    
    if(name.equals("left")) {
        left = keyPressed;
    }
    
    if(name.equals("right")) {
        right = keyPressed;
    }
    
    if(name.equals("pick") && !keyPressed) {
        CollisionResults results = new CollisionResults();
        Ray ray = new Ray(cam.getLocation(), cam.getDirection());
        rootNode.collideWith(ray, results);
        
        CollisionResult result = results.getClosestCollision();
        
        if(result != null) {
            
            if(helloText != null) {
                guiNode.detachChild(helloText);
            }
            guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
            helloText = new BitmapText(guiFont, false);
            helloText.setSize(guiFont.getCharSet().getRenderedSize());
            helloText.setText("Clicked object: " + result);
            helloText.setLocalTranslation(settings.getWidth()/2 - helloText.getLineWidth()/2, settings.getHeight(), 0);
            guiNode.attachChild(helloText);

        }
    }
    
}

@Override
public void simpleUpdate(float tpf) {

    if(forward) {
        player.setWalkDirection(cam.getDirection().mult(2));
    }
    if(backward) {
        player.setWalkDirection(cam.getDirection().negate().mult(2));
    }
    if(left) {
        player.setWalkDirection(cam.getLeft().mult(2));
    }
    if(right) {
        player.setWalkDirection(cam.getLeft().negate().mult(2));
    }
    
    cam.setLocation(player.getPhysicsLocation().add(0, 5, 0));
}

But I still don’t fall down.
And If I use the move keys (WASD) my player don’t stop moving if I release the key.

Yeah, because nothing ever sets walk direction back to zero. Simple programming logic problem it seems.

Edit: and note that you still aren’t adding the control to the spatial.

Can you spot an addControl() anywhere in there? I certainly can’t…

Perhaps the Javadoc for this method will underscore what we have been saying:
https://javadoc.jmonkeyengine.org/com/jme3/scene/control/Control.html#setSpatial-com.jme3.scene.Spatial-

Parameters:
spatial - the spatial to be controlled. This should not be called from user code.

Emphasis added.

I added a spatial from jme3-test-data

public void initPlayer() {
playerSpatial = assetManager.loadModel(“Models/Oto/Oto.mesh.xml”);
rootNode.attachChild(playerSpatial);
playerShape = new CapsuleCollisionShape(1.5f, 6, 1);
player = new CharacterControl(playerShape, 0.5f);
player.setSpatial(playerSpatial);
playerSpatial.addControl(player);
player.setFallSpeed(20);
player.setGravity(new Vector3f(0, -10f, 0));
player.setJumpSpeed(20);
player.setPhysicsLocation(new Vector3f(0, 100, 0));
bulletAppState.getPhysicsSpace().add(player);
}

Did I forget something?

Do not call that. That is not a method that you should be calling so don’t call it because the method shouldn’t be called by user code… as the javadoc says. See the javadoc link above that says you shouldn’t call that from user code because it’s not a method you should be calling.

So don’t call that. Just don’t.

That’s the magic line you just now added. Everything should work now.

The initPlayer()-method looks now:

public void initPlayer() {
playerSpatial = assetManager.loadModel(“Models/Oto/Oto.mesh.xml”);
rootNode.attachChild(playerSpatial);
playerShape = new CapsuleCollisionShape(1.5f, 6, 1);
player = new CharacterControl(playerShape, 0.5f);
playerSpatial.addControl(player);
player.setFallSpeed(20);
player.setGravity(new Vector3f(0, -10f, 0));
player.setJumpSpeed(20);
player.setPhysicsLocation(new Vector3f(0, 100, 0));
bulletAppState.getPhysicsSpace().add(player);
}

I deleted the setSpatial()-method and leave the addControl()-method for playerSpatial. But I still stay above the terrain and don’t fall down. Did I forget something :sweat:

Note the order.

you can use a visual editor to work with physics…

2 Likes

Okay, thank you now it does work!

Sorry if I getting on our nerves, but this problem is finally solved :smiley:

1 Like