[SOLVED] Anything that I can do to increase performance?

It looks like you’re using a lot of extra and over complicated code for aiming; I’m not sure where the error is exactly but I imagine it has to do with an improper integer value being returned by your method.

Although you could definitely simplify your code using just the built-in functions for Vector math, and eliminate the extra math so it’s easier to debug.

I would personally do something like this to make the NPC look at the nearest player. (unless there’s some other reason you need to have the look direction in the form of an int)

public Vector3f aimForNearestPlayer(List<Player> players) {
        Vector3f clC = null;
        float cDist = Float.MAX_VALUE;
        for(Player p : players) {
            float cD = p.position.distance(this.spear.getWorldTransform().getTranslation());
            if (cD < cDist && p != this) {
                cDist = cD;
                clC = p.position;
            }
        }
        System.out.println(clC);
        if (clC == null) return this.rot; // in this case, rot is a Vector3f that represents current lookDirection rather than an int
        
        Node playerNode = player.getNode();
        playerNode .lookAt(clC, Vector3f.UNIT_Y);  

       rot = cLc; // store the last direction that the player looked
        return rot;
    }

Rather than creating a new node to extract the ViewDirection like you did in your example, I wold just apply that to the player’s node and cut out the integers.

I’m slightly confused:
clC stays exactly the same after playerNode .lookAt(clC, Vector3f.UNIT_Y);
And here’s how I use rot (Note: the returned rot from the aimForNEarestPlayer is put in newRot)

if (newRot != rot) {
            int jump = 13;
            float pos_rot = (360 + newRot - rot) % 360;

            if (pos_rot < 180)  {
                rot += jump;
                pos_rot = (360 + newRot - rot) % 360;
                if (pos_rot > 180) rot = newRot;
            }
            else {
                rot -= jump;
                pos_rot = (360 + newRot - rot) % 360;
                if (pos_rot < 180) rot = newRot;
            }
            this.rotation = new Quaternion().fromAngleAxis(rot*FastMath.DEG_TO_RAD, Vector3f.UNIT_Y);
        }

I thought that clC is the location of the target NPC that the player is aiming towards. In which case the value wouldn’t matter after you call playerNode .lookAt(clC, Vector3f.UNIT_Y); since that method would rotate your player model for you, in which case the method could actually be void, and you could instead add a simple .getRotation() method that just returns rotwhen you need it

But everyone codes with different preferences, so if your method of using integers to store the rotation makes more sense, then I don’t want to confuse you trying to make you do things the way I would. I personally avoid using the toAngles() and fromAngles() methods unless I absolutely need to since I’m a bad mathematician and that adds room for extra logical errors.

Regardless, youraimForNearestPlayer() method looks like it is fine either way since you are using the lookAt() method properly. So it must have to do with a logical error when you convert the rotation to angles and back.

Am I right that you are just turning left or right by 13 degrees at a time depending on the direction of the other ‘thing’?

That’s the way I’m reading the code.

If so then there are WAY easier ways to do that and leave angles completely out of it. They are just an expensive source of error that is typically 100% unnecessary. The only thing I usually use angles for is displaying things to the user (or keeping track of Quake-camera style mouse input.)

Yes, you’re right. What easier ways are you thinking of though?

If my guess is right and you just want to rotate left or right as some other object is left or right then the summary is:
-do a dot product with the left vector of your rotating object and the relative normalized position vector of the target object.
-if the dot product is positive, rotate left by a fixed amount (to emulate what it looks like you’re doing), if positive the rotate right.

To rotate by a fixed amount, just make some Quaternions for those rotations and just multiply them by the existing rotation… ie: call spatial.rotate(quat13) where quat13 = new Quaternion().fromAngles(0, 13 degrees as radians, 0)

But the nice thing about this approach is that you can scale the rotation by the dot product which means your rotating object will eventually zero in on the target exactly instead of flopping back and forth past it or whatever.

What I’m gathering from this:

    float dp = position.dot(clC);
    if (dp > 0) {
        rot.addLocal(STEP);
    } else {
        rot.subtractLocal(STEP);
    }

Is this correct?
Edit: It seems to be wrong, because as soon as I add the step quaternion, my model flies up off the screen and splits apart. Is there something else I should be doing?
Edit 3: rot.multLocal doesn’t go crazy anymore, but I still have almost no idea about how to use quaternions.

jME Math for Dummies

I’m sorry… The real source of confusion for me seems to be the dot product. It seems to work OK if I use quaternion.dot(otherQuaternion);, but I still have to deal with the ‘seam’ at the 0 or 360 degree points where the player spins around once before heading in the desired direction.

Dotting quaternions makes no sense.

You dot the vectors.

Vector3f target = …
Vector3f yourLocation = …
Vector3f leftDir = …

Vector3f relativeLoc = target.subtract(yourLocation).normalizeLocal();
float steerDot = leftDir.dot(relativeLoc);

If steerDot < 0 turn left.
If steerDot > 0 turn right,
if steerDot is very close to 0… do nothing.

If you want to deal with things behind you then dot the forward vector, too… and deal with the case of objects directly behind you.

float forwardDot = dir.dot(relativeLoc);
if forwardDot < 0 && steerDot is very close to 0, turn randomly left or right

…or, use forwardDot to ‘bloom’ your steerDot like:

if( forwardDot < 0 ) {
    if( Math.abs(steerDot) < 0.0001 ) {
        steerDot = 1; // arbitrarily turn right if it's 180 degrees behind us
    } else {
        steerDot = FastMath.sign(steerDot); // max left or right
    }
}

For turning… assume you have some predefined quaternions:
Quaternion rotLeft = new Quaternion().fromAngles(0, FastMath.DEG_TO_RAD * 13, 0);
Quaternion rotRight = new Quaternion().fromAngles(0, FastMath.DEG_TO_RAD * -13, 0);
if it’s a spatial:
spatial.rotate(rotLeft);
or
spatial.rotate(rotRight);

…if for some reason you are just keeping some rotation quaternion:
myRotation.multLocal(rotLeft);
or
myRotation.multLocal(rotRight);

If you get all of that working then we can talk about rotating only part of rotLeft or rotRight depending on steerDot.

Edit:
A dot product may feel a little magic but it’s worth knowing what it does. In this case, for two normalized vectors it’s giving you the cosine of the angle between them… or thought of another way the projection of one vector along the other. If the vectors are parallel then cosine = 1 if the vectors are orthogonal then cosine = 0. If the vectors are 45 degrees from each other then cosine = cos(QUARTER_PI) and so on.

1 Like

The reason I was using two quaternions was because of my player rotate method.

Vector2f mp = Main.inputManager_.getCursorPosition();
float xDiff = (Main.settings_.getWidth() / 2) - mp.x;
float yDiff = mp.y - (Main.settings_.getHeight() / 2);
Main.gl.player.quatHeading = new Quaternion().fromAngleAxis((float) Math.atan2(xDiff, yDiff), Vector3f.UNIT_Y);

(Yes, I haven’t gone through and converted the inputManager_ etc… to getInputManager() yet)
I tried using the coordinates as Main.gl.player.vecHeading = new Vector3f(xDiff, 0, yDiff).add(Main.gl.player.position); but that did’nt work very well.

Also, what is leftDir? I understand the target and your location, but I really don’t understand where you get this.

leftDir is the x unit vector in whatever orientation you are. I have trouble following your code so I have no idea what you start with… but presuming you have some current rotation as a Quaternion then the leftDir is yourRotation.mult(Vector3f.UNIT_X).

If this were your head, leftDir is the unit vector that points out your left ear.

In case it matters, frontDir would be yourRotation.mult(Vector3f.UNIT_Z).

1 Like

@pspeed The player works fine now. But the NPCs have a problem.
The player gets it’s position from the mouse,
A (hopefully) easy version of what I use to rotate the player.

Vector2f mousePosition = Main.inputManager_.getCursorPosition();
float centeredX = (Main.settings_.getWidth() / 2) - mousePosition.x;
float centeredY = mousePosition.y - (Main.settings_.getHeight() / 2);
playerTarget = new Vector3f(centeredX, 0, centeredY);

Here is what the NPC uses.

public void aimForNearestPlayer(List<Player> players) {
    Vector3f clC = null;
    float cDist = Float.MAX_VALUE;
    for(Player p : players) {
        float cD = p.position.distance(this.spear.getWorldTransform().getTranslation());
        if (cD < cDist && p != this) {
            cDist = cD;
            clC = p.position;
        }
    }
    if (clC != null) heading = clC;
}

What this does is sets the target location(heading) to the location of the nearest player. There were no other players in my test, and yet it still did this:

a) is spear a Spatial? If so you do a lot of work to get distance.

b) I don’t know what you do with heading so I can’t say any more what might be wrong. But… it seems like you are aiming using one vector but moving using another.

A) Yes what’s a better way?(Im assuming you know one) Spear is the pointy thing on the front of both players.
B) heading = target. PlayerTarget = target. I just have it labeled heading in my code.

The code I am using in the player class to rotate(NPCs and the actual player both extend this)

    Vector3f leftDir = rot.mult(Vector3f.UNIT_X);
    Vector3f relativeLoc = heading.subtract(position).normalizeLocal();
    float dp = leftDir.dot(relativeLoc);
    if (dp > 0) {
        rot.multLocal(STEP);
    } else if (dp < 0){
        rot.multLocal(STEPrev);
    }

At the beginning of the class:

public static final Quaternion STEP = new Quaternion().fromAngles(0f, 13f * FastMath.DEG_TO_RAD, 0f), STEPrev = new Quaternion().fromAngles(0f, -13f * FastMath.DEG_TO_RAD, 0f);

I found something else:
If the mouse is very close too the player, the player moves in a slow circle. Could this be part of the reason why the NPC rotation was doing that?

What is “rot” in this context?

Is the pointy spear thing forward in the z axis of the NPC?

If spear is a spatial then:
p.position.distance(this.spear.getWorldTranslation());

There are probably two reasons for this… one that you are probably checking the dot against exactly 0 instead of “super close to 0”… it will almost never be exactly 0. Two that your player can only turn in 13 degree increments and so will probably just keeping steering slowly past it. Like maybe it never turns enough to turn all the way there before driving past it.

If Z is the red line in the scene composer then yes.

I need to see the code for how you calculate the rot, etc for the NPC, how you move it forward, etc… There is a mismatch there. Like it’s steering with the wrong orientation or moving with the wrong orientation.

Just in case, I think you should be doing all of these calculations with the “ball” part of the NPC and not the “pointy” thing. It removes a lot of ambiguity.