Player Rotation and movement problems (with pictures for illustration)

Hi,

I am having quite a few problem with my player movement and rotation, but first the code:

[java]package mygame;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
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.ChaseCamera;
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.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

public class Main extends SimpleApplication implements ActionListener, AnimEventListener {

public static void main(String[] args) {
    Main app = new Main();
    app.start();
}
private Spatial gameLevel;
private Spatial player;
private Quaternion myQuaternion = new Quaternion(0f, 1f, 0f, 1f);

private BulletAppState bulletAppState;    
private BetterCharacterControl playerControl;
private ChaseCamera chaseCam;
private AnimChannel animChannel;
private AnimControl animControl;

private boolean left,right, up, down, jump, sprint, placeholder;
 
private Vector3f walkDirection = new Vector3f(0,0,0);
private float airTime = 0;
private Vector3f viewDirection = new Vector3f();

Node playerNode;




@Override
public void simpleInitApp() {

bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);

flyCam.setEnabled(false);
mouseInput.setCursorVisible(false);

    
initSunLight();
initAmbientLight();
initScene();
initPlayer();
initPlayerPhysics();
initChaseCam();
initControls();

}

    
    
@Override
public void simpleUpdate(float tpf) { 
    
  walkDirection.set(0,0,0);
    
if (up && !down){
  walkDirection.addLocal(1f, 0f, 0f);
 }
if (down && !up && !sprint){ walkDirection.addLocal(-0.5f, 0f, 0f);  
 }
if (up && sprint){
  walkDirection.addLocal(2f, 0f, 0f);
 }
if (left){
  playerControl.getViewDirection();
  playerControl.setViewDirection(myQuaternion.mult(Vector3f.UNIT_Z));     
 }
if (right){
  playerControl.setViewDirection(myQuaternion.mult(Vector3f.UNIT_Z.negate()));
 }
if (jump){
  playerControl.jump();
 }

/* if (!playerControl.isOnGround()) {
airTime += tpf;
} else {
airTime = 0;
}*/

playerControl.setWalkDirection(walkDirection.multLocal(5f));

}

@Override
public void simpleRender(RenderManager rm) {
    
}

private void initSunLight(){ 

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

private void initAmbientLight(){
     /** A white ambient light source. */ 
AmbientLight ambient = new AmbientLight();
ambient.setColor(ColorRGBA.White.mult(1.3f));
rootNode.addLight(ambient);    
}

private void initScene(){
    gameLevel = assetManager.loadModel("Scenes/Scene.j3o");        
    gameLevel.setLocalTranslation(0, -1, 0);
    gameLevel.addControl(new RigidBodyControl(0f));
    // 0 stands for the weight of the object (how fast it falls to the ground)
    rootNode.attachChild(gameLevel);
    bulletAppState.getPhysicsSpace().addAll(gameLevel);
}

private void initPlayer(){
//create player
player = assetManager.loadModel("Models/turtle/TURTLE_body.mesh.xml");
player.setMaterial(assetManager.loadMaterial("Materials/turtle.j3m"));
player.scale(1f);
player.rotate(0, FastMath.PI / 2 , 0);


playerNode = new Node();
playerNode.attachChild(player);

 animControl = player.getControl(AnimControl.class);
 animControl.addListener(this);
 animChannel = animControl.createChannel();
 animChannel.setAnim("idle");
 animChannel.setSpeed(1f);



}

private void initPlayerPhysics(){
  //Create Controls
playerControl = new BetterCharacterControl(0.26f, 0.5f, 20f); //contruct character collsion shape and weight
playerNode.addControl(playerControl); //attach Control to playerNode crated above
        playerControl.setJumpForce(new Vector3f(0, 20f, 0));
        playerControl.setGravity(new Vector3f(0, -10f, 0));
playerControl.warp(new Vector3f(0, 10, 10));
        bulletAppState.getPhysicsSpace().add(playerControl);
        bulletAppState.getPhysicsSpace().addAll(playerNode);
 rootNode.attachChild(playerNode);
}

private void initChaseCam(){
chaseCam = new ChaseCamera(cam, player, inputManager);
chaseCam.setSmoothMotion(true);
chaseCam.setTrailingEnabled(true);
chaseCam.setMinDistance(5f);
chaseCam.getHorizontalRotation();
}

private void initControls(){
 inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
 inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
 inputManager.addMapping("CharForward", new KeyTrigger(KeyInput.KEY_W));
 inputManager.addMapping("CharBackward", new KeyTrigger(KeyInput.KEY_S));
 inputManager.addMapping("CharJump", new KeyTrigger (KeyInput.KEY_SPACE));
 inputManager.addMapping("CharSprint", new KeyTrigger (KeyInput.KEY_LSHIFT));
 inputManager.addListener(this, "CharLeft", "CharRight");
 inputManager.addListener(this, "CharForward", "CharBackward");
 inputManager.addListener(this, "CharJump");
 inputManager.addListener(this, "CharSprint");
 
}


public void onAction(String name, boolean isPressed, float tpf) {
   if (name.equals("CharLeft")) {
        left = isPressed;
        
    } if (name.equals("CharRight")) {
        right = isPressed;
        
    } if (name.equals("CharForward")) {
        up = isPressed;
        
    } if (name.equals("CharBackward")) {
        down = isPressed;
        
    } if (name.equals("CharJump")){
        jump = isPressed;
        
    } if (name.equals("CharSprint")){
        sprint = isPressed;
        
    }
    
}


public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
    
}

public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
    //unused
}

}
[/java]

The problems:

Rotation problem:

When i press A or D the player “snaps”, or instantly rotates 90°, basically instantly looks to his left or to his right, but only the left or right, from his origin position. So pressing A two times has o effect.

Desired Result:

A continuous rotation which is dependant on the current viewDirection.

Movement Problem:

After the player rotated and I press W he still moves in the same direction.

I would like the player always to move in the viewDirection.

Terrain Adjustement Problem:

The player is a turtle (you have to imagine it basically like a Car). However, the player does not adjust to the terrain:

I would like the turtle to move up hills like this. (This should naturally apply to all rotations)

I hope all my problems are clear, and thank you a ton in advance for any hep.

Hmm, no clue why the Illustration don’t show.

Another try (the red arrow indicates the viewDirection) :

Rotation Problem:

Roation wanted:

Movement Problem:

Movement wanted:

Terrain Problem:

Terrain wanted:

Edit: Finally. A preview function would be nice :wink:

About left-right rotations. You can check this demo which has such rotation http://hub.jmonkeyengine.org/forum/topic/ai-demo-mifth/

  1. Apply the rotation gradually, use tpf to do that
  2. Just set the viewDirection and walkDirection to the same direction
  3. Set the gravity to rotate the player or simply attach the actual model to a Node that is controlled by the BetterCharacter and rotate the model instead

@mifth thank you very much.

Now the rotation and player walk works like a charm now.

@normen

How could I let gravity rotate the player?

You set it, as I said.

Okay, sorry to ask and bother you.

Just try what is said first instead of immediately posting another question. The question “How do I let gravity rotate the character” shows me that you didn’t even try to set the gravity because it rotates the character directly when you set it.

@normen said: Just try what is said first instead of immediately posting another question. The question "How do I let gravity rotate the character" shows me that you didn't even try to set the gravity because it rotates the character directly when you set it.

I can guess that he wants only rotate the chatacter according to plane normal, but leave the gravity in the same direction. But who knows what he wants. :slight_smile:

@normen said: Just try what is said first instead of immediately posting another question. The question "How do I let gravity rotate the character" shows me that you didn't even try to set the gravity because it rotates the character directly when you set it.

Immediately is slightly exaggerated. I’m just very new to the engine, and find it very difficult to translate your brieg answers into code. E.g. the only way I altered the gravity so far was with .setGravity(new Vector3f(…); and chaing the values does not rotate may player. But I assume that’s not what you meant. Therefore I asked.

And thinking about it, that what @normen said would be the perfect solution. Sadly no clue how to do that.

@mifth said: I can guess that he wants only rotate the chatacter according to plane normal, but leave the gravity in the same direction. But who knows what he wants. :)

In this case it wouldn’t matter much as it would be perpendicular to the terrain tangent anyway. But I gave an example for not changing the gravity as well.

Okay I’m sorry. Would it be possible that you elaborate your thought a bit, or maybe even post a tiny example, because I don’t know how to change the gravity in a way it adapts to the terrain’s normals.

Furthermore if I change the values here:

playerControl.setGravity(new Vector3f(0f, -10f, 0f));

the rotation of the player does not change.

It would be very kind if you could explain how you would do it thoroughly.

Well your gravity vector still points down… So the direction of the gravity won’t change. I suppose somebody posted this already, but check again if you really get what a direction vector or a gravity vector is or means, its a vector that points in the direction of the gravity and its length sets the force of the gravity…
https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:math_for_dummies

I think my previous post was slightly unfortunate. Normally I have so the vector points downwards on the y-axis that obvious,

playerControl.setGravity(new Vector3f(0, -10f, 0f));

but even if I change the values to smth like

(-5f, -10f, 0f)

the rotation of the player does not change, even though the direction vector now is diagonal and does not point directly down.

Maybe you set the gravity before you add the object to the physics space. Look at the TestBetterCharacter example, it shows how the character can walk on a sphere with constantly changing gravity and always perpendicular to the sphere tangent.

I couldn’t quite manage to get the gravity thing done, so I attached the model to a Node and now try to rotate the model according to the terrain normals. I tried to “attach” a ray to the player and cast it directly downwards to give me the normals of the terrain. However, when I move around with the character the normal value doesn’t change even though the terrain is uneven. Why?

[java]
public void simpleUpdate(float tpf) {

CollisionResults results = new CollisionResults();

    Ray ray = new Ray();
    Vector3f pos = new Vector3f(playerNode.getLocalTranslation().add(0f, 1f, 0f));
    Vector3f dir = new Vector3f(0f, -1f, 0f);
    dir.subtractLocal(pos).normalizeLocal();
    ray.setOrigin(pos);
    ray.setDirection(dir);
    terrain.collideWith(ray, results);
    Vector3f normals = results.getClosestCollision().getContactNormal();
    System.out.println(normals);

}
[/java]

Your direction vector, why do you do what you do in line 8?

I got the code from this thread and adapted it for me.

http://hub.jmonkeyengine.org/forum/topic/get-normal-vector-from-terrain/

Thinking about it, normalising the Vector seems strange.

Removing that line fixed that issue.

However now there is a new one.

[java]private void getTerrainNormal(){
CollisionResults results = new CollisionResults();

    Ray ray = new Ray();
    Vector3f pos = new Vector3f(playerNode.getLocalTranslation().add(0f, 1f, 0f));
    Vector3f dir = new Vector3f(0f, -1f, 0f);
    dir.subtractLocal(pos).normalizeLocal();
    ray.setOrigin(pos);
    ray.setDirection(dir);
    terrain.collideWith(ray, results); 
    Vector3f normals = results.getClosestCollision().getContactNormal();
    
    
    
    if ((rotate.x) != (normals.x)){
        rotate = normals;            
        player.rotate(rotate.x, 0f, rotate.z);
        System.out.println(rotate);
        
    }[/java] 

When I rotate the player accorign the normal vector, he rotates like to much, or not even in the correct direction, very hard to tell.

rotate() takes radians of rotation about the x, y, and z axes… this has nothing to do with direction vectors. So:
player.rotate(rotate.x, 0f, rotate.z);

…is complete nonsense, really.

Also, regarding this mess:
[java]
Vector3f pos = new Vector3f(playerNode.getLocalTranslation().add(0f, 1f, 0f));
Vector3f dir = new Vector3f(0f, -1f, 0f);
dir.subtractLocal(pos).normalizeLocal();
[/java]

You’ve completely misunderstood the code you cut-pasted and then incorrectly modified it. Granted, the original code was kind of confusing since it did extra work to create a direction vector that it didn’t need to do.

Remove this line:
dir.subtractLocal(pos).normalizeLocal();

…it is entirely unnecessary.