Player Rotation and movement problems (with pictures for illustration)

I had the intention to remove it, but somewhy forgot. By your reaction I feel like I have to apologise for a mistake.

But in which way have i misunderstood the code (naturally apart from the vector to rotate, which truly does not make sense at all which is why I asked in the first place. I didn’t know how to convert that vector into a rotation or smth like that)

[java]Vector3f pos = new Vector3f(playerNode.getLocalTranslation().add(0f, 1f, 0f));[/java]

I want the starting point of the ray to be the playerNode and add 1f on the y-axis because otherwise it sometimes is in the ground.

[java]Vector3f dir = new Vector3f(0f, -1f, 0f);[/java]

And the direction should go directly downwards.

[java]terrain.collideWith(ray, results);
Vector3f normals = results.getClosestCollision().getContactNormal();[/java]

Here I wanted to check for collision with the terrain node and get the normal.

So what did I misunderstand? And if you could explain how I could use the normal vector to rotate my player so it faces the same direction I would be very grateful.

Well, if you do not want to use normen’s earlier advice of setting the view direction to the walk direction then a quick trip into the javadocs could have helped:
http://hub.jmonkeyengine.org/javadoc/com/jme3/scene/Spatial.html#rotateUpTo(com.jme3.math.Vector3f)

The player rotation when pressing A or D works fine, I’m working on the third problem, I want player model to adjust according to the terrain, for example when moving up a hill. Normen’s suggestion was to rotate the player model, which is what I am struggeling to do.

@sumie said: The player rotation when pressing A or D works fine, I'm working on the third problem, I want player model to adjust according to the terrain, for example when moving up a hill. Normen's suggestion was to rotate the player model, which is what I am struggeling to do.
@pspeed said: Well, if you do not want to use normen's earlier advice of setting the view direction to the walk direction then a quick trip into the javadocs could have helped: http://hub.jmonkeyengine.org/javadoc/com/jme3/scene/Spatial.html#rotateUpTo(com.jme3.math.Vector3f)

If it wasn’t clear:

http://hub.jmonkeyengine.org/javadoc/com/jme3/scene/Spatial.html#rotateUpTo(com.jme3.math.Vector3f)

Or:

http://hub.jmonkeyengine.org/javadoc/com/jme3/scene/Spatial.html#rotateUpTo(com.jme3.math.Vector3f)

Or maybe:

http://hub.jmonkeyengine.org/javadoc/com/jme3/scene/Spatial.html#rotateUpTo(com.jme3.math.Vector3f)

:wink:

@sumie said: The player rotation when pressing A or D works fine, I'm working on the third problem, I want player model to adjust according to the terrain, for example when moving up a hill. Normen's suggestion was to rotate the player model, which is what I am struggeling to do.

Use direction vectors, I’m sure you read the tutorial by now, so I remind you that said its much easier to handle direction vectors than rotations. “Rotating the model” doesn’t necessarily translate to “use the rotate method”…
https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:math_for_dummies

rotateUpTo gives some weird results. If you can’t explain in a way an idiot understands it, forget it. Then i’ll just make a completely even map, will never get this done. I give up. :cry:

@sumie said: rotateUpTo gives some weird results. If you can't explain in a way an idiot understands it, forget it. Then i'll just make a completely even map, will never get this done. I give up. :cry:

I’m trying to see the code you have but I can’t see it from here. My far sight vision must be faulty… and the X-ray vision has been on the fritz for some time.

We can’t help you with code that gives weird results unless we see it. Good luck with your game.

Good point. Well its far from being a game yet.

