Character is sinking into the floor!

Hello,



I am trying to let a character walk on a box using the physics space.


  1. I dont really know how to adjust the capsulecollisionshape to the model. I am using the ninja model and its feet are in the center of the capsule I created. Do I need a model with 0,0,0 in its center or is there a way to adjust the collisionshape?


  2. The CapsuleCollisionShape falls onto the box and then slowly starts to sink into it, if I dont move it. I guess I set something up in a wrong way, cause the collision only seems to work for the sides of the triangles. At least it looks like that when I enable the debug mode. I just cant figure out whats wrong.



    [java]

    package test;



    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.CharacterControl;

    import com.jme3.bullet.control.RigidBodyControl;

    import com.jme3.bullet.util.CollisionShapeFactory;

    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.DirectionalLight;

    import com.jme3.material.Material;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.Spatial;

    import com.jme3.scene.shape.Box;

    import com.jme3.texture.Texture;



    public class Test extends SimpleApplication implements ActionListener {



    public static void main(String[] args) {

    new Test().start();

    }



    private Spatial floor;

    private Spatial charNode;

    private ChaseCamera camera;

    private BulletAppState bulletAppState;

    private RigidBodyControl landscape;

    private boolean left, right, up, down;

    private Vector3f walkDirection = new Vector3f();

    private CharacterControl character;

    private float speed = 0.2f;



    @Override

    public void simpleInitApp() {

    bulletAppState = new BulletAppState();

    stateManager.attach(bulletAppState);



    this.getStateManager().getState(BulletAppState.class).getPhysicsSpace()

    .enableDebug(this.getAssetManager());



    loadScene();

    loadCharacter();

    setUpLight();

    setUpKeys();



    // CAMERA

    flyCam.setEnabled(false);

    camera = new ChaseCamera(cam, charNode, inputManager);

    camera.setDefaultDistance(40);

    camera.setDefaultHorizontalRotation(Data.DEG90);

    camera.setDefaultVerticalRotation(Data.DEG90 - Data.DEG90 / 5);



    viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));

    }



    private void loadCharacter() {

    // initialze physical character behaviour, including collision shape

    CapsuleCollisionShape capsule = new CapsuleCollisionShape(1f, 2f);

    character = new CharacterControl(capsule, 0.01f);



    // load the visible character model and add the physical behaviour to it

    charNode = assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");

    charNode.setLocalScale(0.03f);

    charNode.addControl(character);



    character.setPhysicsLocation(new Vector3f(1, 20, 1));



    // Make character visible and physical

    rootNode.attachChild(charNode); // make it visible

    bulletAppState.getPhysicsSpace().add(character); // make it physical



    }



    private void loadScene() {

    Box quad = new Box(50f, 1f, 50f);

    floor = new Geometry("quad", quad);



    Material mat1 = new Material(assetManager,

    "Common/MatDefs/Misc/SimpleTextured.j3md");

    Texture gras = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg");

    mat1.setTexture("m_ColorMap", gras);



    floor.setMaterial(mat1);



    CollisionShape sceneShape = CollisionShapeFactory

    .createMeshShape(floor);

    landscape = new RigidBodyControl(sceneShape, 0);

    floor.addControl(landscape);



    rootNode.attachChild(floor);

    bulletAppState.getPhysicsSpace().add(landscape);

    }



    private void setUpLight() {

    DirectionalLight dl = new DirectionalLight();

    dl.setColor(ColorRGBA.White);

    dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());

    rootNode.addLight(dl);

    }



    private void setUpKeys() {

    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.addListener(this, "Left");

    inputManager.addListener(this, "Right");

    inputManager.addListener(this, "Up");

    inputManager.addListener(this, "Down");

    }



    @Override

    public void simpleUpdate(float tpf) {

    walkDirection.set(0, 0, 0);

    if (left) {

    walkDirection.setX(-speed);

    }

    if (right) {

    walkDirection.setX(speed);

    }

    if (up) {

    walkDirection.setZ(-speed);

    }

    if (down) {

    walkDirection.setZ(speed);

    }



    character.setWalkDirection(walkDirection);

    }



    @Override

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

    if (name.equals("Down") && keyPressed) {

    down = true;

    } else if (name.equals("Down") && !keyPressed) {

    down = false;

    } else if (name.equals("Up") && keyPressed) {

    up = true;

    } else if (name.equals("Up") && !keyPressed) {

    up = false;

    } else if (name.equals("Left") && keyPressed) {

    left = true;

    } else if (name.equals("Left") && !keyPressed) {

    left = false;

    } else if (name.equals("Right") && keyPressed) {

    right = true;

    } else if (name.equals("Right") && !keyPressed) {

    right = false;

    }

    }

    }

    [/java]

Anyone got an idea?

Hi mldeluxe,



I checked your testclass and its indeed realy strange. I looked at the TestQ3 in the bullet examples again and I can see this behavior in parts of the level too.

Maybe something is broken in the CharacterControl?! I didnt use CharacterControl since a couple of month now. Before that it worked fine.



Strange, strange. Maybe normen can help.



milchmann

Hello



I’ve heard other users over the past few months encountering the characterControl slipping through the floor problem. The suggested solution usually is to have a more dense mesh for the floor object. The box face that you’re using for the floor is 100x100 units, which is quite large. I’m guessing the reason it’s suggested is because the physics calculations get a bit confused when the collision object has it’s points so spread apart. Anyway, it’s an easy thing to try.



