First-Person Perspective

Hello,



I’ve just tried out the tutorial where you control a character in first-person and let him collide with the environment.



Some problems I have encountered are:



-When you jump and you are in the air, you can sometimes jump again if you hit space quick enough.

-You can flip over the camera, making your character turn upside down.

-You can move while in air.

-When your camera looks down and you move backward, you start flying.



How can I fix these problems?

1 - This is sometimes caused by jumping while facing down (due to you kind of jumping into the ground). In the SetViewDirection, remove anything which moves it in the Y axis, see if that helps

2 - Restrict the camera movement, there are a few ways. The easiest way i found is to create your own camera, copy and paste the FlyByCamera code, and change it.



My rotateCamera method looks like this:



[java] private void rotateCamera(float value, Vector3f axis) {



if (dragToRotate) {

if (!canRotate) {

return;

}

}



Quaternion rotateAmount = new Quaternion().fromAngleNormalAxis(ROTATION_SPEED * value, axis);

Quaternion origRotation = cam.getRotation().clone();



if (axis.equals(initialUpVec)) {

cam.setRotation(rotateAmount.multLocal(cam.getRotation()).normalizeLocal());

} else {

cam.setRotation(cam.getRotation().mult(rotateAmount).normalizeLocal());

}



boolean camUpsideDown = cam.getUp().getY() < 0;



if (camUpsideDown) {

cam.setRotation(rotation); //reset rotation back

}

}[/java]



But i’ve modified it quite a bit, and theres some decencies on other parts of my code, but it should give u an idea.



3 - Check if character.isOnGround returns false, and then don’t allow movement

4 - Remove the flycam (although #1 should fix this as well)

Is this the newst FlyByCamera code or is there a newer one:



http://code.google.com/p/jmonkeyengine/source/browse/branches/jme3/src/core/com/jme3/input/FlyByCamera.java?r=5416

you would want this one:



http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/core/com/jme3/input/FlyByCamera.java



or just Ctrl + Shift + B on a FlyByCamera class in the JMEDK and it will take you to the source of it

if you use their logic then use this:

[java]

protected void rotateCamera(float value, Vector3f axis){

if (dragToRotate){

if (canRotate){

// value = -value;

}else{

return;

}

}



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();



Quaternion origRotation = cam.getRotation().clone();



cam.setAxes(q);



boolean camUpsideDown = cam.getUp().getY() < 0;



if (camUpsideDown) {

cam.setRotation(rotation); //reset rotation back

}

}

[/java]



also don’t forget to disable the old one and register the new one with the inputListener like the flycamappstate does



http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/core/com/jme3/app/FlyCamAppState.java

How do I replace the FlyCamAppState with my custom one? The FlyCamAppState still has a reference to the old flyCam.

Ok if you wanna use your own app state, then in the super constructor of your SimpleApplication



[java]

public class MySimpleApplication extends SimpleApplication {



public MySimpleApplication() {

super (new StatsAppState(), new YourFlyCamAppState());

}

}

[/java]



or just choose the app states you want in the super constructor, and attach your flycamappstate, or flycam + register with inputManager in simpleInitApp

I don’t get why my cursor isn’t bound to the new cam. I can move it out of the game. This was not possible with the old flycam.

It is registered with the inputManager and all.

can you show us how you are doing it, did you create your own appstate?

In my SimpleInitApp:



[java] FlipLockedFlyByCamera newFlyCam = new FlipLockedFlyByCamera(this.cam); //This extends FlyByCam and override the rotate method

newFlyCam.setEnabled(true);

newFlyCam.setDragToRotate(this.flyCam.isDragToRotate());

newFlyCam.setRotationSpeed(this.flyCam.getRotationSpeed());

this.flyCam.setEnabled(false);

this.flyCam = newFlyCam;

this.flyCam.registerWithInput(this.inputManager);

this.flyCam.setMoveSpeed(100); [/java]

remove any reference to flyCam for now. And remove the FlyCamAppState (by omitting it from the super Constructor)



Try this constructor:

[java]

public class MySimpleApplication extends SimpleApplication {



public MySimpleApplication() {

super (new StatsAppState());

}

}

[/java]



and in simple init

[java]FlipLockedFlyByCamera newFlyCam = new FlipLockedFlyByCamera(this.cam);

newFlyCam.registerWithInput(this.inputManager);

newFlyCam.setMoveSpeed(100); [/java]

It gives me a Nullpointer if I do that (calling the super constructor without a FlyCamAppState).

where does it give you the null? are you using flycam anywhere still?

Forgot to remove all references to it. Now it works.

The movement is broken now though. When I do this.cam.setLocation(player.getPhysicsLocation());, it doesn’t work anymore.

Can I somehow debug and see why a CharacterControl does not react to moving/jumping (a call to its jump method for example is ignored as well).

This is my game code:



[java]public class MyGame extends SimpleApplication implements ActionListener {



private static final String VERSION = “0.1.0”;



private boolean walkLeft = false, walkRight = false, walkUp = false, walkDown = false;

private CharacterControl player;



public static void main(String[] args) {

MyGame app = new MyGame();



//Set game title

AppSettings gameSettings = new AppSettings(true);

gameSettings.setTitle(VERSION);



gameSettings.setResolution(1280, 720);

gameSettings.setBitsPerPixel(16);

gameSettings.setVSync(false);

gameSettings.setSamples(0);

app.setSettings(gameSettings);



app.setShowSettings(false);



app.start();

}



public MyGame() {

super(new StatsAppState(), new FlipLockedFlyCamAppState(), new DebugKeysAppState());

}



@Override

public void simpleInitApp() {

BulletAppState physicsCalculator = new BulletAppState();

this.stateManager.attach(physicsCalculator);



this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));

if (stateManager.getState(FlipLockedFlyCamAppState.class) != null) {

this.flyCam = new FlipLockedFlyByCamera(cam);

this.flyCam.setMoveSpeed(1f); // odd to set this here but it did it before

stateManager.getState(FlipLockedFlyCamAppState.class).setCamera( (FlipLockedFlyByCamera)flyCam );

}

this.flyCam.setMoveSpeed(100);



this.initKeys();



DirectionalLight sunlight = new DirectionalLight();

sunlight.setColor(ColorRGBA.White);

sunlight.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());

