Character falling into endless void through terrain

So I’m really new to jME and I was trying to make a character walk on the terrain I made. The thing is I’m following a tutorial on YouTube on how to make this but even after writing the code exactly as the video shows, I’m not getting the same results. Some help would be appreciated. Thank you. I don’t know if I can post the link but here it is: - YouTube

And here’s the code:

public class Main extends SimpleApplication {

public static void main(String[] args) {
    Main app = new Main();
    app.start();
}
private BulletAppState bulletAppState;
private BetterCharacterControl jugador;
private Node jugadorNodo;

@Override
public void simpleInitApp() {

    // Se ajusta la velocidad de la cámara
    flyCam.setMoveSpeed(50f);

    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);

    // Se crea el mapa
    initTerrain();
    initLight();

    // Se crea el jugador/héroe
    initJugador();

}

private void initTerrain() {
    Spatial terreno = assetManager.loadModel("Scenes/primerMapa.j3o");
    RigidBodyControl terrenoControl = new RigidBodyControl(0f);
    terreno.addControl(terrenoControl);
    bulletAppState.getPhysicsSpace().add(terrenoControl);
    rootNode.attachChild(terreno);
}

private void initLight() {
    AmbientLight ambient = new AmbientLight();
    ambient.setColor(ColorRGBA.White);
    rootNode.addLight(ambient);
}

private void initJugador() {
    jugadorNodo = new Node("Jugador");
    jugador = new BetterCharacterControl(0.3f, 1.8f, 70f);
    jugadorNodo.addControl(jugador);
    bulletAppState.getPhysicsSpace().add(jugador);
    rootNode.attachChild(jugadorNodo);
    jugador.warp(new Vector3f(0.0f, 20.0f, 0.0f));
}

@Override
public void simpleUpdate(float tpf) {
    cam.setLocation(jugadorNodo.getLocalTranslation().add(new Vector3f(0.0f, 1.8f, 0.0f)));
}

@Override
public void simpleRender(RenderManager rm) {
    //TODO: add render code
}

I think the problem is here…

[java]
private void initTerrain() {
Spatial terreno = assetManager.loadModel(“Scenes/primerMapa.j3o”);
RigidBodyControl terrenoControl = new RigidBodyControl(0f);
terreno.addControl(terrenoControl);
bulletAppState.getPhysicsSpace().add(terrenoControl);
rootNode.attachChild(terreno);
}[/java]

You create a RigidBodyControl and add the control to the Spatial “terreno”, but I dont think you’ve ever actually defined it as a ‘shape’. Try adding…

[java]CollisionShape sceneShape = CollisionShapeFactory.createMeshShape((Node)terreno);//create a collision shape for the model[/java]
And use this in the constructor for the control. So perhaps like this:

[java]
private void initTerrain() {
Spatial terreno = assetManager.loadModel(“Scenes/primerMapa.j3o”);
CollisionShape sceneShape = CollisionShapeFactory.createMeshShape((Node)terreno);
RigidBodyControl terrenoControl = new RigidBodyControl(sceneShape,0f);
terreno.addControl(terrenoControl);
bulletAppState.getPhysicsSpace().add(terrenoControl);
rootNode.attachChild(terreno);
}[/java]

I’m not 100% on this, it’s from memory of doing it myself, so give it a try and report back :slight_smile:

Thanks for replying, I was losing hope :cry: . I think I actually found a way to “fix” de the problem by creating a “plane shape” like this:

[java]
private void initTerrain() {
Spatial terreno = assetManager.loadModel(“Scenes/primerMapa.j3o”);
PlaneCollisionShape terrenoShape = new PlaneCollisionShape(new Plane(new Vector3f(0, 1f, 0), 1));
RigidBodyControl terrenoControl = new RigidBodyControl(terrenoShape, 30f);
terreno.addControl(terrenoControl);
bulletAppState.getPhysicsSpace().add(terrenoControl);
rootNode.attachChild(terreno);
}
[/java]

It works for the ground but I still don’t know how to make it recognize the hills on my terrain as shapes. Any further advice is welcomed.

Oh I forgot to mention JESTERRRRRR, It still didn’t work adding the shape that way.

Ok, I ran the code, and my addition made no difference, so scrap that.

I used your exact code, but warped the jugador (jugador.warp(new Vector3f(0.0f, 60.0f, 0.0f)):wink: up to 60 instead of 20. I fell until I landed on the terrain and it worked fine (used a model of mine). Can you try moving him higher up and see if he lands properly?

Also, try adding this line

bulletAppState.setDebugEnabled(true);

and see if you get a wireframe outline for your terrain

Yes, the player (“Jugador”) lands properly on the terrain at 60. I tried adding bulletAppState.setDebugEnabled(true); and It showed the wireframe of some other Box shapes I had but it didn’t show the wireframe of the terrain itself

So was he just starting inside the terrain? Might be worth trying the shape thing again with debug, see how that goes. Is that ok or still having problems?

Yup, still going inside the terrain.

@JESTERRRRRR Also, try adding this line

bulletAppState.setDebugEnabled(true);

and see if you get a wireframe outline for your terrain

I don’t think you’ll get a debug wireframe when using jBullet, I think that is only when using the “alpha-version-that-has-bugs-do-not-use-unless-you-can-handle-the-bugs-without-spamming-the-forum”-native bullet.

@DualHunter said: Yup, still going inside the terrain.

The mass of your BetterCharacterControl looks pretty high… try reducing it from 70 to 1 i.e.

jugador = new BetterCharacterControl(0.3f, 1.8f, 1f);

@javagame said: The mass of your BetterCharacterControl looks pretty high... try reducing it from 70 to 1 i.e.

jugador = new BetterCharacterControl(0.3f, 1.8f, 1f);

1 kilo for a 1,80 person? That seems a bit low.

1 Like
@javagame said: The mass of your BetterCharacterControl looks pretty high… try reducing it from 70 to 1 i.e.

Reducing it to 1 doesn’t seem to change anything.

@normen said: 1 kilo for a 1,80 person? That seems a bit low.

Oh, I didn’t realize the mass was based off real life figures!

I tested it with his code and he didn’t fall through

@DualHunter said: Reducing it to 1 doesn't seem to change anything.

Post your entire class as you have it now

@javagame said: I tested it with his code and he didn’t fall through

What part of the code?

Actually I changed it to a regular CharacterControl because of having that problem witth BetterCharacterControl. I added some more stuff.

[java]
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
import com.jme3.bullet.collision.shapes.PlaneCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Plane;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

/**

  • test

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

    public static void main(String[] args) {
    Main app = new Main();
    app.start();
    }
    private BulletAppState bulletAppState;
    private CharacterControl jugador;
    private Node jugadorNodo;
    private Node estructurasNodo;
    private boolean W, A, S, D, ESPACIO;
    private Vector3f walkDirection = new Vector3f(0, 0, 0);

    @Override
    public void simpleInitApp() {

     // Se ajusta la velocidad de la cámara
     flyCam.setMoveSpeed(50f);
    
     // Se une el bulletAppState al stateManager
     bulletAppState = new BulletAppState();
     stateManager.attach(bulletAppState);
    
     // Se crea el mapa
     initTerrain();
     initLight();
    
     // Se crea el jugador/héroe
     initJugador();
    
     // Se crean las estructuras
     initCasa(40f,0f,20f);
     initCasa(-32f,0f,20f);
     initCasa(-100f,0f,20f);
    
     // Se crean los controles
     initInput();
    

    }
    // CollisionShapeFactory myFactory = new CollisionShapeFactory();

    private void initTerrain() {
    Spatial terreno = assetManager.loadModel(“Scenes/primerMapa.j3o”);
    PlaneCollisionShape terrenoShape = new PlaneCollisionShape(new Plane(new Vector3f(0, 1f, 0), 1));
    // CollisionShape terrenoShape = myFactory.createMeshShape((Node)terreno);
    RigidBodyControl terrenoControl = new RigidBodyControl(terrenoShape, 30f);
    // RigidBodyControl terrenoControl = new RigidBodyControl(0f);
    terreno.addControl(terrenoControl);
    bulletAppState.getPhysicsSpace().add(terrenoControl);
    bulletAppState.setDebugEnabled(true);
    rootNode.attachChild(terreno);
    }

    private void initLight() {
    // Luz
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    rootNode.addLight(sun);

     // Sombras
     FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
     viewPort.addProcessor(fpp);
     SSAOFilter ssaoFilter = new SSAOFilter(12.94f, 43.93f, 0.33f, 0.60f);
     fpp.addFilter(ssaoFilter);
    
     // Niebla
    

// FogFilter fog = new FogFilter();
// fog.setFogColor(new ColorRGBA(0.9f, 0.9f, 0.9f, 1.0f));
// fog.setFogDistance(155);
// fog.setFogDensity(1.0f);
// fpp.addFilter(fog);
// viewPort.addProcessor(fpp);
}

private void initCasa(float x, float y, float z) {
    estructurasNodo = new Node();
    Spatial casa = assetManager.loadModel("Models/house 5.j3o");
    casa.setLocalScale(0.05f, 0.05f, 0.05f);
    casa.setLocalTranslation(x, y, z);
    estructurasNodo.attachChild(casa);
    CompoundCollisionShape casaShape = new CompoundCollisionShape();
    PlaneCollisionShape suelo = new PlaneCollisionShape(new Plane(new Vector3f(0, 1f, 0), 1));
    BoxCollisionShape caja = new BoxCollisionShape(new Vector3f(27f, 30f, 30f));
    casaShape.addChildShape(suelo, casa.getLocalTranslation());
    casaShape.addChildShape(caja, new Vector3f(casa.getLocalTranslation().getX()+14,casa.getLocalTranslation().getY()+22,casa.getLocalTranslation().getZ()+9));
    RigidBodyControl casaControl = new RigidBodyControl(casaShape, 100f);
    estructurasNodo.addControl(casaControl);
    bulletAppState.getPhysicsSpace().add(casaControl);
    bulletAppState.getPhysicsSpace().addAll(estructurasNodo);
    rootNode.attachChild(estructurasNodo);
}

private void initJugador() {
    jugadorNodo = new Node();
    CapsuleCollisionShape personajeShape = new CapsuleCollisionShape(0.3f, 1.8f);
    jugador = new CharacterControl(personajeShape, speed);
    jugadorNodo.addControl(jugador);
    bulletAppState.getPhysicsSpace().add(jugador);
    bulletAppState.getPhysicsSpace().addAll(jugadorNodo);
    rootNode.attachChild(jugadorNodo);
    jugador.warp(new Vector3f(0.0f, 60f, 0.0f));
    jugador.setGravity(50f);

// jugador.setGravity(new Vector3f(0f, -80f, 0f));
}

private void initInput() {
    inputManager.addMapping("Adelante", new KeyTrigger(keyInput.KEY_W));
    inputManager.addMapping("Atras", new KeyTrigger(keyInput.KEY_S));
    inputManager.addMapping("Izquierda", new KeyTrigger(keyInput.KEY_A));
    inputManager.addMapping("Derecha", new KeyTrigger(keyInput.KEY_D));
    inputManager.addMapping("ESPACIO", new KeyTrigger(keyInput.KEY_SPACE));

    inputManager.addListener(this, "Adelante");
    inputManager.addListener(this, "Atras");
    inputManager.addListener(this, "Izquierda");
    inputManager.addListener(this, "Derecha");
    inputManager.addListener(this, "ESPACIO");

}

private void initMovimiento() {
    walkDirection.set(0f, 0f, 0f);

    if (W) {
        walkDirection.addLocal(cam.getDirection());
    }
    if (A) {
        walkDirection.addLocal(cam.getLeft());
    }
    if (D) {
        walkDirection.addLocal(cam.getLeft().negate());
    }
    if (S) {
        walkDirection.addLocal(cam.getDirection().negate());
    }
    if (ESPACIO) {
        jugador.jump();
    }
    
    // Evita que pueda volar
    walkDirection.setY(0f);

    jugador.setWalkDirection(walkDirection);
}

public void onAction(String name, boolean isPressed, float tpf) {
    if (name.equals("Adelante")) {
        W = isPressed;
    }
    if (name.equals("Atras")) {
        S = isPressed;
    }
    if (name.equals("Izquierda")) {
        A = isPressed;
    }
    if (name.equals("Derecha")) {
        D = isPressed;
    }
    if (name.equals("ESPACIO")) {
        ESPACIO = isPressed;
    }
}

@Override
public void simpleUpdate(float tpf) {
    // Posiciona la cámara con el movimiento y la posición del nodo del jugador.
    // Multiplica por un vector con coordenadas en Y para establecer la
    // "altura" de la cámara.
    cam.setLocation(jugadorNodo.getLocalTranslation().add(new Vector3f(0.0f, 2.5f, 0.0f)));
    initMovimiento();
}

@Override
public void simpleRender(RenderManager rm) {
    //TODO: add render code
}

}

[/java]

@javagame said: Post your entire class as you have it now

It still goes through the hill parts of the terrain but at least it doesn’t fall into the endless void

Its because you are using a plane, a flat surface, instead of the shape of the terrain…
[java]private void initTerrain() {
Spatial terreno = assetManager.loadModel(“Scenes/primerMapa.j3o”);
PlaneCollisionShape terrenoShape = new PlaneCollisionShape(new Plane(new Vector3f(0, 1f, 0), 1));
// CollisionShape terrenoShape = myFactory.createMeshShape((Node)terreno);
RigidBodyControl terrenoControl = new RigidBodyControl(terrenoShape, 30f);
// RigidBodyControl terrenoControl = new RigidBodyControl(0f);
terreno.addControl(terrenoControl);
bulletAppState.getPhysicsSpace().add(terrenoControl);
bulletAppState.setDebugEnabled(true);
rootNode.attachChild(terreno);
}[/java]

change this function to be

[java]private void initTerrain() {
Spatial terreno = assetManager.loadModel(“Scenes/primerMapa.j3o”);
CollisionShape terrenohape = CollisionShapeFactory.createMeshShape((Node) terreno);
RigidBodyControl terrenoControl = new RigidBodyControl(terrenoShape, 0f);
terreno.addControl(terrenoControl);
bulletAppState.getPhysicsSpace().add(terrenoControl);
bulletAppState.setDebugEnabled(true);
rootNode.attachChild(terreno);
}[/java]

refer to the helloCollision tutorial
And if that works, it should, then you should be able ot go back to BetterCharacterControl if you want

@DualHunter said: It still goes through the hill parts of the terrain but at least it doesn't fall into the endless void

Y si cada casa esta sobre el terreno, no necesitas añadir un “suelo” cada vez.

@javagame said: Y si cada casa esta sobre el terreno, no necesitas añadir un "suelo" cada vez.

Si no lo hago el modelo terminado cayendo al infinito también