BetterCharacterControl + CameraNode how to make First Person View

I would like to use BetterCharacterControl and CameraNode to make a normal first person view(as in any fps , gun attached to screen not 3rd person).
If I am using SimpleApplication and set flyCam.setEnabled(false);

How do I make the camera follow my mouse movements? Should the mouse be moving the BetterCharacterControl or should it be Moving the CameraNode

Thanks for any help.

-Greg

Hi there,

Usually with the better character control you add it as a control to a physical model or node. As for your camera it should be just along for the ride normally I use ChaseCamera even for fps and just zoom it in all the way and lock the distance.

You can also always attach the camera directly to your Node.

Also, the bettercharactercontrol would move the camera via the setWalkDirection() method

You can set

flyCam.setMoveSpeed(0);
flyCam.setZoomSpeed(0);

(so the fly camera is still rotating with the mouse) instead of disable it and in simpleUpdate() move the camera according with the BetterCharacterControl position and rotate the BetterCharacterControl according with the camera rotation.

I think this is the easiest way.

Sorry I still don’t understand.
Does anyone have a code example of BetterCharacterControl for a FPS view? Where the mouse moves the Character

the mouse moves the character?

I dont understand

Like you click where you want to go?

Sorry not explaining it well.
I just want to do a fps like minecraft or portal where the movement of the mouse moves the view around, but I want to do this with the BetterCharacterControl.

I am looking for a code example since I am sure others have done this before.

Thanks for any help
-Greg

Yes indeed I have!

I used the chase cam though for my fps as I’m just use to doing this and it is nifty to be able to go from first to 3rd person.

Here is my github from the contest and it is an fps using the bettercharactercontrol and has the mouse used like you say.

The relevant classes are CameraManager PlayerManager and InteractionManager

2 Likes

This is my solution, even if Big Bob’s one is still good.
[java]

public void simpleUpdate(float tpf) { 		
	if(PLAYER_NODE==null||BETTER_CHARACTER_CONTROL==null||GUN_NODE==null)return;
	Vector3f cameradir=cam.getDirection();
	Vector3f playerposition=PLAYER_NODE.getWorldTranslation();
	BETTER_CHARACTER_CONTROL.setViewDirection(cameradir);
	cam.setLocation(playerposition);
            
            Vector3f gunpos= cam.getLocation().clone().subtractLocal(GUN_NODE.getWorldTranslation()).addLocal(GUN_NODE.getLocalTranslation());
            GUN_NODE.setLocalTranslation(gunpos);
            
            
            Quaternion gunrot=cam.getRotation().clone().subtractLocal(GUN_NODE.getWorldRotation()).addLocal(GUN_NODE.getLocalRotation());
            GUN_NODE.setLocalRotation(gunrot);
}

[/java]

1 Like

Thanks @BigBob it is working now. I Used the ChaseCamera instead of CameraNode.
The Next Thing I need to figure out is how to attach a weapon. I tried attaching to the CharacterNode but when I look up or down it stays in a fixed position and does not move with the camera. Since its a ChaseCamera I am not sure how to attached the weapon to the Camera.

This is what I did if anyone else needs it.

[java]
package mygame;

import com.jme3.app.DebugKeysAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.app.StatsAppState;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.terrain.geomipmap.TerrainQuad;

public class TestTwo extends SimpleApplication implements ActionListener {

private BulletAppState bulletAppState;
private BetterCharacterControl physicsCharacter;
private TerrainQuad terrain;
private ChaseCamera chaseCam;
private Node characterNode;
boolean leftStrafe = false, rightStrafe = false;
private boolean left = false, right = false, up = false, down = false;
private Vector3f walkDirection = new Vector3f();
private Vector3f camDir = new Vector3f();
private Vector3f camLeft = new Vector3f();
public final float SPEED = 5.0f;

public TestTwo() {
    //Skipping Fly Cam
    super(new StatsAppState(),  new DebugKeysAppState());
}

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

@Override
public void simpleInitApp() {
    this.setDisplayFps(false);
    this.setDisplayStatView(false);
    this.setShowSettings(false);
    this.inputManager.setCursorVisible(false);

    // activate physics
    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);
    initTerrian();
    initPlayer();
    initWeapon();
    initLight();
    initChaseCamera();
    bulletAppState.setDebugEnabled(true);
    setUpKeys();
   
}

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

    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White);
    rootNode.addLight(sun);
}

private void initTerrian() {
    Spatial level = assetManager.loadModel("Scenes/LevelOne.j3o");
    rootNode.attachChild(level);
    terrain = (TerrainQuad) ((Node) rootNode.getChild("New Scene")).getChild("terrain-LevelOne");
    terrain.addControl(new RigidBodyControl(0));
    bulletAppState.getPhysicsSpace().add(terrain);
}

private void initPlayer() {
    // Create a node for the character model
    characterNode = new Node("character node");
    characterNode.setLocalTranslation(new Vector3f(4, 5, 2));

    // Add a character control to the node so we can add other things and
    // control the model rotation
    physicsCharacter = new BetterCharacterControl(0.3f, 2.0f, 8f);
    characterNode.addControl(physicsCharacter);
    bulletAppState.getPhysicsSpace().add(physicsCharacter);

    // Load model, attach to character node
    Node model = (Node) assetManager.loadModel("Models/Man.mesh.j3o");
    model.setLocalScale(0.25f);
    model.setLocalTranslation(new Vector3f(0, 1.1f, 0));
    model.setName("Player");
   
    characterNode.attachChild(model);

    // Add character node to the rootNode
    rootNode.attachChild(characterNode);
}

private void initWeapon(){
    Node weapon = (Node) assetManager.loadModel("Models/Cube/Cube.mesh.j3o");
    weapon.setLocalScale(.1f);
    weapon.setLocalTranslation(new Vector3f(-3f, 0f, 5f));
    characterNode.attachChild(weapon);
    
}

private void initChaseCamera() {        
    chaseCam = new ChaseCamera(cam, characterNode.getChild("Player"), inputManager);
    chaseCam.setMinDistance(0.1f);
    chaseCam.setMaxDistance(0.1f);
    chaseCam.setInvertVerticalAxis(true);
    chaseCam.setDragToRotate(false);        
}

@Override
public void simpleUpdate(float tpf) {        
    camDir.set(cam.getDirection()).multLocal(SPEED, 0.0f, SPEED);
    camLeft.set(cam.getLeft()).multLocal(SPEED);
    walkDirection.set(0, 0, 0);
    if (left) {
        walkDirection.addLocal(camLeft);
    }
    if (right) {
        walkDirection.addLocal(camLeft.negate());
    }
    if (up) {
        walkDirection.addLocal(camDir);
    }
    if (down) {
        walkDirection.addLocal(camDir.negate());
    }
    physicsCharacter.setWalkDirection(walkDirection.multLocal(1));
    physicsCharacter.setViewDirection(camDir);
}

private void setUpKeys() {
    inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping("Click", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
    inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addListener(this, "Up");
    inputManager.addListener(this, "Down");
    inputManager.addListener(this, "Left");
    inputManager.addListener(this, "Right");
    inputManager.addListener(this, "Click");
    inputManager.addListener(this, "Jump");
}

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("Down")) {
        down = isPressed;
    } else if (binding.equals("Up")) {
        up = isPressed;
    }else if (binding.equals("Jump")) {
        physicsCharacter.jump();
    }
}

}
[/java]

@Scriptblocks(gbluntzer)

Well theres a few ways to do it!

The standard practice I’ve heard is to attach the gun to the GUI Node.

What I’ve done to make it a physical object in the scene you can use the “attachment node” for a bone.

This attachment node can be easily made accessible through the sdk, or by retrieving the skeleton then retrieving a specific bone, then retrieving the attachment node.

This will make the object attached move along with the arms (lets say a knife in hand as you run) the knife will be a physical object in the scene attached to the hand following its animations!