BetterCharacterControl - problems with first person

Hey guys,
I’m new to 3D programming and the JMonkeyEngine, so please do not judge me for the mistakes I surely made :slight_smile:
I’m just trying to create a Terrain where a first person character can walk. This is my approach:

[java]package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
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.math.ColorRGBA;
import com.jme3.math.Vector3f;
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 {

    private BulletAppState bulletAppState;
    private RigidBodyControl terrainControl;
    private Node player;
    private BetterCharacterControl playerControl;

    private Vector3f walkDirection = new Vector3f(0.0f, 0.0f, 0.0f);
    private boolean left,right,front,back;

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

    @Override
    public void simpleInitApp() {
    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);

     initTerrain();
     initLight();
     initPlayer();
     initKeys();
    

    }

    private void initTerrain() {
    Spatial terrain = assetManager.loadModel(“Scenes/Terrain.j3o”);
    terrainControl = new RigidBodyControl(0.0f);
    terrain.addControl(terrainControl);
    bulletAppState.getPhysicsSpace().add(terrainControl);
    rootNode.attachChild(terrain);
    }

    private void initPlayer() {
    player = new Node(“player”);
    player.setLocalTranslation(new Vector3f(50f, 30f, 0.0f));
    playerControl = new BetterCharacterControl(1f, -1.8f, 1f);
    playerControl.setApplyPhysicsLocal(true);
    playerControl.setSpatial(player);
    player.addControl(playerControl);
    bulletAppState.getPhysicsSpace().add(playerControl);
    rootNode.attachChild(player);
    }

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

    public void initKeys() {
    inputManager.addMapping(“Left”, new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping(“Front”, new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping(“Back”, new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping(“Jump”, new KeyTrigger(KeyInput.KEY_SPACE));

     inputManager.addListener(this, "Left");
     inputManager.addListener(this, "Right");
     inputManager.addListener(this, "Front");
     inputManager.addListener(this, "Back");
     inputManager.addListener(this, "Jump");
    

    }

    @Override
    public void simpleUpdate(float tpf) {
    Vector3f camDir = cam.getDirection();
    Vector3f camLeft = cam.getLeft();

     walkDirection.set(0.0f, 0.0f, 0.0f);
     
     if(left) {
         walkDirection.addLocal(camLeft);
     }
     if(right) {
         walkDirection.addLocal(camLeft.negate());
     }
     if(front) {
         walkDirection.addLocal(camDir);
     }
     if(back) {
         walkDirection.addLocal(camDir.negate());
     }
     
     walkDirection.setY(0.0f);
     
     playerControl.setWalkDirection(walkDirection);
     
     cam.setLocation(player.getLocalTranslation());
    

    }

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

    public void onAction(String name, boolean isPressed, float tpf) {
    if(name.equals(“Left”)) {
    left = isPressed;
    } else if(name.equals(“Right”)) {
    right = isPressed;
    } else if(name.equals(“Front”)) {
    front = isPressed;
    } else if(name.equals(“Back”)) {
    back = isPressed;
    } else if(name.equals(“Jump”)) {
    playerControl.jump();
    }
    }
    }
    [/java]

It works fine so far, but I’ve got a few problems. First of all, I need to set the speed for the BetterCharacterControl. It’s moving incredebly slow. I could change it by changing this
[java]Vector3f camDir = cam.getDirection();
Vector3f camLeft = cam.getLeft();[/java]
to this:
[java]Vector3f camDir = cam.getDirection().mult(20f);
Vector3f camLeft = cam.getLeft().mult(10f);
[/java]

But after this the physics when walking onto a hill are completely messed up. When moving over a hill, the character flies over the terrain because he’s so fast. Any idea how I could solve this?

Then I’d also like to disable the possibility to look to the feet of the character (I’m simply to lazy to make a model for them :)). How can I tell the camera not to move further when a certain point is reached?

It’d also be great if I could disable moving in the air. How can I find out if the character does not touch the ground?

And at last I’d like to disable the zooming. Is this just done with the stateManager?

As you see, I tried to add jumping (space) but BetterCharacterControl.jump() simply does nothing. Any ideas?

Thanks!

mathiasj

  1. this is really a large problem, possible “solutions” include increasing gravity, doing a raycast downwards, and applying force downwards if lossing contanct, and jump key was not pressed the last X ms.

  2. same raycast downwards, there should also be a onGround or isOnground or so in the class

  3. you need to remove the mappings for the zoom

  4. you could get camera rotation, convert to angles, check for your constraints, and clamp them, convert back to quaternion and apply to camera.

  5. to less force? false multithreading? I can gurantee that it’s working as I use it without problems.

I just tried getting rid of the first problem, but the method onGround() (of the BetterCharacterControl) always returned false. I saw, that I’ve create my Control this way:
[java]playerControl = new BetterCharacterControl(1f, -1.8f, 1f);[/java]
and because of the negative height, id didn’t work. I changed it to this:
[java]playerControl = new BetterCharacterControl(0.5f, 1.8f, 80f);[/java]
Now onGround() returns the correct boolean, but the BetterCharacterControl is stuck in the ground… (That’s also why I started with a negative float at the beginning - The only way the player was not in the ground).

mathiasj

Okay, the negative height seems also to be the problem with jumping…

Recenter your model either in modeleing program,
or with a node and attaching it offseted, then move the controll ot the node isntead of the model.

I don’t even use a model for the character because it’s a first person game. And this didn’t work either:

[java]player = new Node(“player”);
playerControl = new BetterCharacterControl(0.5f, 1.8f, 80f);
playerControl.setApplyPhysicsLocal(true);
playerControl.setSpatial(player);
player.addControl(playerControl);
bulletAppState.getPhysicsSpace().add(playerControl);
playerControl.warp(new Vector3f(58f, 6f, 0.0f));
rootNode.attachChild(player);[/java]

mathiasj

well your spatial is your model, nromally palyers have their center at the feet, so you need to offset your spatial then with a node.

Hmm I can’t get it done… Could you please provide some code?

Another solution: leaving the node where it is and setting the camera above the actual player… Is this a good or bad idea?

mathiasj

Well the player should be so centerd, that it is visually right, the camera should not be mixed with that, it should be handled seperatly.

And how should I center the character? You said something of offset… It’d be nice if you could give me some code.
Everything seems to be easier with the default CharacterControl… :slight_smile:

mathiasj

Node char = new Node();
char.attachChild(whatEverVisiblePlayer);
whatEverVisblePlayer.setLocalTranslation(0,1.8f,0);
character.setSpatial(char);

This didn’t work…
[java]private void initPlayer() {
player = new Node(“player”);
Node whatEverVisiblePlayer = new Node();
player.attachChild(whatEverVisiblePlayer);
whatEverVisiblePlayer.setLocalTranslation(0,1.8f,0);;
playerControl = new BetterCharacterControl(0.5f, 1.8f, 80f);
playerControl.setApplyPhysicsLocal(true);
playerControl.setSpatial(player);
player.addControl(playerControl);
bulletAppState.getPhysicsSpace().add(playerControl);
playerControl.warp(new Vector3f(58f, 6f, 0.0f));
rootNode.attachChild(player);
//playerControl.setGravity(new Vector3f(0f, -100f, 0f));
}[/java]

But I read from another Thread:

For BetterCharacterControl the origin is at the bottom of the capsule (as it should be if you ask me :P) So when simply asking for translation you will get the location of the bottom of the capsule. In effect you simply need to translate the cam up.
So it would be right to move the position of the camera up? This would also explain why the player (with a random model) is not in the ground when using a third person view...

To the other porblems:

  1. Increasing gravity is not that good because sometimes (when I am at the top of a mountain and jump) I fall through the ground because the force is too big. And with increased gravity I start bumping around when moving (because the ground is rough). And what about the ray? Didn’t really try that so far, but would you mean something like this?
    [java]Ray ray = new Ray(cam.getLocation(), new Vector3f(0,-10f,0);[/java]
    But I don’t know how this could help me…

  2. Works fine, thanks!

  3. It worked, but you can’t do that in simpleInitApp(). (No problem, just wanted to inform others)

  4. I tried using something like this:
    [java]float[] angles = new float[3];
    cam.getRotation().toAngles(angles);
    if(angles[2] > Math.PI*(3/4)) { //More than 135°
    angles[2] = (float) Math.PI*(3/4);
    Quaternion quat = cam.getRotation().fromAngles(angles);
    cam.setRotation(quat);
    }[/java]
    But it showed no effect. Could you give me some code?

  5. Jumping is solved now (The problem was the negative height)

And thanks! You already helped me a lot!

All my code would be useless for you, as it uses a ES logic and has no visual components, anyway here is it.

[java]
private void createNew() {
for (final GameEntity newHumanoid : this.humanoidSet.getAdded()) {
HumanoidSystem.logger.info("Creating new HumanoidPhysics for " + newHumanoid.getUuid());
final Humanoid humanoid = (Humanoid) newHumanoid.get(this.humanoidIndex);
if (humanoid.getParentPhysicsSpace() == null) {
HumanoidSystem.logger.debug("No parentPhysicsSpace for Humanoid " + newHumanoid);
continue;
}
final VGSBetterCharacterControll controll = new VGSBetterCharacterControll(0.5f, humanoid.getSize(), humanoid.getMass());
final Position pos = (Position) newHumanoid.get(this.positionIndex);
controll.warp(pos.asVector3f());
this.bcc.put(newHumanoid.getUuid(), controll);

		controll.getRigidBody().setUserObject(newHumanoid);
		controll.setDuckedFactor(humanoid.getDuckSize() / humanoid.getSize());
		controll.setGravity(new Vector3f(0, -10, 0));

		boolean addListener = false;
		if (this.supervisedPhysicSpaces.get(humanoid.getParentPhysicsSpace()) == null) {
			addListener = true;
		}
		final boolean faddListener = addListener;

		this.physicSystem.addTask(new IPhysicTask() {
			@Override
			public void run(final PhysicSpaceHolder pspace) {
				pspace.add(controll);
				if (faddListener) {
					pspace.addListener(HumanoidSystem.this);
				}
			}
		}, humanoid.getParentPhysicsSpace());
	}
}

[/java]

Do you also have code for the problem with the feet?

roughtly something like this.

getCamlocation(){
player.getLocation()+Vector(0,2,0)
}

Yeah that’s working but I meant the “problem” that I don’t want to model the feet so some code to not allow the camera to go further after a certain angle is reached.

Thanks!