Beginner, fixed FlyByCamera for QWERTZ keyboards

Hi everyone.

I’m a jMonkeyEngine beginner from Croatia, and I have a QWERTZ keyboard. Since FlyByCamera uses QWERTY, and I found this topic
[java]http://hub.jmonkeyengine.org/forum/topic/bettercharactercontrol-cameranode-how-to-make-first-person-view/[/java]
on avoiding it, I’ve decided to write a little QWERTZ FlyByCamera implementation.

Since I’m just on my first jMK tutorials, I hope you can comment / criticise / improve this solution.

Thank you!

Main.java

[java]
package mygame;

import com.jme3.app.DebugKeysAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.app.StatsAppState;
import com.jme3.app.state.AppState;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;

/**

  • test

  • @author normenhansen
    */
    public class Main extends SimpleApplication {

    Vector3f camLocation;

    public Main(AppState… initialStates) {
    //Skipping Fly Cam
    // inspired by:
    //http://hub.jmonkeyengine.org/forum/topic/bettercharactercontrol-cameranode-how-to-make-first-person-view/
    super(new StatsAppState(), new DebugKeysAppState());
    }

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

    @Override
    public void simpleInitApp() {
    camLocation = new Vector3f(0f, 0f, 10f);
    cam.setLocation(camLocation);
    new AnalogListenerImpl(cam, inputManager);

     Box b = new Box(1f, 1f, 1f);
     Geometry geom = new Geometry("Box", b);
    
     Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
     mat.setColor("Color", ColorRGBA.Blue);
     geom.setMaterial(mat);
    
     rootNode.attachChild(geom);
    

    }

    @Override
    public void simpleUpdate(float tpf) {
    //TODO: add update code
    }

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

}
[/java]

AnalogListenerImpl

[java]
package mygame;

import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix3f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;

public class AnalogListenerImpl implements AnalogListener {

public static final String FLYCAM_Forward = "FLYCAM_Forward";
public static final String FLYCAM_Backward = "FLYCAM_Backward";
public static final String FLYCAM_StrafeLeft = "FLYCAM_StrafeLeft";
public static final String FLYCAM_StrafeRight = "FLYCAM_StrafeRight";
public static final String FLYCAM_Left = "FLYCAM_Left";
public static final String FLYCAM_Right = "FLYCAM_Right";
public static final String FLYCAM_Up = "FLYCAM_Up";
public static final String FLYCAM_Down = "FLYCAM_Down";
public static final String FLYCAM_Rise = "FLYCAM_Rise";
public static final String FLYCAM_Lower = "FLYCAM_Lower";
public static final String FLYCAM_ZoomIn = "FLYCAM_ZoomIn";
public static final String FLYCAM_ZoomOut = "FLYCAM_ZoomOut";

Camera cam;
InputManager inputManager;
Vector3f initialUpVec;
float rotationSpeed = 1f;
float moveSpeed = 3f;
float zoomSpeed = 1f;
boolean invertY = false;

public AnalogListenerImpl(Camera cam, InputManager inputManager) {
    this.cam = cam;
    this.inputManager = inputManager;
    
    initialUpVec = cam.getUp().clone();
    
    inputManager.setCursorVisible(false);
    inputManager.addMapping(FLYCAM_Forward,  new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping(FLYCAM_Backward,  new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping(FLYCAM_StrafeLeft,  new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping(FLYCAM_StrafeRight,  new KeyTrigger(KeyInput.KEY_D));
    inputManager.addListener(this, FLYCAM_Forward);
    inputManager.addListener(this, FLYCAM_Backward);
    inputManager.addListener(this, FLYCAM_StrafeLeft);
    inputManager.addListener(this, FLYCAM_StrafeRight);
    inputManager.addMapping(FLYCAM_Up, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
    inputManager.addMapping(FLYCAM_Down, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
    inputManager.addMapping(FLYCAM_Left, new MouseAxisTrigger(MouseInput.AXIS_X, true));
    inputManager.addMapping(FLYCAM_Right, new MouseAxisTrigger(MouseInput.AXIS_X, false));
    inputManager.addListener(this, FLYCAM_Up);
    inputManager.addListener(this, FLYCAM_Down);
    inputManager.addListener(this, FLYCAM_Left);
    inputManager.addListener(this, FLYCAM_Right);
    inputManager.addMapping(FLYCAM_Rise, new KeyTrigger(KeyInput.KEY_Q));
    inputManager.addMapping(FLYCAM_Lower, new KeyTrigger(KeyInput.KEY_Y));
    inputManager.addListener(this, FLYCAM_Rise);
    inputManager.addListener(this, FLYCAM_Lower);
    inputManager.addMapping(FLYCAM_ZoomIn, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
    inputManager.addMapping(FLYCAM_ZoomOut, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));
    inputManager.addListener(this, FLYCAM_ZoomIn);
    inputManager.addListener(this, FLYCAM_ZoomOut);
}

public void onAnalog(String name, float value, float tpf) {
    if (name.equals(FLYCAM_Left)){
        rotateCamera(value, initialUpVec);
    }else if (name.equals(FLYCAM_Right)){
        rotateCamera(-value, initialUpVec);
    }else if (name.equals(FLYCAM_Up)){
        rotateCamera(-value * (invertY ? -1 : 1), cam.getLeft());
    }else if (name.equals(FLYCAM_Down)){
        rotateCamera(value * (invertY ? -1 : 1), cam.getLeft());
    }else if (name.equals(FLYCAM_Forward)){
        moveCamera(value, false);
    }else if (name.equals(FLYCAM_Backward)){
        moveCamera(-value, false);
    }else if (name.equals(FLYCAM_StrafeLeft)){
        moveCamera(value, true);
    }else if (name.equals(FLYCAM_StrafeRight)){
        moveCamera(-value, true);
    }else if (name.equals(FLYCAM_Rise)){
        riseCamera(value);
    }else if (name.equals(FLYCAM_Lower)){
        riseCamera(-value);
    }else if (name.equals(FLYCAM_ZoomIn)){
        zoomCamera(value);
    }else if (name.equals(FLYCAM_ZoomOut)){
        zoomCamera(-value);
    }
}

protected void moveCamera(float value, boolean sideways){
    Vector3f vel = new Vector3f();
    Vector3f pos = cam.getLocation().clone();

    if (sideways){
        cam.getLeft(vel);
    }else{
        cam.getDirection(vel);
    }
    vel.multLocal(value * moveSpeed);
    pos.addLocal(vel);

    cam.setLocation(pos);
}

protected void rotateCamera(float value, Vector3f axis){
    Matrix3f mat = new Matrix3f();
    mat.fromAngleNormalAxis(rotationSpeed * value, axis);

    Vector3f up = cam.getUp();
    Vector3f left = cam.getLeft();
    Vector3f dir = cam.getDirection();

    mat.mult(up, up);
    mat.mult(left, left);
    mat.mult(dir, dir);

    Quaternion q = new Quaternion();
    q.fromAxes(left, up, dir);
    q.normalizeLocal();

    cam.setAxes(q);
}

protected void riseCamera(float value){
    Vector3f vel = new Vector3f(0, value * moveSpeed, 0);
    Vector3f pos = cam.getLocation().clone();

    pos.addLocal(vel);
    cam.setLocation(pos);
}

protected void zoomCamera(float value){
    // derive fovY value
    float h = cam.getFrustumTop();
    float w = cam.getFrustumRight();
    float aspect = w / h;

    float near = cam.getFrustumNear();

    float fovY = FastMath.atan(h / near)
              / (FastMath.DEG_TO_RAD * .5f);
    float newFovY = fovY + value * 0.1f * zoomSpeed;
    if (newFovY > 0f) {
        // Don't let the FOV go zero or negative.
        fovY = newFovY;
    }

    h = FastMath.tan( fovY * FastMath.DEG_TO_RAD * .5f) * near;
    w = h * aspect;

    cam.setFrustumTop(h);
    cam.setFrustumBottom(-h);
    cam.setFrustumLeft(-w);
    cam.setFrustumRight(w);
}

}
[/java]

Why don’t you simply extend the FlyByCamera class with the good bindings? Personnaly, for AZERTY need, I use this class :

[java]public class AzertyFlyByCamera extends FlyByCamera {

public AzertyFlyByCamera(Camera cam) {
	super(cam);
}

public void registerWithInput(InputManager inputManager){
    this.inputManager = inputManager;
    
    String[] mappings = new String[]{
        "FLYCAM_Left",
        "FLYCAM_Right",
        "FLYCAM_Up",
        "FLYCAM_Down",

        "FLYCAM_StrafeLeft",
        "FLYCAM_StrafeRight",
        "FLYCAM_Forward",
        "FLYCAM_Backward",

        "FLYCAM_ZoomIn",
        "FLYCAM_ZoomOut",
        "FLYCAM_RotateDrag",

        "FLYCAM_Rise",
        "FLYCAM_Lower"
    };

    // both mouse and button - rotation of cam
    inputManager.addMapping("FLYCAM_Left", new MouseAxisTrigger(MouseInput.AXIS_X, true),
                                           new KeyTrigger(KeyInput.KEY_LEFT));

    inputManager.addMapping("FLYCAM_Right", new MouseAxisTrigger(MouseInput.AXIS_X, false),
                                            new KeyTrigger(KeyInput.KEY_RIGHT));

    inputManager.addMapping("FLYCAM_Up", new MouseAxisTrigger(MouseInput.AXIS_Y, false),
                                         new KeyTrigger(KeyInput.KEY_UP));

    inputManager.addMapping("FLYCAM_Down", new MouseAxisTrigger(MouseInput.AXIS_Y, true),
                                           new KeyTrigger(KeyInput.KEY_DOWN));

    // mouse only - zoom in/out with wheel, and rotate drag
    inputManager.addMapping("FLYCAM_ZoomIn", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
    inputManager.addMapping("FLYCAM_ZoomOut", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));
    inputManager.addMapping("FLYCAM_RotateDrag", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));

    // keyboard only WASD for movement and WZ for rise/lower height
    inputManager.addMapping("FLYCAM_StrafeLeft", new KeyTrigger(KeyInput.KEY_Q));
    inputManager.addMapping("FLYCAM_StrafeRight", new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping("FLYCAM_Forward", new KeyTrigger(KeyInput.KEY_Z));
    inputManager.addMapping("FLYCAM_Backward", new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping("FLYCAM_Rise", new KeyTrigger(KeyInput.KEY_R));
    inputManager.addMapping("FLYCAM_Lower", new KeyTrigger(KeyInput.KEY_F));

    inputManager.addListener(this, mappings);
    inputManager.setCursorVisible(dragToRotate);

    Joystick[] joysticks = inputManager.getJoysticks();
    if (joysticks != null && joysticks.length > 0){
        Joystick joystick = joysticks[0];
        joystick.assignAxis("FLYCAM_StrafeRight", "FLYCAM_StrafeLeft", JoyInput.AXIS_POV_X);
        joystick.assignAxis("FLYCAM_Forward", "FLYCAM_Backward", JoyInput.AXIS_POV_Y);
        joystick.assignAxis("FLYCAM_Right", "FLYCAM_Left", joystick.getXAxisIndex());
        joystick.assignAxis("FLYCAM_Down", "FLYCAM_Up", joystick.getYAxisIndex());
    }
}

}[/java]