How to find angel from rotation towards a certain point

I am trying to figure out how an enemy should turn so that he is facing the player. The world is flat, so i can concentrate on only a plane approach so far. Only relevant rotation your be the one arround the y-axis in this case.

I have these situations:
Situation A:
[java]_______
| E | <= enemy
| / | <= enemy is looking exactly 45° down right
| P | <= player is approachin from bottom at roughly 30° to the view direction of enemy
-------[/java]
Situation B:
[java]_______
| E| <= enemy
| P / | <= enemy is looking exactly 45° down right
| | player is approachin from left at roughly 30° to the view direction of enemy
-------[/java]

I am trying to figure out the angle the enemy has to turn now. I tried several approaches but the results are somewhat strange.
The code i have to figure it out:

[java]
Quaternion currentRot = enemy.getLocalRotation();
Vector3f initialAxes = new Vector3f[3];
currentRot.toAxes(initialAxes);
Plane forward = new Plane(initialAxes[0], 0);
Plane sidewards = new Plane(initialAxes[2], 0);

        if (forward.whichSide(player.getLocalTranslation()) == Plane.Side.Positive) {
            System.out.println(&quot;Player is in front of enemy.&quot;);
        } else if (forward.whichSide(player.getLocalTranslation()) == Plane.Side.None) {
            System.out.println(&quot;Player is next to enemy.&quot;);
        } else {
            System.out.println(&quot;Player is behind of enemy.&quot;);
        }
        
        if (sidewards.whichSide(player.getLocalTranslation()) == Plane.Side.Negative) {
           System.out.println(&quot;Player is left of enemy.&quot;);
        } else if (sidewards.whichSide(player.getLocalTranslation()) == Plane.Side.None) {
            System.out.println(&quot;Player is next to enemy.&quot;);
        } else {
            System.out.println(&quot;Player right of enemy.&quot;);
        }
        
        Quaternion wantedRot = currentRot.clone();
        wantedRot.lookAt(player.getLocalTranslation(), Vector3f.UNIT_Y);
        
        float angle1 = currentRot.getY() - wantedRot.getY();
        System.out.println(&quot;Simple angle towards player: &quot; + angle1 * FastMath.RAD_TO_DEG);
        
        float angle2 = FastMath.acos(currentRot.dot(wantedRot));
        System.out.println(&quot;Dotproduct angle towards player: &quot; + angle2 * FastMath.RAD_TO_DEG);
        
        Quaternion delta = currentRot.inverse().mult(wantedRot);
        System.out.println(&quot;Deltaquaternion angle towards player: &quot; + delta.getY() * FastMath.RAD_TO_DEG);
        
        Vector3f enemyPosition = enemy.getLocalTranslation();
        Vector3f enemyForwardPoint = enemy.getLocalRotation().mult(new Vector3f(0, 0, 1));
        Vector3f playerPosition = player.getLocalTranslation();
        Vector3f viewDirection = enemyForwardPoint.subtract(enemyPosition).normalize();
        Vector3f playerDirection = playerPosition.subtract(enemyPosition).normalize();
        float angle3 = FastMath.acos(viewDirection.dot(playerDirection));
        System.out.println(&quot;Trigonometry angle towards player: &quot; + delta.getY() * FastMath.RAD_TO_DEG);

[/java]

In situation A the output is:

Player is in front of enemy. Player is left of enemy. Simple angle towards player: 39.604862 Dotproduct angle towards player: 99.04719 Deltaquaternion angle towards player: -56.582966 Trigonometry angle towards player: 23.150555

In situation B the output is:

Player is behind of enemy. Player is left of enemy. Simple angle towards player: 23.71655 Dotproduct angle towards player: 81.83965 Deltaquaternion angle towards player: -56.71563 Trigonometry angle towards player: 20.338633

