[Solved]CollisionListener Not Registering Side Collisions?

Hi all!

I’m new here and I don’t have much JMonkeyEngine experience, so please bear with me if I don’t understand anything.
In my project, I am importing a scene from blender and detecting collision to try to allow the character to wall jump. I am trying to do this by adding a CollisionListener to my class and attaching it to the BulletAppState. In my CollisionListener method, I am checking for collisions to see whether or not the character is on a wall, and if so to allow the character bounce off. The problem arises when I try to detect side collision. In my program, if I jump and walk sideways (or forward) into a wall while in the air, the CollisionListener does not register the collision. I have tried testing for this by disabling the jumping checks and just by printing out a line, but the problem still seems to appear.

Is this normal behavior? If so, how can I make the CollisionListener detect a sideways collision? If not, what am I doing wrong? Any help is much appreciated.

Code is attached below (Sorry I don’t know how to format it beyond 4 spaces and I don’t know how to see a live preview like with StackExchange):

package mygame;

<Insert imports here>

public class Main extends SimpleApplication implements ActionListener, PhysicsCollisionListener {

public static void main(String[] args) {
    Main app = new Main();
    app.start();
}

private Spatial scene;
private BulletAppState bullet;
private CharacterControl player;
private Node characterNode;

private Vector3f walkDirection = new Vector3f();
private Vector3f camDir = new Vector3f();
private Vector3f camLeft = new Vector3f();

private boolean left, right, up, down;

@Override
public void simpleInitApp() {
    assetManager.registerLoader(BlenderLoader.class, "blend");
    
    this.cam.setFrustumPerspective(70, 16f/9f, 0.01f, 100f);
    
    scene = assetManager.loadModel("Scenes/Scene.blend");
    scene.scale(10);
    rootNode.attachChild(scene);
    
    viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
    this.flyCam.setMoveSpeed(10);
    
    bullet = new BulletAppState();
    
    stateManager.attach(bullet);
    
    setupInput();
    
    setupCollision();
    bullet.getPhysicsSpace().addCollisionListener(this);
    
    setupLights();
    
    setupEffects();
}

public void setupLights(){
    /** A white, directional light source */ 
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    rootNode.addLight(sun);
}

public void setupEffects(){
    /* this shadow needs a directional light */
    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
    FXAAFilter fxaa = new FXAAFilter();
    fpp.addFilter(fxaa);
    viewPort.addProcessor(fpp);
}

public float capsuleHeight = 1.5f;

public void setupCollision(){
    CollisionShape sceneCollisionShape = CollisionShapeFactory.createMeshShape(scene);
    RigidBodyControl sceneControl = new RigidBodyControl(sceneCollisionShape, 0);
    scene.addControl(sceneControl);
    
    CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.75f / 2f, capsuleHeight, 1);
    player = new CharacterControl(capsuleShape, 0.05f);
    player.setGravity(new Vector3f(0,-30f,0));
    characterNode = new Node();
    characterNode.addControl(player);
    characterNode.setName("player");
    
    bullet.getPhysicsSpace().add(player);
    bullet.getPhysicsSpace().add(sceneControl);
    
    player.setPhysicsLocation(startingPosition);
}

private Vector3f startingPosition = new Vector3f(0, 10, 0);

public void setupInput(){
    inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addListener(this, "Left");
    inputManager.addListener(this, "Right");
    inputManager.addListener(this, "Up");
    inputManager.addListener(this, "Down");
    inputManager.addListener(this, "Jump");
}

public boolean jump;

@Override
public void simpleUpdate(float tpf) {
    if(player.getPhysicsLocation().y < -10)
        player.setPhysicsLocation(startingPosition);
    
    camDir.set(cam.getDirection()).multLocal(0.6f);
    camLeft.set(cam.getLeft()).multLocal(0.4f);
    walkDirection.set(0, 0, 0);
    camDir.y = 0;
    if (left) {
        walkDirection.addLocal(camLeft);
    }
    if (right) {
        walkDirection.addLocal(camLeft.negate());
    }
    if (up) {
        walkDirection.addLocal(camDir);
    }
    if (down) {
        walkDirection.addLocal(camDir.negate());
    }
    if(jump){
        if (bottom || sides) {
            /*if(sides){
                System.out.println(reflect(walkDirection, collisionNormal));
                player.setLinearVelocity(reflect(walkDirection, collisionNormal).mult(5));
            }*/
            System.out.println("jumping " + counter++ + " " + sides + " " + bottom);
            //player.
            player.jump(new Vector3f(0,10f,0));
        }
    }
    player.setWalkDirection(walkDirection);
    cam.setLocation(player.getPhysicsLocation());
    sides = false;
    bottom = false;
}

int counter;

public Vector3f reflect(Vector3f direction, Vector3f normal){
    //r=d−2(d⋅n)n
    return direction.subtract(normal.mult(2 * direction.dot(normal)));
}

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

@Override
public void onAction(String binding, boolean isPressed, float tpf) {
    if (binding.equals("Left")) {
        left = isPressed;
    } else if (binding.equals("Right")) {
      right = isPressed;
    } else if (binding.equals("Up")) {
      up = isPressed;
    } else if (binding.equals("Down")) {
      down = isPressed;
    } else if (binding.equals("Jump")) {
      jump = isPressed;
    }
}

private boolean bottom, sides;
private Vector3f collisionNormal;
private int counter2;

@Override
public void collision(PhysicsCollisionEvent event) {
    //Do not need to call super or anything like that
    System.out.println("Objects Collide " + counter2++);
    bottom = true;
    
    /*if(event.getNodeA().getName().equals("player")){
        collisionNormal = event.getNormalWorldOnB().negate();
        collisionNormal.y = 0;
        
        if(collisionNormal.angleBetween(Vector3f.UNIT_Y.negate()) > Math.toRadians(45)){
            sides = true;
        }else{
            bottom = true;
        }
    }else if(event.getNodeB().getName().equals("player")){
        collisionNormal = event.getNormalWorldOnB();
        collisionNormal.y = 0;
        
        if(collisionNormal.angleBetween(Vector3f.UNIT_Y.negate()) > Math.toRadians(45)){
            sides = true;
        }else{
            bottom = true;
        }
    }*/
}
}

Have you tried enabling bullet debug mode to see what you believe to be true? Sometimes the collision shape may not be where you expect, or some other erroneous mis-communication.

Hi jayfella,

I enabled the debug mode and am very confused with the result. I attached a screenshot of the project with debug mode enabled. Is this normal? Also, if it helps, I am importing a blender file, though the result appears to be the same with a j3o binary.

Thanks

I can’t really tell to be honest, but inknow that scales and rotations can cause unwanted issues. Make sure you set the scales to 1 and so on in blender.

I guess you need to apply transforms or whatever.

Without knowing how you got these from Blender to JME, it’s hard to say more than that.

Hi pspeed and jayfella,

I decided to modify my code a bit and test it out with the scene from Hello Collision without scaling it. The results are hit or miss, but they do seem better, though the program still doesn’t catch some of the side collisions. Is there another way to create a collision mesh from a blender file other than by importing the scene and creating the mesh that way? Also, on a side note, since the CharacterControl class doesn’t support applying a force, when I tried to set the linear velocity of the character control it set the x and y velocities to 0 and kept the y velocity the same with the jump behavior. Is this normal? Am I running an out of date verison of jMonkeyEngine? I installed it very recently.

Thanks

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.PhysicsTickListener;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
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.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.FXAAFilter;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderLoader;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Cylinder;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.system.AppSettings;

public class Main extends SimpleApplication implements ActionListener, PhysicsCollisionListener {

    public static void main(String[] args) {
        Main app = new Main();
        
        app.setShowSettings(false);
        
        AppSettings settings = new AppSettings(true);
        
        settings.setResolution(1920, 1080);
        app.setSettings(settings);
        
        app.start();
    }
    
    private Spatial scene;
    private BulletAppState bullet;
    private CharacterControl player;
    private Spatial characterNode;
    
    private Vector3f walkDirection = new Vector3f();
    private Vector3f camDir = new Vector3f();
    private Vector3f camLeft = new Vector3f();
    
    private boolean left, right, up, down;
    
    private float moveSpeed = 0.15f;
    
    @Override
    public void simpleInitApp() {
        assetManager.registerLoader(BlenderLoader.class, "blend");
        
        this.cam.setFrustumPerspective(70, 16f/9f, 0.01f, 100f);
        
        scene = assetManager.loadModel("Scenes/Scene.blend");
        
        assetManager.registerLocator("Assets/town.zip", ZipLocator.class);
        scene = assetManager.loadModel("main.scene");
        
        //scene.scale(10);
        rootNode.attachChild(scene);
        
        viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
        this.flyCam.setMoveSpeed(moveSpeed);
        
        bullet = new BulletAppState();
        
        stateManager.attach(bullet);
        
        setupInput();
        
        setupCollision();
        bullet.getPhysicsSpace().addCollisionListener(this);
        bullet.setDebugEnabled(true);
        bullet.setEnabled(true);
                
        setupLights();
        
        setupEffects();
    }
    
    public void setupLights(){
        /** A white, directional light source */ 
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);
    }
    
    public void setupEffects(){
        /* this shadow needs a directional light */
        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        FXAAFilter fxaa = new FXAAFilter();
        fpp.addFilter(fxaa);
        viewPort.addProcessor(fpp);
    }
    
    public float capsuleHeight = 1.5f;
    public RigidBodyControl sceneControl;
    
    public void setupCollision(){
        CollisionShape sceneCollisionShape = CollisionShapeFactory.createMeshShape(scene);
        sceneControl = new RigidBodyControl(sceneCollisionShape, 0);
        scene.addControl(sceneControl);
        scene.setName("scene");
        
        CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.75f / 2f, capsuleHeight, 1);
        player = new CharacterControl(capsuleShape, 0.05f);
        player.setGravity(new Vector3f(0,-30f,0));
        characterNode = new Geometry("player", new Cylinder(1000, 1000, 0.75f / 2f, capsuleHeight));
        characterNode.addControl(player);
        characterNode.setName("player");
        
        bullet.getPhysicsSpace().add(player);
        bullet.getPhysicsSpace().add(sceneControl);
        
        player.setPhysicsLocation(startingPosition);
    }
    
    private Vector3f startingPosition = new Vector3f(0, 10, 0);
    
    public void setupInput(){
        inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addListener(this, "Left");
        inputManager.addListener(this, "Right");
        inputManager.addListener(this, "Up");
        inputManager.addListener(this, "Down");
        inputManager.addListener(this, "Jump");
    }

    public boolean jump;
    public Vector3f velocity = new Vector3f();
    
    @Override
    public void simpleUpdate(float tpf) {
        if(player.getPhysicsLocation().y < -10)
            player.setPhysicsLocation(startingPosition);
        
        camDir.set(cam.getDirection()).multLocal(0.6f);
        camLeft.set(cam.getLeft()).multLocal(0.4f);
        walkDirection.set(0, 0, 0);
        camDir.y = 0;
        if (left) {
            walkDirection.addLocal(camLeft);
        }
        if (right) {
            walkDirection.addLocal(camLeft.negate());
        }
        if (up) {
            walkDirection.addLocal(camDir);
        }
        if (down) {
            walkDirection.addLocal(camDir.negate());
        }
        
        if(jump){
            if (bottom || sides) {
                if(sides && !bottom){
                    //System.out.println(reflect(walkDirection, collisionNormal));
                    collisionNormal.normalize();
                    //player.setPhysicsLocation(player.getPhysicsLocation().add(collisionNormal.negate()));
                    velocity = velocity.add(collisionNormal.negate().mult(3));
                    
                }
                //System.out.println("jumping " + counter++ + " " + sides + " " + bottom);
                //player.
                player.jump(new Vector3f(0,10f,0));
            }
            //player.setLinearVelocity(new Vector3f((float)1E10, 0, 0));
        }
        velocity = velocity.mult(0.9f);
        walkDirection = walkDirection.normalize().add(velocity).mult(moveSpeed);
        player.setWalkDirection(walkDirection);
        cam.setLocation(player.getPhysicsLocation());
        sides = false;
        bottom = false;
    }
    
    int counter;
    
    public Vector3f reflect(Vector3f direction, Vector3f normal){
        //r=d−2(d⋅n)n
        return direction.subtract(normal.mult(2 * direction.dot(normal)));
    }

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

    @Override
    public void onAction(String binding, boolean isPressed, float tpf) {
        if (binding.equals("Left")) {
            left = isPressed;
        } else if (binding.equals("Right")) {
          right = isPressed;
        } else if (binding.equals("Up")) {
          up = isPressed;
        } else if (binding.equals("Down")) {
          down = isPressed;
        } else if (binding.equals("Jump")) {
          jump = isPressed;
        }
    }
    
    private boolean bottom, sides;
    private Vector3f collisionNormal;
    private int counter2;
    
    @Override
    public void collision(PhysicsCollisionEvent event) {
        //Do not need to call super or anything like that
        System.out.println("Objects Collide " + counter2++);
        bottom = true;
        if(event.getNodeA().getName().equals("player")){
            processPlayerCollision(event.getNodeA(), event.getNormalWorldOnB().negate());
        }else if(event.getNodeB().getName().equals("player")){
            processPlayerCollision(event.getNodeB(), event.getNormalWorldOnB());
        }
    }
    
    public void processPlayerCollision(Spatial player, Vector3f normal){
            collisionNormal = normal.normalize();
            
            //System.out.println(collisionNormal);
            
            if(collisionNormal.angleBetween(Vector3f.UNIT_Y.negate()) > Math.toRadians(45)){
                sides = true;
            }else{
                bottom = true;
            }
    }
}

Hi pspeed and jayfella,

I decided to modify my code a bit and test it out with the scene from Hello Collision without scaling it. The results are hit or miss, but they do seem better, though the program still doesn’t catch some of the side collisions. Is there another way to create a collision mesh from a blender file other than by importing the scene and creating the mesh that way? Also, on a side note, since the CharacterControl class doesn’t support applying a force, when I tried to set the linear velocity of the character control it set the x and y velocities to 0 and kept the y velocity the same with the jump behavior. Is this normal? Am I running an out of date verison of jMonkeyEngine? I installed it very recently.

Thanks

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.PhysicsTickListener;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
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.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.FXAAFilter;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderLoader;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Cylinder;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.system.AppSettings;

public class Main extends SimpleApplication implements ActionListener, PhysicsCollisionListener {

    public static void main(String[] args) {
        Main app = new Main();
        
        app.setShowSettings(false);
        
        AppSettings settings = new AppSettings(true);
        
        settings.setResolution(1920, 1080);
        app.setSettings(settings);
        
        app.start();
    }
    
    private Spatial scene;
    private BulletAppState bullet;
    private CharacterControl player;
    private Spatial characterNode;
    
    private Vector3f walkDirection = new Vector3f();
    private Vector3f camDir = new Vector3f();
    private Vector3f camLeft = new Vector3f();
    
    private boolean left, right, up, down;
    
    private float moveSpeed = 0.15f;
    
    @Override
    public void simpleInitApp() {
        assetManager.registerLoader(BlenderLoader.class, "blend");
        
        this.cam.setFrustumPerspective(70, 16f/9f, 0.01f, 100f);
        
        scene = assetManager.loadModel("Scenes/Scene.blend");
        
        assetManager.registerLocator("Assets/town.zip", ZipLocator.class);
        scene = assetManager.loadModel("main.scene");
        
        //scene.scale(10);
        rootNode.attachChild(scene);
        
        viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
        this.flyCam.setMoveSpeed(moveSpeed);
        
        bullet = new BulletAppState();
        
        stateManager.attach(bullet);
        
        setupInput();
        
        setupCollision();
        bullet.getPhysicsSpace().addCollisionListener(this);
        bullet.setDebugEnabled(true);
        bullet.setEnabled(true);
                
        setupLights();
        
        setupEffects();
    }
    