[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.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
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.Ray;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.CartoonEdgeFilter;
import com.jme3.post.filters.LightScatteringFilter;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.shadow.CompareMode;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.EdgeFilteringMode;
import com.jme3.system.AppSettings;

public class Main extends SimpleApplication implements ActionListener, AnimEventListener {

public static void main(String[] args) {
    Main app = new Main();
    AppSettings settings = new AppSettings(true);
    settings.setFrameRate(60);
    settings.setTitle("Turtle Race");
    app.setSettings(settings);
    app.start();
}

private FilterPostProcessor fpp;
private Spatial gameLevel;
private Spatial player;
private BulletAppState bulletAppState;
private BetterCharacterControl playerControl;
private ChaseCamera chaseCam;
private AnimChannel animChannel;
private AnimControl animControl;
private Vector3f rotate = Vector3f.ZERO;
private boolean left, right, up, down, sprint;
private float rotateSpeed, moveSpeed, sprintSpeed, backMoveSpeed;
private DirectionalLight sun;
private Node playerNode;
private Node terrain;

@Override
public void simpleInitApp() {

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

    viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
    flyCam.setEnabled(false);
    mouseInput.setCursorVisible(false);


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


}

@Override
public void simpleUpdate(float tpf) {
    //initCameraCollision();
    
    getTerrainNormal();     
    
    
    
    moveSpeed = 55f * tpf;
    sprintSpeed = 130f * tpf;
    rotateSpeed = 35f * tpf;

    movePlayer();
    rotatePlayer();

    if (animChannel.getAnimationName().equals("walk") && !up) {
        animChannel.setAnim("idle");
        animChannel.setLoopMode(LoopMode.Loop);
    }
}

@Override
public void simpleRender(RenderManager rm) {
}

private void initSunLight() {...}

public void setupFilters() {...}
    
public void makeToonish(Spatial spatial) {...}       

private void initAmbientLight() {...}

private void initScene() {
    gameLevel = assetManager.loadModel("Scenes/Scene.j3o");
    gameLevel.setLocalTranslation(0, -1, 0);
    gameLevel.addControl(new RigidBodyControl(0f));
    gameLevel.setShadowMode(RenderQueue.ShadowMode.Receive);
    terrain = new Node("Terrain");
    terrain.attachChild(gameLevel);
    rootNode.attachChild(terrain);

    bulletAppState.getPhysicsSpace().addAll(gameLevel);
}

private void initPlayer() {
    //create player
    player = assetManager.loadModel("Models/turtle/TURTLE_body.mesh.j3o");
    player.setMaterial(assetManager.loadMaterial("Materials/turtle.j3m"));
    player.scale(1f);
    player.setShadowMode(ShadowMode.CastAndReceive);


    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.warp(new Vector3f(20, -5, 20));
    bulletAppState.getPhysicsSpace().add(playerControl);
    bulletAppState.getPhysicsSpace().addAll(playerNode);
    playerControl.setGravity(new Vector3f(0f, -10f, 0f));
    rootNode.attachChild(playerNode);
}

private void movePlayer() {

    stopPlayer();

    if ((up /*|| down*/) || (up && sprint)) {
        animChannel.setSpeed(2f);
        Vector3f walkDir = playerControl.getViewDirection().mult(moveSpeed);
        walkAnim();

        /*if (!up) {
         walkDir = playerControl.getViewDirection().mult(backMoveSpeed);
         walkDir.negateLocal();
            
         }*/

        if (sprint) {
            walkDir = playerControl.getViewDirection().mult(sprintSpeed);
            animChannel.setSpeed(3f);
        }

        playerControl.setWalkDirection(walkDir);
        System.out.println(walkDir);

    }
}

private void rotatePlayer() {



    if ((left && up) || (right && up)) {

        Quaternion rotQua = new Quaternion().fromAngleAxis(FastMath.DEG_TO_RAD * rotateSpeed, Vector3f.UNIT_Y);
        if (!left) {
            rotQua.inverseLocal();
        }

        rotQua = playerNode.getLocalRotation().mult(rotQua);

        playerControl.setViewDirection(rotQua.mult(Vector3f.UNIT_Z).normalizeLocal());
    }
}

private void stopPlayer() {
    playerControl.setWalkDirection(Vector3f.ZERO);
}

private void walkAnim() {
    if (!animChannel.getAnimationName().equals("walk") && up) {
        animChannel.setAnim("walk");
        animChannel.setSpeed(2f);
        animChannel.setLoopMode(LoopMode.Loop);
    }
}

private void initChaseCam() {
    chaseCam = new ChaseCamera(cam, playerNode, inputManager);
    chaseCam.setSmoothMotion(true);
    chaseCam.setTrailingEnabled(true);
    chaseCam.setMinDistance(3f);
    chaseCam.setDefaultDistance(15f);
    chaseCam.setTrailingSensitivity(10f);


}

private void initCameraCollision() {...}

private void getTerrainNormal(){
    //doesn't work (yet)
    
    CollisionResults results = new CollisionResults();

    Ray ray = new Ray();
    Vector3f pos = new Vector3f(playerNode.getWorldTranslation().add(0f, 1f, 0f));
    Vector3f dir = new Vector3f(0f, -1f, 0f);
    ray.setOrigin(pos);
    ray.setDirection(dir);
    terrain.collideWith(ray, results); 
    Vector3f normals = results.getClosestCollision().getContactNormal();
    
    
    
    if ((rotate.x) != (normals.x)){
        rotate = normals;
        player.rotateUpTo(rotate);
        
    }
    
   
    
    
    
    
}

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("CharSprint", new KeyTrigger(KeyInput.KEY_LSHIFT));
    inputManager.addListener(this, "CharLeft", "CharRight");
    inputManager.addListener(this, "CharForward", "CharBackward");
    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("CharSprint")) {
        sprint = isPressed;

    }

}

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

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

}
[/java]