this.rootNode.addLight(sunlight);



Spatial startMap = this.assetManager.loadModel(“Scenes/farm.j3o”);



CollisionShape startMapCollisionShape = CollisionShapeFactory.createMeshShape(startMap);

RigidBodyControl landScape = new RigidBodyControl(startMapCollisionShape, 0);

startMap.addControl(landScape);



CapsuleCollisionShape playerCollisionShape = new CapsuleCollisionShape(1.5f, 6f, 1);

this.player = new CharacterControl(playerCollisionShape, 0.05f);

this.player.setJumpSpeed(17.5f);

this.player.setFallSpeed(30);

this.player.setGravity(50);

this.player.setPhysicsLocation(new Vector3f(10, 10, 10));



this.rootNode.attachChild(startMap);

physicsCalculator.getPhysicsSpace().add(landScape);

physicsCalculator.getPhysicsSpace().add(player);

}



@Override

public void simpleUpdate(float tpf) {

Vector3f camDir = cam.getDirection().clone().multLocal(0.5f);

Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);

Vector3f walkDirection = new Vector3f(0, 0, 0);

if (this.walkLeft) { walkDirection.addLocal(camLeft); }

if (this.walkRight) { walkDirection.addLocal(camLeft.negate()); }

if (this.walkUp) { walkDirection.addLocal(camDir); }

if (this.walkDown) { walkDirection.addLocal(camDir.negate()); }

walkDirection.setY(0f);

this.player.setWalkDirection(walkDirection);

this.cam.setLocation(player.getPhysicsLocation());

}



private void initKeys() {

this.inputManager.addMapping(“Left”, new KeyTrigger(KeyInput.KEY_A));

this.inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_D));

this.inputManager.addMapping(“Up”, new KeyTrigger(KeyInput.KEY_W));

this.inputManager.addMapping(“Down”, new KeyTrigger(KeyInput.KEY_S));

this.inputManager.addMapping(“Jump”, new KeyTrigger(KeyInput.KEY_SPACE));

this.inputManager.addListener(this, new String[] {“Left”, “Right”, “Up”, “Down”, “Jump”});

}



@Override

public void onAction(String name, boolean isPressed, float tpf) {

if (name.equals(“Jump”))

this.player.jump();

else if (name.equals(“Left”))

this.walkLeft = isPressed && this.player.onGround();

else if (name.equals(“Right”))

this.walkRight = isPressed && this.player.onGround();

else if (name.equals(“Up”))

this.walkUp = isPressed && this.player.onGround();

else if (name.equals(“Down”))

this.walkDown = isPressed && this.player.onGround();

}

}[/java]



My CharacterControl doesn’t react. Can anyone confirm this? If so, why? You can remove the custom camera. I don’t think the camera causes it.

In order to fix the looking down and flying problem, change this line:



[java]Vector3f camDir = cam.getDirection().clone().multLocal(0.5f);[/java]



To this:



[java]

Vector3f camDir = new Vector3f(cam.getDirection.getX(), 0, cam.getDirection().getZ()).normalizeLocal().multLocal(0.5f);

[/java]



And remove this:



[java]

walkDirection.setY(0);

[/java]



The way you currently have will make the player travel much slower when looking up more or less. In other words, the straighter your look axis was to having Y = 0 would cause your player to move faster. Anything higher/lower would result in slower movement.



As for the CharacterControl not reacting, try the following:



First, place a System.out.println(“Pressing W”); in the onAction method where it detects if the W key is pressed. That makes sure you’ve set up input correctly. If you see it outputting “Pressing W” in the output, then your input is set up right.



If that succeeds, add System.out.println(walkUp); line to the update loop and make sure you’re correctly making each boolean true/false. You might have problems there. If this succeeds, then you’re getting all the right boolean stuff.



If this succeeds, have it output your walkDirection at the end of each execution of simpleUpdate(float tpf). If the walkDirection is (0, 0, 0) then you’re failing to add the directions somehow.



^Ghetto debugging. :smiley:



If that doesn’t help I really don’t know. It looks like proper code, really.