    public void setupLights(){
        /** A white, directional light source */ 
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);
    }
    
    public void setupEffects(){
        /* this shadow needs a directional light */
        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        FXAAFilter fxaa = new FXAAFilter();
        fpp.addFilter(fxaa);
        viewPort.addProcessor(fpp);
    }
    
    public float capsuleHeight = 1.5f;
    public RigidBodyControl sceneControl;
    
    public void setupCollision(){
        CollisionShape sceneCollisionShape = CollisionShapeFactory.createMeshShape(scene);
        sceneControl = new RigidBodyControl(sceneCollisionShape, 0);
        scene.addControl(sceneControl);
        scene.setName("scene");
        
        CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.75f / 2f, capsuleHeight, 1);
        player = new CharacterControl(capsuleShape, 0.05f);
        player.setGravity(new Vector3f(0,-30f,0));
        characterNode = new Geometry("player", new Cylinder(1000, 1000, 0.75f / 2f, capsuleHeight));
        characterNode.addControl(player);
        characterNode.setName("player");
        
        bullet.getPhysicsSpace().add(player);
        bullet.getPhysicsSpace().add(sceneControl);
        
        player.setPhysicsLocation(startingPosition);
    }
    
    private Vector3f startingPosition = new Vector3f(0, 10, 0);
    
    public void setupInput(){
        inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addListener(this, "Left");
        inputManager.addListener(this, "Right");
        inputManager.addListener(this, "Up");
        inputManager.addListener(this, "Down");
        inputManager.addListener(this, "Jump");
    }

    public boolean jump;
    public Vector3f velocity = new Vector3f();
    
    @Override
    public void simpleUpdate(float tpf) {
        if(player.getPhysicsLocation().y < -10)
            player.setPhysicsLocation(startingPosition);
        
        camDir.set(cam.getDirection()).multLocal(0.6f);
        camLeft.set(cam.getLeft()).multLocal(0.4f);
        walkDirection.set(0, 0, 0);
        camDir.y = 0;
        if (left) {
            walkDirection.addLocal(camLeft);
        }
        if (right) {
            walkDirection.addLocal(camLeft.negate());
        }
        if (up) {
            walkDirection.addLocal(camDir);
        }
        if (down) {
            walkDirection.addLocal(camDir.negate());
        }
        
        if(jump){
            if (bottom || sides) {
                if(sides && !bottom){
                    //System.out.println(reflect(walkDirection, collisionNormal));
                    collisionNormal.normalize();
                    //player.setPhysicsLocation(player.getPhysicsLocation().add(collisionNormal.negate()));
                    velocity = velocity.add(collisionNormal.negate().mult(3));
                    
                }
                //System.out.println("jumping " + counter++ + " " + sides + " " + bottom);
                //player.
                player.jump(new Vector3f(0,10f,0));
            }
            //player.setLinearVelocity(new Vector3f((float)1E10, 0, 0));
        }
        velocity = velocity.mult(0.9f);
        walkDirection = walkDirection.normalize().add(velocity).mult(moveSpeed);
        player.setWalkDirection(walkDirection);
        cam.setLocation(player.getPhysicsLocation());
        sides = false;
        bottom = false;
    }
    
    int counter;
    
    public Vector3f reflect(Vector3f direction, Vector3f normal){
        //r=d−2(d⋅n)n
        return direction.subtract(normal.mult(2 * direction.dot(normal)));
    }

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

    @Override
    public void onAction(String binding, boolean isPressed, float tpf) {
        if (binding.equals("Left")) {
            left = isPressed;
        } else if (binding.equals("Right")) {
          right = isPressed;
        } else if (binding.equals("Up")) {
          up = isPressed;
        } else if (binding.equals("Down")) {
          down = isPressed;
        } else if (binding.equals("Jump")) {
          jump = isPressed;
        }
    }
    
    private boolean bottom, sides;
    private Vector3f collisionNormal;
    private int counter2;
    
    @Override
    public void collision(PhysicsCollisionEvent event) {
        //Do not need to call super or anything like that
        System.out.println("Objects Collide " + counter2++);
        bottom = true;
        if(event.getNodeA().getName().equals("player")){
            processPlayerCollision(event.getNodeA(), event.getNormalWorldOnB().negate());
        }else if(event.getNodeB().getName().equals("player")){
            processPlayerCollision(event.getNodeB(), event.getNormalWorldOnB());
        }
    }
    
    public void processPlayerCollision(Spatial player, Vector3f normal){
            collisionNormal = normal.normalize();
            
            //System.out.println(collisionNormal);
            
            if(collisionNormal.angleBetween(Vector3f.UNIT_Y.negate()) > Math.toRadians(45)){
                sides = true;
            }else{
                bottom = true;
            }
    }
}

Have you tried this?

https://javadoc.jmonkeyengine.org/index.html?com/jme3/bullet/control/BetterCharacterControl.html
https://wiki.jmonkeyengine.org/jme3/advanced/walking_character.html#bettercharactercontrol

Hi mitm,

Thanks for the resources! I tried implementing the BetterCharacterControl class but I had to extend it to find the physics space location to use for the camera. Is that how it works? Also, thd BetterCharacterControl class’s onGround is always false for some reason. Do you have any insights as to why this is happening?

Thanks

BetterCharacterControl (extension):
package mygame;

import com.jme3.math.Vector3f;

public class BetterCharacterControl extends com.jme3.bullet.control.BetterCharacterControl {

    public BetterCharacterControl(float f, float f2, float f3){
        super(f, f2, f3);
    }