@sumie said: If you can't explain in a way an idiot understands it, forget it.

I thought the tutorial was more or less that.

Other than the erroneous condition which will cause it to not update even if it might have changed:
[java]
if ((rotate.x) != (normals.x)){
rotate = normals;
player.rotateUpTo(rotate);

    }

[/java]

…should be fine. You may have to print the values or do some kind of other basic debugging to see what is strange about the value.

These are some of the values I receive.

Normals: (0.32011932, 0.9283304, -0.18901421) Player Rotation: (-0.1890378, 0.9465076, -0.010717265, 0.2612883) Normals: (0.19104286, 0.9618472, -0.19583844) Player Rotation: (-0.13676675, 0.9390928, 0.019236315, 0.31468654) Normals: (0.21992669, 0.96117073, -0.16668251) Player Rotation: (-0.13859054, 0.9029418, -0.014423348, 0.40654498) Normals: (0.20771185, 0.96380514, -0.16713937) Player Rotation: (-0.1338015, 0.89317936, -0.013438374, 0.42912176)

Can’t really tell if, or what is truly incorrect, besides not being the same values. The result is definitely not as desired: $

Did you take out the bogus if statement that would cause you random problems? Post the latest code with your debug prinlts so we don’t have to guess stuff like that.

Yep, took it out.

Code:

[java]
private void getTerrainNormal() {
//doesn’t work (yet)

    CollisionResults results = new CollisionResults();

    Ray ray = new Ray();
    Vector3f pos = new Vector3f(playerNode.getWorldTranslation().add(0f, 1f, 0f));
    Vector3f dir = new Vector3f(0f, -1f, 0f);
    ray.setOrigin(pos);
    ray.setDirection(dir);
    terrain.collideWith(ray, results);
    Vector3f normals = results.getClosestCollision().getContactNormal();

    System.out.println("Normals: " + normals);
    player.rotateUpTo(normals);
    Quaternion worldRotation = player.getWorldRotation();
    System.out.println("Player Rotation: " + worldRotation);



}[/java] 

And thats in the SimpleUpdate().

So, what could be the problem?

Two things you can look it… first instead of printing the world rotation print the local rotation to avoid picking up any parent rotations that there might be.

Second, you can also print the localRotation.mult(Vector3f.UNIT_Y) to see if it matches the (misnamed) ‘normals’ variable. That will at least narrow down where the problem is.

Your “pos” is always straight above the object, not above it in the direction of the terrain normal.

@normen said: Your "pos" is always straight above the object, not above it in the direction of the terrain normal.

I think that’s ok, though, since he wants the terrain normal where his feet are.

@pspeed said: I think that's ok, though, since he wants the terrain normal where his feet are.

Oh, did he say the model has its center at the feet? Didn’t follow the whole thread, sry.

@normen said: Oh, did he say the model has its center at the feet? Didn't follow the whole thread, sry.

That’s a good point. I only assumed myself.

It position is not at the player’s feet then you will need to project down by the distance to the feet: worldRotation.mult(new Vector3f(0, -distanceToFeet, 0)) to find the point that you are using getWorldRotation() for now.