First of all its strange that in B the planes are somewhat turned arround so now the player is seen behind and left wich is not true,
Then the calculated angles have me at a loss, in theory they all should give the same value, but each one of them is different. The simple one looks like the one that is the most realistic, altho it is not negative in situation B as i excpected.
Are my calculations wrong somehow ?
Is there an easy and simple way to determine the rotation from the current view to a certain point ?

What i need to know is if the player is left or right of enemy and at what angle so i can turn the enemy by that angle to face the player.
Also i need to know if the player is behind or in front of the enemy so in one case enemy can start walking forward, in other case walking backward and inverse rotation to turn.

As stated in the documentation, wantedRot.lookAt(player.getLocalTranslation(), Vector3f.UNIT_Y); computes the quaternion representing the direction player.getLocalTranslation().

That means you get the angle from the origin (0,0,0) to your player. NOT from the enemy to the player.

Additionally, you don’t need to set it as the currentRot.clone(), since it is totally overridden by lookAt(). a simple New Quaternion().lookAt() is sufficient.

Similarly, your detection planes are built at the origin (offset = 0) :

  • forward has its normal vector toward the enemy X axis (right)
  • sidewards has its normal vector toward the enemy Z axis (behind)

So… forward tests if the player is right from the origin and sidewards if the player is behind the origin…

1 Like

Ok, that explains why dotproduct and delta calculations are different from rest, but still the planes in situations are different even tho the enemy dos not move at all, so they should be the same, and the other two calculations should be same result (if i dont have some mistake in my math).

Thanks, that fixed the plane problem.
Seems to work right now:

[java] Quaternion currentRot = enemy.getLocalRotation();
Vector3f[] initialAxes = new Vector3f[3];
currentRot.toAxes(initialAxes);
Plane forward = new Plane();
forward.setOriginNormal(enemy.getLocalTranslation(), initialAxes[2]);
Plane sidewards = new Plane();
sidewards.setOriginNormal(enemy.getLocalTranslation(), initialAxes[0]);

        if (forward.whichSide(player.getLocalTranslation()) == Plane.Side.Positive) {
            System.out.println("Player is in front of enemy.");
        } else if (forward.whichSide(player.getLocalTranslation()) == Plane.Side.None) {
            System.out.println("Player is next to enemy.");
        } else {
            System.out.println("Player is behind of enemy.");
        }
        
        if (sidewards.whichSide(player.getLocalTranslation()) == Plane.Side.Positive) {
           System.out.println("Player is left of enemy.");
        } else if (sidewards.whichSide(player.getLocalTranslation()) == Plane.Side.None) {
            System.out.println("Player is next to enemy.");
        } else {
            System.out.println("Player right of enemy.");
        }[/java]

Still need the right angle from enemy view direction to player position.

The delta Y-angle should give you that. Positive to the right, Negative to the left. If I’m not wrong.

If i would calculate the delta right it would be that, true :wink:

Well, setting aside the math behind it i went the lazy way and used an empty helper node and works like a charm now.

[java]
Node helperNode = new Node(“ViewHelper”);
helperNode.setLocalTranslation(enemy.getLocalTranslation());
helperNode.lookAt(player.getLocalTranslation(), Vector3f.UNIT_Y);
Quaternion delta = enemy.getLocalRotation().inverse().multLocal(helperNode.getLocalRotation());

        System.out.println("Deltaquaternion angle towards player: " + delta.toAngles(null)[1] * FastMath.RAD_TO_DEG);

[/java]

Just not sure how performat that is if this is called each update cycle.

Just a note: the Plane stuff was unnecessary. A simple dot product with a direction vector and the target position would have told you the same thing.
[java]
Vector3f dir = rotation.mult( Vector3f.UNIT_Z );
float dot = dir.dot(position);
if( dot < 0 )
// behind
else
// in front[/java]

Also, similarly the dot can be used to tell left/right… even better you will get how far left or right since small dot means nearly in front of the player. A dot of +/- 1.0 means 90 degrees to the left or right.