Java-Gaming.org
 Featured games (81) games approved by the League of Dukes Games in Showcase (498) Games in Android Showcase (115) games submitted by our members Games in WIP (562) games currently in development
 News: Read the Java Gaming Resources, or peek at the official Java tutorials
Pages: [1]
 ignore  |  Print
 Rotate Towards Point  (Read 1976 times) 0 Members and 1 Guest are viewing this topic.
danielcmessias

Senior Newbie

 « Posted 2013-01-21 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!

Junior Member

 « Reply #1 - Posted 2013-01-21 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 2013-01-21 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).
Agro
 « Reply #3 - Posted 2013-01-21 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 2013-01-21 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)));`

Junior Member

 « Reply #5 - Posted 2013-01-21 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 2013-01-21 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 2013-01-21 19:40:54 »

 1 `PlayerAngle = (float) Math.toDegrees(Math.atan2(player.x-x, 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 2013-01-21 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.x-x, 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 co-ordinates, and not the current rotation of the entity. It's integrating that part that I'm finding most difficult.
Agro
 « Reply #9 - Posted 2013-01-21 19:52:07 »

 1 `playerAngle = (float) Math.toDegrees(Math.atan2(player.x-x, player.y - y));`

This line is wrong. It should be

 1 `playerAngle = (float) Math.toDegrees(Math.atan2(player.y-y, player.x - x));`

danielcmessias

Senior Newbie

 « Reply #10 - Posted 2013-01-21 19:59:52 »

 1 `playerAngle = (float) Math.toDegrees(Math.atan2(player.x-x, player.y - y));`

This line is wrong. It should be

 1 `playerAngle = (float) Math.toDegrees(Math.atan2(player.y-y, 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

JGO Kernel

Medals: 107
Projects: 3

You think about my Avatar right now!

 « Reply #11 - Posted 2013-01-21 20:23:12 »

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

See my:
My development Blog:     | Or look at my RPG | Or simply my coding
http://matheusdev.tumblr.comRuins of Revenge  |      On Github
danielcmessias

Senior Newbie

 « Reply #12 - Posted 2013-01-21 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.

Junior Member

 « Reply #13 - Posted 2013-01-21 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 Z-component 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 2013-01-21 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

Junior Member

Projects: 1

 « Reply #15 - Posted 2013-01-21 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 Z-component 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 cross-product is below a threshold.
sixtyten

Junior Member

Projects: 1

 « Reply #16 - Posted 2013-01-21 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 `// Get vector from entity to playerdouble wx = player.x - x;double wy = player.y - y;// Entity forward vectordouble vx = Math.cos(rotation);double vy = Math.sin(rotation);// Cross-product of the two vectors - sign of result signals if the// player is in a clockwise or counter-clockwise directiondouble cross = (wx * vy) - (wy * vx);// Rotate entity towards playerif (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.

Junior Member

 « Reply #17 - Posted 2013-01-21 23:06:21 »

You need to normalize w.
sixtyten

Junior Member

Projects: 1

 « Reply #18 - Posted 2013-01-21 23:07:13 »

You need to normalize w.

Not if you're only checking the sign

Junior Member

 « Reply #19 - Posted 2013-01-21 23:15:24 »

True, but you are also checking its magnitude on line 14.
sixtyten

Junior Member

Projects: 1

 « Reply #20 - Posted 2013-01-21 23:18:11 »

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

Junior Member

 « Reply #21 - Posted 2013-01-22 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 // No need to test for -180 deg.{  character.setAngle(a1 + turn);}`
Agro
 « Reply #22 - Posted 2013-01-22 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 X-axis. 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.

Junior Member

 « Reply #23 - Posted 2013-01-22 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 2013-01-22 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.

Junior Member

 « Reply #25 - Posted 2013-01-23 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 2013-01-23 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 2013-01-23 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

Junior Member

Projects: 1

 « Reply #28 - Posted 2013-01-23 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.x-x, 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 co-ordinates, 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 `// NOTE: atan2 takes y first, then x!playerAngle = (float) Math.toDegrees(Math.atan2(player.y - y, player.x - x));turnAngle = playerAngle - rotation;// Bound range of turn between -180 and +180 degrees// Can also use while's instead of if's, if turnAngle ends up outside range of -360 to +360if (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 2013-01-23 21:01:54 »

Solution

I'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 0-360. The result of atan2 gave me something running clockwise and -180-180. The key turned out to be converting to counterclockwise 0-360, 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.y-y, player.x-x)))+270)%360;shipRotation = Math.abs(rotation%360);turnSpeed = 2;angle = playerAngle-shipRotation;            if(Math.abs(angle)
Pages: [1]
 ignore  |  Print

You cannot reply to this message, because it is very, very old.

 Add your game by posting it in the WIP section, or publish it in Showcase. The first screenshot will be displayed as a thumbnail.
 BurntPizza (21 views) 2014-09-21 02:42:18 BurntPizza (15 views) 2014-09-21 01:30:30 moogie (18 views) 2014-09-21 00:26:15 UprightPath (25 views) 2014-09-20 20:14:06 BurntPizza (27 views) 2014-09-19 03:14:18 Dwinin (42 views) 2014-09-12 09:08:26 Norakomi (73 views) 2014-09-10 13:57:51 TehJavaDev (97 views) 2014-09-10 06:39:09 Tekkerue (49 views) 2014-09-09 02:24:56 mitcheeb (70 views) 2014-09-08 06:06:29
 BurntPizza 37x Riven 18x Rayvolution 18x basil_ 16x ags1 16x princec 16x KevinWorkman 15x LiquidNitrogen 12x deathpat 11x kevglass 11x nsigma 11x theagentd 11x HeroesGraveDev 9x Gibbo3771 6x cylab 6x EgonOlsen 6x
 List of Learning Resources2014-08-16 10:40:00List of Learning Resources2014-08-05 19:33:27Resources for WIP games2014-08-01 16:20:17Resources for WIP games2014-08-01 16:19:50List of Learning Resources2014-07-31 16:29:50List of Learning Resources2014-07-31 16:26:06List of Learning Resources2014-07-31 11:54:12HotSpot Optionsby dleskov2014-07-08 01:59:08
 java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org