danielcmessias
Senior Newbie


«
Posted
20130121 18:44:45 » 

Hi,
I expect a very simple answer, but beyond me. I've done some searching but can't find anything.
I want to rotate a sprite at a constant rate a tick so it will try to face another sprite.
I can calculate the angle between them, it's dealing with the rotation increment/decrement when the angle crosses the 360 degrees mark to 0 degrees, messes up my code and I just can't think through it.
Many thanks for any help!




Best Username Ever


«
Reply #1  Posted
20130121 19:02:48 » 

angle = cos ^{1}((a / a) dot (b / b)) That will give you an angle between 180 and 180. For the sake of brevity, I will assume you understand the vector math, but if not, feel free to ask. Using that number you can do something like this. 1 2 3 4 5 6 7 8 9 10 11 12
 if(Math.abs(angle) < turnSpeed) { thing.setDirection(Math.atan(thing.x  target.x, thing.y  target.y); } else if(angle > 0) { thing.rotate(turnSpeed); } else { thing.rotate(turnSpeed); } 




Sammidysam


«
Reply #2  Posted
20130121 19:19:34 » 

You are using two arguments in Math.atan. Might want to switch it to Math.atan2 (which is a better method by all means).




Games published by our own members! Check 'em out!


Agro


«
Reply #3  Posted
20130121 19:23:23 » 

Math.atan2(y, x) is the correct function parameters, you used Math.atan which only takes 1 parameter.




danielcmessias
Senior Newbie


«
Reply #4  Posted
20130121 19:31:25 » 

Thanks for the help, I'm relatively new to Vector Maths, is this code looking somewhat right? Written in the update method of an entity, trying to point towards the player. 1 2 3 4
 Vector2 p1 = new Vector2(x/Math.abs(x),y/Math.abs(y)); Vector2 p2 = new Vector2(player.x/Math.abs(player.x),player.y/Math.abs(player.y)); float angle = (float) Math.acos(Math.toRadians(p1.dot(p2))); 




Best Username Ever


«
Reply #5  Posted
20130121 19:37:16 » 

There are a number of things I did not correct. It should be sin and the cross product. A is the direction of thing. B is the vector from thing to target.




Agro


«
Reply #6  Posted
20130121 19:38:56 » 

There are a number of things I did not correct. It should be sin and the cross product. A is the direction of thing. B is the vector from thing to target.
I don't think that's exactly correct. All you need is Math.atan2 or the dot product. Where did you get sin and cross product from?




danielcmessias
Senior Newbie


«
Reply #7  Posted
20130121 19:40:54 » 

1
 PlayerAngle = (float) Math.toDegrees(Math.atan2(player.xx, player.y  y)); 
This will give me the angle between 180 and 180, I'm not sure what the need for the dot product is? Edit: Sorry, you said 'or', my bad!




danielcmessias
Senior Newbie


«
Reply #8  Posted
20130121 19:48:54 » 

Ok, so currently I am at this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
 playerAngle = (float) Math.toDegrees(Math.atan2(player.xx, player.y  y)); turnSpeed = 2; if(Math.abs(playerAngle) < turnSpeed) { rotation = playerAngle; } else if(playerAngle > 0) { rotation = turnSpeed; } else { rotation += turnSpeed; } 
At the moment it seems I am only accounting for the angle between two coordinates, and not the current rotation of the entity. It's integrating that part that I'm finding most difficult.




Agro


«
Reply #9  Posted
20130121 19:52:07 » 

1
 playerAngle = (float) Math.toDegrees(Math.atan2(player.xx, player.y  y)); 
This line is wrong. It should be 1
 playerAngle = (float) Math.toDegrees(Math.atan2(player.yy, player.x  x)); 




Games published by our own members! Check 'em out!


danielcmessias
Senior Newbie


«
Reply #10  Posted
20130121 19:59:52 » 

1
 playerAngle = (float) Math.toDegrees(Math.atan2(player.xx, player.y  y)); 
This line is wrong. It should be 1
 playerAngle = (float) Math.toDegrees(Math.atan2(player.yy, player.x  x)); 
Oh yeah, cheers. I'm still confused on how to get this fully working though. Merely using playerAngle won't account for the entities current rotation, it'll only give the angle between two points. The entity will just spin infinitely as playerAngle will never change so long as it is static, despite it rotating. How do I factor in it's rotation? Thanks




matheus23


«
Reply #11  Posted
20130121 20:23:12 » 

Just an offtopic question: Why the heck is it (y, x) and not the usual (x, y)?




danielcmessias
Senior Newbie


«
Reply #12  Posted
20130121 21:48:03 » 

Just an offtopic question: Why the heck is it (y, x) and not the usual (x, y)?
That's just the order the atan2 function in java's math library wants them.




Best Username Ever


«
Reply #13  Posted
20130121 22:08:29 » 

Actually it is important that you use the right inverse function to get the right sign. Intuitively, you want to put yourself in the position of your character and facing the same direction. You turn clockwise if it is to your left, counter clockwise if it is on your right, or face the target directly if is right about in front of you. Atan will give you an absolute angle. The dot product will tell you if something is in front or behind you. To make the sign tell whether you are on the left or right, rotate the direction you are facing 90 degrees and use that with your dot product. In 2D, that is like taking the Zcomponent of the cross product. I was thinking cross product because swapping the order for two vectors changes the outcome while dot product does not. That gives you the correct sign and I think the right angle if you use sin ^{1}. I was thinking my answer was applicable in 2D or 3D, but I will have to look at the math to figure out what my train of thought really was. In 2D, you could subtract two absolute angles from each other and adjust the answer to get the right sign. If all the numbers work out, you could do the same thing I wrote in one line. thing.rotate(Math.max(maxTurn, Math.min(maxTurn, angle))); 




Agro


«
Reply #14  Posted
20130121 22:11:35 » 

Even if you don't get the right sign, you can still do a few calculations to get the right one.
And I think the reasoning for y being first in the Math.atan2 function is because its sort of like slope:
sin(a) / cos(a) = tan(a)




sixtyten


«
Reply #15  Posted
20130121 22:31:15 » 

Actually it is important that you use the right inverse function to get the right sign. Intuitively, you want to put yourself in the position of your character and facing the same direction. You turn clockwise if it is to your left, counter clockwise if it is on your right, or face the target directly if is right about in front of you. Atan will give you an absolute angle. The dot product will tell you if something is in front or behind you. To make the sign tell whether you are on the left or right, rotate the direction you are facing 90 degrees and use that with your dot product. In 2D, that is like taking the Zcomponent of the cross product. I was thinking cross product because swapping the order for two vectors changes the outcome while dot product does not. That gives you the correct sign and I think the right angle if you use sin ^{1}. I was thinking my answer was applicable in 2D or 3D, but I will have to look at the math to figure out what my train of thought really was. In 2D, you could subtract two absolute angles from each other and adjust the answer to get the right sign. If all the numbers work out, you could do the same thing I wrote in one line. thing.rotate(Math.max(maxTurn, Math.min(maxTurn, angle))); 
You can use a cross product to do the rotation as well, just do: thing.forward x (target.pos  thing.pos) where thing.forward is the forward vector of the sprite to be rotated (which doesn't need to be normalised). Then as you show on your diagram, ve or +ve Z indicate which way to rotate. Then keep rotating until the abs of the crossproduct is below a threshold.




sixtyten


«
Reply #16  Posted
20130121 22:53:51 » 

Thanks for the help, I'm relatively new to Vector Maths, is this code looking somewhat right? Written in the update method of an entity, trying to point towards the player. 1 2 3 4
 Vector2 p1 = new Vector2(x/Math.abs(x),y/Math.abs(y)); Vector2 p2 = new Vector2(player.x/Math.abs(player.x),player.y/Math.abs(player.y)); float angle = (float) Math.acos(Math.toRadians(p1.dot(p2))); 
Remember if you use the dot or cross product, you should be doing it with vectors and not the positions as you are using here. So as I said above, you want to use the entity forward (direction) vector and entity>player vector. e.g. with cross product 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 double wx = player.x  x; double wy = player.y  y;
double vx = Math.cos(rotation); double vy = Math.sin(rotation);
double cross = (wx * vy)  (wy * vx);
if (Math.abs(cross) < turnSpeed) { rotation = Math.atan2(wy, wx); } else if (cross < 0) { rotation += turnSpeed; } else { rotation = turnSpeed; }

Those calculations are in radians, but hope you get the gist. Edit: Actually one other thing you need to check with the cross product is that the entity is not facing directly away from the player (i.e. 180 degrees), as the cross will also be zero then. You can do that with a dot product.





sixtyten


«
Reply #18  Posted
20130121 23:07:13 » 

You need to normalize w.
Not if you're only checking the sign




Best Username Ever


«
Reply #19  Posted
20130121 23:15:24 » 

You need to normalize w.
Not if you're only checking the sign True, but you are also checking its magnitude on line 14.




sixtyten


«
Reply #20  Posted
20130121 23:18:11 » 

Ya, fair point. That line is all wrong anyway as I'm comparing an angle to the (nonnormalised) sine of an angle!




Best Username Ever


«
Reply #21  Posted
20130122 00:39:30 » 

Use the angle of the direction the character is facing (a1) and atan2 to get the angle of the vector from the character to the target (a2). Adjust the angle (a2  a1) using: 1 2
 while(angle <= 180.0) angle += 360.0; while(angle > 180.0) angle = 360.0; 
(Use radians in the real code.) 1 2 3 4 5 6 7 8 9 10 11 12
 if(Math.abs(angle) < turn) { character.setAngle(a2); } else if(angle > 0) { character.setAngle(a1  turn); } else { character.setAngle(a1 + turn); } 




Agro


«
Reply #22  Posted
20130122 00:42:41 » 

If you do it correctly, I don't even think the whiles are needed. 1
 double angle = Math.toDegrees(Math.atan2(target.x  this.x, target.y  this.y)); 
That will return an angle between 180 and 180 with the angle starting on the II quadrant along the Xaxis. You just have to add 180 to it and everything will be resolved to an angle resulting in a positive number. I just did it right now.




Best Username Ever


«
Reply #23  Posted
20130122 02:51:30 » 

You could get 180  180 = 360 or 180  180 = 360 or anything in between. You don't need while loops either, but it gets the point across for the general case.




Roquen


«
Reply #24  Posted
20130122 07:19:56 » 

Using a complex number/vectors instead you have: the sign of the cross product (orthogonal direction) tells you which direction is the minimal angular path. A constant angular velocity (for fixed simulation timesteps) is a fixed complex number. Multiple the current rotation (identical to unit direction vector) performs the rotation. Re cross product and if the sign has changed you have overshot the target direction..set to target. No trig, inverse trig or any explicit angles.




Best Username Ever


«
Reply #25  Posted
20130123 00:47:16 » 

If you do that, you will continuously overshoot the target when the angle between them is less than the angular velocity. I tried to solve the problem of all three cases of the if, else if, else block without using only multiplications. You could avoid trig if you stored cos(maxTurn) (because it is constant.) I could not do away with both square roots and inverse trig functions.




Roquen


«
Reply #26  Posted
20130123 06:32:24 » 

If you do that, you will continuously overshoot the target when the angle between them is less than the angular velocity. Who are you talking to? I note that if the sign changes you've overshot.




danielcmessias
Senior Newbie


«
Reply #27  Posted
20130123 20:16:50 » 

I'm really confused, sorry, but this is maths all beyond my ability. Could anybody clarify how to actually practically implement this, ideally with minimal advanced vector manipulation (advanced for me)! Thanks




sixtyten


«
Reply #28  Posted
20130123 21:00:54 » 

Ok, so currently I am at this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
 playerAngle = (float) Math.toDegrees(Math.atan2(player.xx, player.y  y)); turnSpeed = 2; if(Math.abs(playerAngle) < turnSpeed) { rotation = playerAngle; } else if(playerAngle > 0) { rotation = turnSpeed; } else { rotation += turnSpeed; } 
At the moment it seems I am only accounting for the angle between two coordinates, and not the current rotation of the entity. It's integrating that part that I'm finding most difficult. The simplest solution (in terms of being easy to understand mathematically) is probably this  just a small modification to your previous code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 playerAngle = (float) Math.toDegrees(Math.atan2(player.y  y, player.x  x)); turnAngle = playerAngle  rotation;
if (turnAngle < 180) turnAngle += 360; if (turnAngle > 180) turnAngle = 360;
turnSpeed = 2; if(Math.abs(turnAngle) < turnSpeed) { rotation = playerAngle; } else if(turnAngle > 0) { rotation += turnSpeed; } else { rotation = turnSpeed; }

Make sure you check your code carefully. There were some bugs there (y/x being swapped in atan2, decreasing rotation when the angle was > 0) which you can spot yourself if you take the time to look through.




danielcmessias
Senior Newbie


«
Reply #29  Posted
20130123 21:01:54 » 

SolutionI've finally reached a perfect solution, although I can't say this code is as efficient as can be. The real problem ended up the different way rotations were being stored. For the entities rotation, it run counterclockwise 0360. The result of atan2 gave me something running clockwise and 180180. The key turned out to be converting to counterclockwise 0360, after which it was a trivial solution, although code posted earlier on here helped out, particulary in ensuring there isn't jerky movement when the entity is pointing towards the ship out by only a fraction of a degree. I'm not sure if this solution would apply to anyone else's code, but hopefully it could be of help to somebody. Either way, there is plenty of reading material on this post, and I'd like to thank everybody who contributed and helped solve my issue! 1 2 3 4 5 6 7 8 9 10 11 12
 playerAngle = (float) (Math.toDegrees((MathUtils.atan2(player.yy, player.xx)))+270)%360; shipRotation = Math.abs(rotation%360); turnSpeed = 2; angle = playerAngleshipRotation; if(Math.abs(angle)<turnSpeed){ rotation = playerAngle; }else if(angle<0){ rotation = turnSpeed; }else{ rotation += turnSpeed; } 




