BetterCharacterControl Issues

Hello, I am currently trying to implement the BetterCharacterControl as a first person controller. There is no method to get the position of the BetterCharacterControl so I have to attach it to a node and get the node’s position – even if this is the “recommended” way to do it, I’d prefer something a little less messy. Additionally, the control bounces in the air randomly after running for a short distance. Lastly, the position of the node seems to be slightly lower than that of the terrain (the node appears at negative y levels according to the System.out.println() statement and the camera fails to show the terrain except during the random bounces and deliberate jumps).

Here is the code:

package battleforcaern.game;

import battleforcaern.BattleForCaern;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;

public class CharacterControlState extends AbstractAppState implements ActionListener{
    private BattleForCaern game;

    private BetterCharacterControl characterControl;
    private Node controlNode;

    private Vector3f walkDirection;
    private Vector3f camDir;
    private Vector3f camLeft;
    private Vector3f negateCache;

    private boolean left = false;
    private boolean right = false;
    private boolean forward = false;
    private boolean backward = false;

    @Override
    public void initialize(AppStateManager stateManager, Application app){
        game = ((BattleForCaern) app);

        characterControl = new BetterCharacterControl(1.5f, 6f, 1);
        characterControl.setGravity(new Vector3f(0, 1f, 0));
        characterControl.setJumpForce(new Vector3f(0, 5f, 0));
        characterControl.warp(new Vector3f(0, 256, 0));
        game.getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(characterControl);

        controlNode = new Node("Character Control Node");
        controlNode.addControl(characterControl);
        game.getRootNode().attachChild(controlNode);

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

        InputManager inputManager = game.getInputManager();
        inputManager.addMapping("forward", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("backward", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("jump", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addListener(this, "forward", "left", "backward", "right", "jump");
    }

    @Override
    public void update(float tpf){
        walkDirection.set(0, 0, 0);

        if(characterControl.isOnGround()){
            camDir.set(game.getCamera().getDirection());
            camLeft.set(game.getCamera().getLeft());

            camDir.setY(0);
            camLeft.setY(0);

            camDir.multLocal(90f);
            camLeft.multLocal(60f);

            if(forward){
                walkDirection.addLocal(camDir);
            }

            if(backward){
                negateCache.set(camDir);
                negateCache.negateLocal();

                walkDirection.addLocal(negateCache);
            }

            if(left){
                walkDirection.addLocal(camLeft);
            }

            if(right){
                negateCache.set(camLeft);
                negateCache.negateLocal();

                walkDirection.addLocal(negateCache);
            }
        }

        characterControl.setWalkDirection(walkDirection);

        game.getCamera().setLocation(controlNode.getWorldTranslation());
        System.out.println(controlNode.getWorldTranslation());
    }

    @Override
    public void cleanup(){
        game.getRootNode().detachChild(controlNode);
        game.getStateManager().getState(BulletAppState.class).getPhysicsSpace().remove(characterControl);
    }

    @Override
    public void onAction(String name, boolean isPressed, float tpf){
        switch(name){
            case "forward":
                forward = isPressed;
                break;
            case "left":
                left = isPressed;
                break;
            case "backward":
                backward = isPressed;
                break;
            case "right":
                right = isPressed;
                break;
            case "jump":
                characterControl.jump();
                break;
        }
    }

}

You keep adding a vector to the movement direction/velocity vector each frame, probably explains why after a while the character moves very quickly. As for your other issue, enable the debug view, that should probably show you why your actual model is in the ground.

Also…

Interpretation:
“I have a 3 meter wide, 6 meter tall cylinder of styrofoam in an environment that’s 1/10th earth’s gravity. Why does it behave strangely?”

Even the moon’s gravity would be 1.6.

So imagine an astronaut that is 18 feet tall and 9 feet wide… but weighs only as much as a 1 liter bottle of soda. Imagine what a regular 1.8 meter, 0.2 meter wide, 100 kg (with suit) astronaut looked like on the moon… then think about what this giant balloon astronaut would behave like. He might be a bit bouncy.

3 Likes

Where am I adding this vector? The closest thing I can think of is setWalkDirection() which is supposed to overwrite all previous velocity vectors. The walkdirection variable is reset to 0,0,0 with each update.

Also, when I enable the debug view, the terrain shows no difference (although the control is clearly colliding with it in some way or another) and the character control’s view is mostly hidden except for a few moments when it becomes visible and appears slightly ahead of where the camera is.

When the camera is decoupled from the position of the charactercontrol (and the charactercontrol rebound to TFGH) little more information is revealed. It simply shows that the charactercontrol bounces as before. It does, however, appear as if it bounces more when going uphill on the terrain although this could just be the human brain’s tendency to see patterns where none exist.

When I change BetterCharacterControl’s constructor call to the following the issue still occurs.

characterControl = new BetterCharacterControl(1f, 4f, 150f);
game.getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(characterControl);

Is it a humanoid character on earth or a giant balloon on some moon? That will help me give you proper values.

If it’s a human on earth then try:
characterControl = new BetterCharacterControl(0.2f, 1.8f, 150f);

And set the gravity to 20 and not 1. Earth gravity is 10 but 10 often looks too spongy in computer games and so most of them use 20 in their physics simulations.

It’s not the solution to all of your problems but step one of “physics behaving funny” is to give it sane values and then work from there.

When I do that the only difference is the size of the character. Also, yes, it is a human on Earth.

Upping gravity should have affected something I’d have thought.

The point of the smaller radius is it will make hills seem less squishy and you won’t bump as much on any random ground-level thing that happens to be near the player.

I understand why that may be useful (I was originally using the defaults from the wiki). However, since it didn’t work, what else could be the issue? I’ll probably try looking at some open source game’s code if I can find some.