BetterCharacterControl jumping on plain surface

hello. Im struggling with BetterCharacterControl jumping for no reason on plain surface (made of tiles 3x3 meters wide in jmonkey, spaced 3 meters from each other so they make a board). here is the code player + camera code:

public class StartGameState extends AbstractAppState{

private final Node rootNode;
private final Node localRootNode = new Node("GameStart");
private final AssetManager assetManager;
private final InputManager inputManager;
Node playerNode;
Node playerLookAtNode;
CameraNode camNode;
public BulletAppState bulletAppState;
private final FlyByCamera flyCam;
private final Camera cam;
private BetterCharacterControl playerCharacterControl;
boolean forward = false;
boolean backward = false;
boolean left = false;
boolean right = false;
private ChaseCamera chaseCam;
private Vector3f playerWalkDirection;




public StartGameState(SimpleApplication app){
    rootNode = app.getRootNode();
    assetManager = app.getAssetManager();
    inputManager = app.getInputManager();
    flyCam = app.getFlyByCamera();
    cam = app.getCamera();
    
}

@Override
public void initialize(AppStateManager stateManager, Application app){
    super.initialize(stateManager, app);
    
    rootNode.attachChild(localRootNode);
    
    bulletAppState = new BulletAppState();
    bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
    bulletAppState.setDebugEnabled(true);
    stateManager.attach(bulletAppState);
    initPlayer();
    initKeys();
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection(new Vector3f(-0.1f, -0.7f, -1.0f));
    rootNode.addLight(sun);


}
public void initKeys(){

inputManager.addMapping("RotateCameraRight", new MouseAxisTrigger(MouseInput.AXIS_X,true));
inputManager.addMapping("RotateCameraLeft", new MouseAxisTrigger(MouseInput.AXIS_X,false));
inputManager.addMapping("RotateCameraUp", new MouseAxisTrigger(MouseInput.AXIS_Y,true));
inputManager.addMapping("RotateCameraDown", new MouseAxisTrigger(MouseInput.AXIS_Y,false));
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("test", new KeyTrigger(KeyInput.KEY_K));




inputManager.addListener(actionListener, "Forward");
inputManager.addListener(actionListener, "Backward");
inputManager.addListener(actionListener, "Left");
inputManager.addListener(actionListener, "Right");
inputManager.addListener(actionListener, "test");



}

public void initPlayer(){
playerCharacterControl = new BetterCharacterControl(0.35f,1.8f,1f);

flyCam.setEnabled(false);
playerNode = new Node();

playerCharacterControl.setJumpForce(new Vector3f(0,2.5f,0));
getPhysicsSpace().add(playerCharacterControl);
getPhysicsSpace().addAll(playerNode);
playerCharacterControl.setGravity(new Vector3f(0,-5,0));
playerNode.addControl(playerCharacterControl);
playerCharacterControl.warp(new Vector3f(0,0,0));

playerLookAtNode = new Node();
playerNode.attachChild(playerLookAtNode);
playerLookAtNode.move(0,1.5f,0);
rootNode.attachChild(playerNode);


flyCam.setEnabled(true);
camNode = new CameraNode("Camera Node", cam);
camNode.setControlDir(ControlDirection.SpatialToCamera);
playerNode.attachChild(camNode);
camNode.setLocalTranslation(new Vector3f(0, 1.5f, 0));
camNode.lookAt(playerLookAtNode.getLocalTranslation(), Vector3f.UNIT_Y);
inputManager.setCursorVisible(false);

}

private final ActionListener actionListener = new ActionListener() {
    @Override
    public void onAction(String name, boolean keyPressed, float tpf) {
      if(name.equals("test")){
      System.out.print(inputManager.getCursorPosition().x);
      camNode.setEnabled(false);
      }
      
      if(name.equals("Forward")){
      if(keyPressed){
          forward = true;
                }else {
          forward = false;
        }
      }
      
      if(name.equals("Backward")){
      if(keyPressed){
          backward = true;
      }else {
          backward = false;
        }
      }
      
         if(name.equals("Left")){
      if(keyPressed){
          left = true;
      }else {
          left = false;
        }
      }
         
      if(name.equals("Right")){
      if(keyPressed){
          right = true;
      }else {
          right = false;
        }
      }
      
      
    }
};

private final AnalogListener analogListener = new AnalogListener() {
    @Override
    public void onAnalog(String name, float value, float tpf) {
    }
};

@Override
public void cleanup(){
rootNode.detachChild(localRootNode);

super.cleanup();
}
@Override
public void update(float tpf) {
    playerCharacterControl.setWalkDirection(new Vector3f(0,0,0));
    playerWalkDirection = cam.getDirection();
    playerWalkDirection.setY(0);
    playerCharacterControl.setViewDirection(new Vector3f(playerWalkDirection));
    if(forward){
    playerCharacterControl.setWalkDirection(playerWalkDirection.mult(10));
    } else if(backward){
    playerCharacterControl.setWalkDirection(playerWalkDirection.mult(2).negate());
    }

    System.out.println(playerNode.getWorldTranslation());}}

and here is the terrain generation code:

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.bullet.BulletAppState;
import static com.jme3.bullet.PhysicsSpace.getPhysicsSpace;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.input.ChaseCamera;
import com.jme3.input.FlyByCamera;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.texture.Texture;
import java.util.ArrayList;

public class TerrainGenerationState extends AbstractAppState{
private final Node rootNode;
private final Node terrainRootNode = new Node("TerrainRootNode");
private  ArrayList<String> tileModels = new ArrayList<String>();
private final AssetManager assetManager;
private BulletAppState bulletAppState;
private Spatial tileSpatial;
private RigidBodyControl tileRigidBody;
private Vector3f tileOffsetVector;
private final AppStateManager stateManager;

public TerrainGenerationState(SimpleApplication StartGameState){
    rootNode = StartGameState.getRootNode();
    assetManager = StartGameState.getAssetManager();
    stateManager = StartGameState.getStateManager();
}

@Override
public void initialize(AppStateManager stateManager, Application app){
    super.initialize(stateManager, app);
    initTileModels();
    initMap();

}


public void initTileModels(){
    
tileModels.add("Models/Floor1/Floor1.j3o");
}
public void initMap(){   
    
    for(int i=-5;i<=4;i++){
    int k = 0;  //arraylist index (the one containing model paths for terrain)
    int r = -15; //row vector
    tileOffsetVector = new Vector3f(3*i,-2,r);
    tileSpatial = assetManager.loadModel(tileModels.get(k));
    tileSpatial.move(tileOffsetVector);         
    tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
    rootNode.attachChild(tileSpatial);
    
    tileOffsetVector = new Vector3f(3*i,-2,r+3);
   tileSpatial = assetManager.loadModel(tileModels.get(k));
   tileSpatial.move(tileOffsetVector);
     tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
   rootNode.attachChild(tileSpatial);

    tileOffsetVector = new Vector3f(3*i,-2,r+6);
   tileSpatial = assetManager.loadModel(tileModels.get(k));
    tileSpatial.move(tileOffsetVector);
      tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
    rootNode.attachChild(tileSpatial);

    tileOffsetVector = new Vector3f(3*i,-2,r+9);
    tileSpatial = assetManager.loadModel(tileModels.get(k));
    tileSpatial.move(tileOffsetVector);
      tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
    rootNode.attachChild(tileSpatial);

    tileOffsetVector = new Vector3f(3*i,-2,r+12);
    tileSpatial = assetManager.loadModel(tileModels.get(k));
    tileSpatial.move(tileOffsetVector);
      tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
    rootNode.attachChild(tileSpatial);

    tileOffsetVector = new Vector3f(3*i,-2,r+15);
    tileSpatial = assetManager.loadModel(tileModels.get(k));
    tileSpatial.move(tileOffsetVector);
      tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
    rootNode.attachChild(tileSpatial);

    tileOffsetVector = new Vector3f(3*i,-2,r+18);
    tileSpatial = assetManager.loadModel(tileModels.get(k));
    tileSpatial.move(tileOffsetVector);
      tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
    rootNode.attachChild(tileSpatial);

    tileOffsetVector = new Vector3f(3*i,-2,r+21);
    tileSpatial = assetManager.loadModel(tileModels.get(k));
    tileSpatial.move(tileOffsetVector);
      tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
    rootNode.attachChild(tileSpatial);

    tileOffsetVector = new Vector3f(3*i,-2,r+24);
    tileSpatial = assetManager.loadModel(tileModels.get(k));
    tileSpatial.move(tileOffsetVector);
      tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
    rootNode.attachChild(tileSpatial);

    tileOffsetVector = new Vector3f(3*i,-2,r+27);
    tileSpatial = assetManager.loadModel(tileModels.get(k));
    tileSpatial.move(tileOffsetVector);
      tileRigidBody = new RigidBodyControl(0);
    tileSpatial.addControl(tileRigidBody);
    getPhysicsSpace().add(tileSpatial);
    rootNode.attachChild(tileSpatial);
    
    }
}

@Override
public void cleanup(){
rootNode.detachChild(terrainRootNode);

super.cleanup();
}
@Override
public void update(float tpf) {}}     

i am fairly sure i am missing something here but i am not sure what it might be, as the tiles are perfectly plain (they are 2d planes 3x3 meters in blender which is exactly 3x3 in jmonkey as far as i know), they are spaced 3 meters between each other (so they fit perfectly) and playerWalkDirection vector Y variable is always set to 0 before moving, so there is literally nothing to bounce off. Here is the playerNode (which betterCharacterControl has control over) translation, to show that this is precisely a matter of physics body bouncing:
coords
the middle one is of course Y value.

Hi, a list of things to do/check:

  • Try with one tile and see if collision still doesn’t work.
  • Reduce your code, because there is a lot of copy paste.
  • Specify what physics libraries and versions you use.
  • Check if the physicsspace instance is the same as the the created one with the bulletAppState.
  • try: remove the moving the spatial directly, move it using the physicsengine ‘setPhysicsLocation’ after you have added it to the physics space.
  • Include video of what is happening as I don’t know what you mean specifically with bouncing
1 Like

This may be related (a very old topic of mine)
BetterCharacterControl acts like trampoline? - Troubleshooting / physics - jMonkeyEngine Hub

Basically, if using BCC, the mesh vertices cannot be larger than the collision box of the BCC collision shape.

Thank you for answering!
I checked if this happens when the player moves on one tile, and the thing that seems to cause the jumping are the “edges” ( the places when the triangles connect in the model). What can i do against this?

also thank you very much for advice


this is how big are the tiles compared to the betterCharacterControl, and the jumping occurs when going through the places where the triangles connect (these blue lines)