    public Vector3f getPhysicsLocation(){
        return rigidBody.getPhysicsLocation();
    }

    @Override
    public void jump() {
        onGround = true;
        System.out.println(onGround);
        super.jump();
    }

}

Main:
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.PhysicsTickListener;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.FXAAFilter;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.blender.BlenderLoader;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Cylinder;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.system.AppSettings;

public class Main extends SimpleApplication implements ActionListener, PhysicsCollisionListener {

    public static void main(String[] args) {
        Main app = new Main();

        app.setShowSettings(false);

        AppSettings settings = new AppSettings(true);

        settings.setResolution(1920, 1080);
        app.setSettings(settings);

        app.start();
    }

    private Spatial scene;
    private BulletAppState bullet;
    private BetterCharacterControl player;
    private ChaseCamera chaseCam;
    private Spatial characterNode;

    private Vector3f walkDirection = new Vector3f();
    private Vector3f camDir = new Vector3f();
    private Vector3f camLeft = new Vector3f();

    private boolean left, right, up, down;

    private float moveSpeed = 0.15f;

    @Override
    public void simpleInitApp() {
        assetManager.registerLoader(BlenderLoader.class, "blend");

        this.cam.setFrustumPerspective(70, 16f/9f, 0.01f, 100f);

        scene = assetManager.loadModel("Scenes/Scene.blend");

        assetManager.registerLocator("Assets/town.zip", ZipLocator.class);
        scene = assetManager.loadModel("main.scene");

        //scene.scale(10);
        rootNode.attachChild(scene);

        viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
        this.flyCam.setMoveSpeed(moveSpeed);

        bullet = new BulletAppState();

        stateManager.attach(bullet);

        setupInput();

        setupCollision();
        bullet.getPhysicsSpace().addCollisionListener(this);
        bullet.setDebugEnabled(true);
        bullet.setEnabled(true);

        setupLights();

        setupEffects();
    }

    public void setupLights(){
        /** A white, directional light source */ 
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
        sun.setColor(ColorRGBA.White);
        rootNode.addLight(sun);
    }

    public void setupEffects(){
        /* this shadow needs a directional light */
        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        FXAAFilter fxaa = new FXAAFilter();
        fpp.addFilter(fxaa);
        viewPort.addProcessor(fpp);
    }

    public float capsuleHeight = 1.5f;
    public RigidBodyControl sceneControl;

    public void setupCollision(){
        CollisionShape sceneCollisionShape = CollisionShapeFactory.createMeshShape(scene);
        sceneControl = new RigidBodyControl(sceneCollisionShape, 0);
        scene.addControl(sceneControl);
        scene.setName("scene");

        player = new BetterCharacterControl(0.75f / 2f, capsuleHeight, 1);
        player.setGravity(new Vector3f(0,-10f,0));
        player.setJumpForce(new Vector3f(0,5f,0));
        characterNode = new Geometry("player", new Cylinder(1000, 1000, 0.75f / 2f, capsuleHeight));
        characterNode.addControl(player);
        characterNode.setName("player");

        //flyCam.setEnabled(false);
        /*chaseCam = new ChaseCamera(cam, characterNode, inputManager);
        chaseCam.setMinDistance(0.1f);
        chaseCam.setMaxDistance(0.1f);
        chaseCam.setInvertVerticalAxis(true);
        chaseCam.setDragToRotate(false); */

        bullet.getPhysicsSpace().add(player);
        bullet.getPhysicsSpace().addAll(characterNode);
        bullet.getPhysicsSpace().add(sceneControl);

        cam.setLocation(startingPosition);
        player.warp(startingPosition);
    }

    private Vector3f startingPosition = new Vector3f(0, 10, 0);

    public void setupInput(){
        inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addListener(this, "Left");
        inputManager.addListener(this, "Right");
        inputManager.addListener(this, "Up");
        inputManager.addListener(this, "Down");
        inputManager.addListener(this, "Jump");
    }

    public boolean jump, jumpedAlready;
    public Vector3f velocity = new Vector3f();

    @Override
    public void simpleUpdate(float tpf) {
        /*if(player.().y < -10)
            player.setPhysicsLocation(startingPosition);*/

        camDir.set(cam.getDirection()).multLocal(0.6f);
        camLeft.set(cam.getLeft()).multLocal(0.4f);
        walkDirection.set(0, 0, 0);
        camDir.y = 0;
        camLeft.y = 0;
        if (left) {
            walkDirection.addLocal(camLeft);
        }
        if (right) {
            walkDirection.addLocal(camLeft.negate());
        }
        if (up) {
            walkDirection.addLocal(camDir);
        }
        if (down) {
            walkDirection.addLocal(camDir.negate());
        }

        if(jump){
            if (bottom || sides && !jumpedAlready) {
                jumpedAlready = true;
                if(sides && !bottom){
                    //System.out.println(reflect(walkDirection, collisionNormal));
                    collisionNormal.normalize();
                    //player.setPhysicsLocation(player.getPhysicsLocation().add(collisionNormal.negate()));
                    velocity = velocity.add(collisionNormal.negate().mult(3));

                }
                //System.out.println("jumping " + counter++ + " " + sides + " " + bottom);
                //player.
                //System.out.println("Jump");
                player.jump();
            }else{
                jumpedAlready = false;
            }
            //player.setLinearVelocity(new Vector3f((float)1E10, 0, 0));
        }
        velocity.y = 0;
        velocity = velocity.mult(0.9f);
        walkDirection = walkDirection.normalize()/*.add(velocity)*/.mult(moveSpeed * 100);
        player.setViewDirection(camDir);
        player.setWalkDirection(walkDirection);
        cam.setLocation(player.getPhysicsLocation().add(0, capsuleHeight * 4f/5f, 0));
        //cam.setLocation(characterNode.getWorldTranslation());
        sides = false;
        bottom = false;
    }

    int counter;

    public Vector3f reflect(Vector3f direction, Vector3f normal){
        //r=d−2(d⋅n)n
        return direction.subtract(normal.mult(2 * direction.dot(normal)));
    }

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

    @Override
    public void onAction(String binding, boolean isPressed, float tpf) {
        if (binding.equals("Left")) {
            left = isPressed;
        } else if (binding.equals("Right")) {
          right = isPressed;
        } else if (binding.equals("Up")) {
          up = isPressed;
        } else if (binding.equals("Down")) {
          down = isPressed;
        } else if (binding.equals("Jump")) {
          jump = isPressed;
        }
    }

    private boolean bottom, sides;
    private Vector3f collisionNormal;
    private int counter2;

    @Override
    public void collision(PhysicsCollisionEvent event) {
        //Do not need to call super or anything like that
        System.out.println("Objects Collide " + counter2++);
        if(event.getNodeA().getName().equals("player")){
            processPlayerCollision(event.getNodeA(), event.getNormalWorldOnB().negate());
        }else if(event.getNodeB().getName().equals("player")){
            processPlayerCollision(event.getNodeB(), event.getNormalWorldOnB());
        }
    }

    public void processPlayerCollision(Spatial player, Vector3f normal){
            collisionNormal = normal.normalize();

            //System.out.println(collisionNormal);

            if(collisionNormal.angleBetween(Vector3f.UNIT_Y.negate()) > Math.toRadians(45)){
                sides = true;
            }else{
                bottom = true;
            }
    }
}

I have never seen someone extend a class of itself so I am not sure what to say.

Heres how I extend BetterCharacterControl.

Pay attention to the cloning part. Without it the control will throw an exception.

To use jump just add this to the onAction() method for the control,

        if (name.equals("Jump")) {
            jump();
        }

To use the control just call it like so,

Then you can do things like this,

       Collection<PhysicsRigidBody> rigidBodyList = charNode.getControl(PCControl.class).getPhysicsSpace().getRigidBodyList();
        for(PhysicsRigidBody prb: rigidBodyList) {
            if(prb.equals(charNode)) {
                Vector3f physicsLocation = prb.getPhysicsLocation();
            }
        }

Im sure there are better ways to do this though like creating a reference to the controls physicsSpace to simplify things.

Im not exactly sure why the original problem is happening and the others are much stronger in the code for jme than I am so feel free to correct anything I have written if there is a better way.

Hi mitm,

Thanks for the help! I took your advice and used it to extend the BetterCharacterControl class. Using BetterCharacterControl ended up fixing the collision issues and I just used my own ground and wall collision method which is posted above. Is there a way to close this thread and mark it as solved?

Thanks

Threads don’t get closed so others can benefit from them. Standard procedure is to select the edit button(looks like a pencil) for your topic title and add [SOLVED] before the title.

[SOLVED]CollisionListener Not Registering Side Collisions?

Thanks! The title is changed now!