Another thing you could mess with is the actual capsule shape’s dimensions. The capsule you’re using is barely a pill shape at all with a length of 1 and a rad of 2 (5 units in all tall, I think).



As far as the collision shape being placed at the feet of the ninja model, any model with a characterControl always has it’s center repositioned to the center of the characterControl’s physics position. I don’t know much about the ninja model myself, but you can offset the center of the model using another node. If you search the forum a bit, you’ll probably find another user who did this fairly recently.



Cheers

~FlaH

I seem to be having the exact same problem, tho on a much smaller scale. I created a 5x5 floor, and my player (a simple cube) still falls into the floor. Tho it looks like the engine is trying to fix itself - meaning the player pops out of the floor and starts sinking back in. The only thing different in my code is using CylinderCollisionShape instead of a capsule and using a simple box for a player instead of a mesh. I’m using todays build, tho not working with jme ide.



It’s actually my first dip into 3d programming so I’m bound to get stuff wrong, but seeing this thread made me think that there’s more to this problem.



The code, for sake of completeness:



App.java

[java]

public void simpleInitApp() {

appState = new BulletAppState();

stateManager.attach(appState);

appState.getPhysicsSpace().enableDebug(assetManager);



attachTile(Floor.create(assetManager, 5, 5)).moveTile(0, -1, 0);



player = Player.create(assetManager);

player.moveTile(1.1f, 1f, 1.1f);



attachTile(player);



flyCam.setEnabled(false);

chaseCam = new ChaseCamera(cam, player.getSpatial(), inputManager);

}

private MapTile attachTile(MapTile tile) {

rootNode.attachChild(tile.getSpatial());

appState.getPhysicsSpace().add(tile.getControl());

return tile;

}

[/java]



Floor.java

[java]

public class Floor implements MapTile {

private CollisionShape cShape;



private RigidBodyControl control;

private Geometry geo;



public static Floor create(AssetManager assetManager, float x, float z) {

Floor o = new Floor();

Box b = new Box(Vector3f.ZERO, x, 0.5f, z);

o.geo = new Geometry(“Floor”, b);

Material mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

mat.setColor(“Color”, ColorRGBA.DarkGray);

o.geo.setMaterial(mat);



o.cShape = CollisionShapeFactory.createBoxShape(o.geo);



o.control = new RigidBodyControl(o.cShape, 0);



o.geo.addControl(o.control);

return o;

}



public PhysicsControl getControl() {

return control;

}



public Floor moveTile(float x, float y, float z) {

this.control.setPhysicsLocation(new Vector3f(x, y, z));

return this;

}



public Spatial getSpatial() {

return geo;

}

}

[/java]



Player.java

[java]

public class Player implements MapTile {

private Spatial node;



private CollisionShape cShape;

private CharacterControl control;



public static Player create(AssetManager assetManager) {

Player o = new Player();



Box b = new Box(0.4f, 0.5f, 0.4f);

o.node = new Geometry(“Player”, b);

Material mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

mat.setColor(“Color”, ColorRGBA.Red);

o.node.setMaterial(mat);



o.cShape = new CylinderCollisionShape(new Vector3f(0.4f, 0.5f, 0.4f), 1);

o.control = new CharacterControl(o.cShape, 0.01f);



o.node.addControl(o.control);



return o;

}



public Spatial getSpatial() {

return node;

}



public CharacterControl getControl() {

return control;

}



public Player moveTile(float x, float y, float z) {

this.control.setPhysicsLocation(new Vector3f(x, y, z));

return this;

}

}

[/java]



If i replace CharacterControl with RigidBodyControl everything is alright - the cube doesn’t fall through the floor.

Sticking with CharacterControl - the suggestions tehflah seem to solve the problem only partially. For example making the CollisionCylinder bigger only lessens the effect. The player still seems to sink into the floor and pop out of it, just not completely. Only about half a unit. Strange.



So - what’s the best way to deal with this problem?



Should I try to get CharacterControl to work or can I use some other control for the player? What are the advantages of using either?

What’s the usual/best scale for working with opengl? (is it ok if my tiles are 1 world unit large? Or should they be bigger?)

Raise the stepHeight of the character and for heavens sake dont use super-large boxes.

Yeah, I got the message about large boxes from tehflahs post :slight_smile:



But this really boils down to what is recommended scale for working here? (meaning the height of an ‘normal’ user-visible object) Is it 1 world unit? 10? I’m trying to build a basis for a tile-based game, so i’ll be working with mostly square-based objects. So how big (world unit wise) should one 3d tile be?



I tried fiddling with the stepHeight but i still get the same thing (big values make the player fall through completely, small result in him sinking in and popping back continuously). I understand this value should probably be relative to how big my other objects are. But the docs or tutorials don’t really say anything about this.

Ok, I scaled everything up to 10 world units (floor made up from 10x10x10 tiles, player 8x10x8) and everything seems fine.



On to the next problem :wink: Thanks.

I noticed a thing you could change to make the code a bit more simple.

[java] if (name.equals("Down") && keyPressed) {

down = true;

} else if (name.equals("Down") && !keyPressed) {

down = false;

}[/java]

Change to:

[java] if (name.equals("Down")) {

down = keyPressed;

}[/java]

And do the same with all the keypresses :slight_smile: