First person camera issues

Hey,

I’m trying to make a first-person camera with a weapon, but somehow I’m not affected by the gravity and physics and my sword falls through the ground. I’ve added [java]playerNode.addControl(player)[/java] which I thought would make the player’s node follow the CharacterControl but it does not work and I can’t figure out what is the problem.

Here is my code:
[java]
package mygame;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
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.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.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Node;
import com.jme3.scene.control.CameraControl.ControlDirection;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;

/**

  • test

  • @author jadam
    */
    public class Main extends SimpleApplication implements ActionListener,AnimEventListener{
    private CameraNode cn;
    private AnimChannel channel;
    private AnimControl control;
    private Node sword;
    private CharacterControl player;
    private Node playerNode;
    private RigidBodyControl landscape;
    private BulletAppState bulletAppState;
    //Temporary vectors used on each frame.
    //They here to avoid instanciating new vectors on each frame
    private Vector3f camDir = new Vector3f();
    private Vector3f camLeft = new Vector3f();

    private TerrainQuad terrain;
    private Material mat_terrain;
    private boolean left = false, right = false, up = false, down = false;
    private Vector3f walkDirection = new Vector3f();
    public static void main(String[] args) {
    Main app = new Main();
    app.start();
    }
    private void initTerrain()
    {
    /** 1. Create terrain material and load four textures into it. */
    mat_terrain = new Material(assetManager,
    “Common/MatDefs/Terrain/Terrain.j3md”);

      /** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */
      mat_terrain.setTexture("Alpha", assetManager.loadTexture(
              "Textures/Terrain/splat/alphamap.png"));
    
      /** 1.2) Add GRASS texture into the red layer (Tex1). */
      Texture grass = assetManager.loadTexture(
              "Textures/Terrain/splat/grass.jpg");
      grass.setWrap(WrapMode.Repeat);
      mat_terrain.setTexture("Tex1", grass);
      mat_terrain.setFloat("Tex1Scale", 64f);
    
      /** 1.3) Add DIRT texture into the green layer (Tex2) */
      Texture dirt = assetManager.loadTexture(
              "Textures/Terrain/splat/dirt.jpg");
      dirt.setWrap(WrapMode.Repeat);
      mat_terrain.setTexture("Tex2", dirt);
      mat_terrain.setFloat("Tex2Scale", 32f);
    
      /** 1.4) Add ROAD texture into the blue layer (Tex3) */
      Texture rock = assetManager.loadTexture(
              "Textures/Terrain/splat/road.jpg");
      rock.setWrap(WrapMode.Repeat);
      mat_terrain.setTexture("Tex3", rock);
      mat_terrain.setFloat("Tex3Scale", 128f);
    
      /** 2. Create the height map */
      AbstractHeightMap heightmap = null;
      Texture heightMapImage = assetManager.loadTexture(
              "Textures/Terrain/splat/mountains512.png");
      heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
      heightmap.load();
    
      int patchSize = 65;
      terrain = new TerrainQuad("my terrain", patchSize, 513, heightmap.getHeightMap());
    
      /** 4. We give the terrain its material, position & scale it, and attach it. */
      terrain.setMaterial(mat_terrain);
      terrain.setLocalTranslation(0, -100, 0);
      terrain.setLocalScale(2f, 1f, 2f);
      rootNode.attachChild(terrain);
    
      /** 5. The LOD (level of detail) depends on were the camera is: */
      TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
      terrain.addControl(control);
      
      CollisionShape sceneShape =
         CollisionShapeFactory.createMeshShape((Node) terrain);
     landscape = new RigidBodyControl(sceneShape, 0);
     terrain.addControl(landscape);
     bulletAppState.getPhysicsSpace().add(terrain);
    

    }
    private void initPlayer()
    {
    playerNode = new Node(“player node”);
    flyCam.setMoveSpeed(100);
    CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
    player = new CharacterControl(capsuleShape, 0.05f);
    player.setJumpSpeed(20);
    player.setFallSpeed(30);
    player.setGravity(30);
    player.setPhysicsLocation(new Vector3f(0, 200, 0));
    bulletAppState.getPhysicsSpace().add(player);

    sword = (Node) assetManager.loadModel(“Scenes/Cube.002.mesh.xml”);

     cn = new CameraNode("first person camera node",cam);
     cn.setControlDir(ControlDirection.CameraToSpatial);
     playerNode.addControl(player);
     
    
     playerNode.attachChild(sword);
     playerNode.attachChild(cn);
     rootNode.attachChild(playerNode);
     
    
    control = sword.getControl(AnimControl.class);
     control.addListener(this);
     channel = control.createChannel();
     channel.setAnim("sword idle");
     channel.setLoopMode(LoopMode.Loop);        
    

    }
    @Override
    public void simpleInitApp() {
    /** Set up Physics */
    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);
    //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
    viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));

     AmbientLight al = new AmbientLight();
     al.setColor(ColorRGBA.White.mult(1.3f));
     rootNode.addLight(al);
     
     
     
     
     
     setUpKeys();
     initTerrain();
     initPlayer();
    

    }
    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
    if (animName.equals(“sword swing”)) {
    channel.setAnim(“sword idle”, 0.50f);
    channel.setLoopMode(LoopMode.Loop);
    channel.setSpeed(1f);
    }
    }
    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.addMapping(“Jump”, new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addMapping(“ATTACK”, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
    inputManager.addListener(this, “Left”);
    inputManager.addListener(this, “Right”);
    inputManager.addListener(this, “Up”);
    inputManager.addListener(this, “Down”);
    inputManager.addListener(this, “Jump”);
    inputManager.addListener(this, “ATTACK”);

}
public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
// unused
}
@Override
public void simpleUpdate(float tpf) {
camDir.set(cam.getDirection()).multLocal(0.6f);
camLeft.set(cam.getLeft()).multLocal(0.4f);
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());
}
player.setWalkDirection(walkDirection);
//cam.setLocation(player.getPhysicsLocation());

}

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

public void onAction(String name, boolean isPressed, float tpf) {
    if (isPressed)
    {
        if (name.equals("ATTACK"))
        {
            channel.setAnim("sword swing");
            channel.setLoopMode(LoopMode.DontLoop);
        }
    }
if (name.equals("Left")) {
  left = isPressed;
} else if (name.equals("Right")) {
  right= isPressed;
} else if (name.equals("Up")) {
  up = isPressed;
} else if (name.equals("Down")) {
  down = isPressed;
} else if (name.equals("Jump")) {
  if (isPressed) { player.jump(); }
}        
}

}
[/java]

Thanks in advance!

the root of your problem is here :

cn.setControlDir(ControlDirection.CameraToSpatial);

this line made the camera authoritative about the position and the rotation of the cameranode. So, no matter where your character is, the camera will set the position (and rotation) of the cameraNode.

This is not the comportement by default, however, which is SpatialToCamera.

If you change this line (comment it for exemple) the camera will follow your character. But, then, the cameranode will decide about the rotation, so you will not be able to look around with the camera (the camera will have a fixed direction).

To solve this, well … you can either use a chasecamera of handle the input and update the camera rotation yourself.

Btw, the sword is falling through ground because you need to set the local translation of the character node.

For exemple like this :

playerNode.setLocalTranslation(0, 200, 0);

If you don’t want to add this line, then put the line

player.setPhysicsLocation(new Vector3f(0, 200, 0));

AFTER the line

playerNode.addControl(player);

because when you add the control to the node, the control update its position to the node’s position, overwriting what you put before.

1 Like

Thanks a lot for the explanation!:slight_smile: