[SOLVED] Player can instantly rotate 180 degrees, bypassing speed regulation

Players in my game are supposed to be able to move at a constant speed, no matter what their heading is set to. The current rotation code works, except when the heading is directly behind the player, in which case, it instantly flips around.

public void rotate() {
    if (heading != null) {
        Vector3f leftDir = rot.mult(Vector3f.UNIT_X);
        Vector3f relativeLoc = heading.subtract(position).normalizeLocal();
        float dp = leftDir.dot(/*new Quaternion().fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y).mult(*/relativeLoc);
        if (dp > STEP.getY()) {
            rot.multLocal(STEP);
        } else if (dp < -STEP.getY()){
            rot.multLocal(STEPrev);
        } else {
            Quaternion nr = node.getLocalRotation();
            node.lookAt(heading, Vector3f.UNIT_Y);
            rot = node.getLocalRotation();
            node.setLocalRotation(nr);
        }
    }
}

I know this has to do with the else branch being triggered, but how can I stop it from being triggered when the player is 180° degrees around? is there a distance check I should be doing? or should I make the first if something like if (dp + 0.01f > STEP.getY()) to fix this? Any better suggestions?

What is that if/else block even trying to do?

You want to go left or you want to go right? Why is there even a third option?

Also, is STEP a quaternion? Why are you comparing it to a magic y value?

  1. If the dot product > the rotation step (STEP.getY()) go in that direction
    The other if is the same thing. The reason it needs to be more than the step value is so the player dosen’t effectively ‘vibrate’, because it’s moving too far one way, then the other.

  2. The else branch just sets the player’s rotation to point where you are aiming.

  3. The magic y value is the rotation.

Is STEP a quaternion?

The general logic should be:
-if it’s to the left of me, turn left
-if it’s to the right of me, turn right
-if it’s absolutely directly behind me, turn left (it doesn’t materially matter which you pick here)

And the reason you wobble back and forth is because you don’t scale the amount of turn by the amount of left/right (ie: the left right dot)

as in

        if (dp > /*STEP.getY()*/0) {
            rot.multLocal(STEP.mult(dp));
        } else {//if (dp < /*-STEP.getY()*/0){
            rot.multLocal(STEPrev.mult(dp));
        }/* else {
            Quaternion nr = node.getLocalRotation();
            node.lookAt(heading, Vector3f.UNIT_Y);
            rot = node.getLocalRotation();
            node.setLocalRotation(nr);
        }*/

Well, you also need to do a dot product with the forward direction so you know if the thing is in front or behind you. Only if it’s in front of you do you do the code you have… or you can prenormalized the dp.

Like, if the forward dot says it’s behind you (<0) then use the sign of the dp… unless dp is 0 then just use 1 or -1.

if( fwdDot < 0 ) {
dp = FastMath.sign(dp);
if( dp == 0 ) dp = 1;
}
…then your code.

If so you are treating its values as if they aren’t magic black-box values… which could be dangerous since they are magic 4 dimensional values that probably do not mean what you think they mean.

I’ve so far been assuming that STEP is a Vector3f.

Ok, this looks like a MUCH better solution then the crazy ting I put together a few hours ago…
What I had checked the distance between two vectors multiplied by direction quaternions. Bad.

public boolean vectorDistCheck(Quaternion q1, Quaternion q2) {
    Vector3f vector1 = q1.multLocal(new Vector3f(0, 0, 1));
    Vector3f vector2 = q2.multLocal(new Vector3f(0, 0, 1));
    float dist = vector1.distance(vector2);
    return dist > 1;
}

It would only equal one if you moved much more than you were supposed to.