Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am so out of math that it hurts, but for some of you this should be a piece of cake. I want to move an object around another along its ages or circumference on a simple circular path. At the moment my game algorithm knows how to move and position a sprite just at the edge of an obstacle and now it waits for the next point to move depending on various conditions.

So the mathematical problem here is how to get (aX, aY) and (bX, bY) positions, when I know the Centre (cX, cY), the object position (oX, oY) and the distance required to move (d)

enter image description here

share|improve this question
1  
Is d a linear distance or is it an arc? –  Byte56 Jun 26 '12 at 22:37
    
It is a linear distance in pixels –  Lumis Jun 26 '12 at 22:44
    
Are you familiar at all with what vectors are and basic operations on them? –  Patrick Hughes Jun 26 '12 at 23:00
    
@Patrick No, I guess I will have to do a course on Vectors. Since this is a frame by frame animation the code should be fast and optimised. –  Lumis Jun 26 '12 at 23:24

3 Answers 3

up vote 8 down vote accepted

(CAVEAT: I'm using two approximations here: the first takes d as an arc length, and the second takes it as an orthogonal length. Both of these approximations should be good for relatively small values of d, but they don't fulfill the precise question as clarified in comments.)

The math on this, fortunately, is relatively straightforward. First of all, we can find the relative vector from our center position to our current position:

deltaX = oX-cX;
deltaY = oY-cY;

And once we have this relative vector, then we can know the radius of the circle we're working on by finding the length of it:

radius = sqrt(deltaX*deltaX+deltaY*deltaY);

What's more, from our relative vector we can find the precise angle that the line from cX to oX is at:

curTheta = atan2(deltaX, deltaY);

Now things get a little bit trickier. First of all, understand that the circumference of a circle — that is, the 'arc length' of an arc with an angular measure of 2π — is 2πr. In general, the arc length of an arc with an angular measure of θ along a circle of radius r is just θr. If we were to use the d in your diagram as the arc length, and since we know the radius, we can find the change in theta to get us to the new position by just dividing out:

deltaTheta = d/radius; // treats d as a distance along the arc

For the case where d needs to be a linear distance, things are a little more complicated, but fortunately not much. There, d is one side of an isoceles triangle whose other two sides are the radius of the circle (from cX/cY to oX/oY and aX/aY respectively), and bisecting this isoceles triangle gives us two right triangles, each of which has d/2 as one side and radius as the hypotenuse; this means that the sine of half our angle is (d/2)/radius, and so the full angle is just twice this:

deltaTheta = 2*asin(d/(2*radius)); // treats d as a linear distance

Notice how if you took the asin out of this formula, and cancelled the 2s, this would be the same as the last formula; this is the same as saying that sin(x) is approximately x for small values of x, which is a useful approximation to know.

Now we can find the new angle by just adding or subtracting:

newTheta = curTheta+deltaTheta; // This will take you to aX, aY. For bX/bY, use curTheta-deltaTheta

Once we have the new angle, then we can use some basic trig to find our updated relative vector:

newDeltaX = radius*cos(newTheta);
newDeltaY = radius*sin(newTheta);

and from our center position and our relative vector we can (finally) find the target point:

aX = cX+newDeltaX;
aY = cY+newDeltaY;

Now, with all this, there are some big caveats to be aware of. For one, you'll notice that this math is mostly floating-point, and in fact it almost has to be; trying to use this method to update in a loop and rounding back to integer values at every step can do everything from making your circle not close (either spiralling inward or outward every time you go around the loop) to not getting it started in the first place! (If your d is too small, then you might discover that the rounded versions of aX/aY or bX/bY are exactly where your start position oX/oY was.) For another, this is very expensive, especially for what it's trying to do; in general, if you know your character is going to be moving in a circular arc, you should plan out the whole arc in advance and not tick it from frame to frame like this, since many of the most expensive calculations here can be front-loaded to cut down on costs. Another good way to trim back the costs, if you really want to update incrementally like this, is to not use trig in the first place; if d is small and you don't need it to be exact but just very close, then you can do a 'trick' by adding a vector of length d to oX/oY, orthogonal to the vector towards your center (note that a vector orthogonal to (dX, dY) is given by (-dY, dX) ), and then shrink it down to the right length. I won't explain this code quite so step-by-step, but hopefully it'll make sense given what you've seen so far. Note that we 'shrink down' the new delta vector implicitly in the last step, where we add it to our center to get the updated point:

deltaX = oX-cX; deltaY = oY-cY;
radius = sqrt(deltaX*deltaX+deltaY*deltaY);
orthoX = -deltaY*d/radius;
orthoY = deltaX*d/radius;
newDeltaX = deltaX+orthoX; newDeltaY = deltaY+orthoY;
newLength = sqrt(newDeltaX*newDeltaX+newDeltaY*newDeltaY);
aX = cX+newDeltaX*radius/newLength; aY = cY+newDeltaY*radius/newLength;
share|improve this answer
1  
Steven I think I am going to try the approximation first as this is just a game where is more important to feel natural and interesting than precise. Also speed matters. Thank you for this long and good tutorial! –  Lumis Jun 26 '12 at 23:39
    
Wow, Steven your approximation is working like a dream! Can you tell me how to change your code to get bX, bY. I am not clear yet on your orthogonal concept... –  Lumis Jun 27 '12 at 0:07
2  
Sure! You'll really want to understand vector math at some point, and once you do I suspect this will be second nature; to get bX/bY you just have to go 'the other way around' - in other words, rather than adding the (particular) orthogonal vector, just subtract it. In terms of the code above, this would be 'newDeltaX = deltaX-orthoX; newDeltaY = deltaY-orthoY;', followed by the same computation of newLength, and then 'bX=cX+newDeltaX*radius/newLength; bY=cY+newDeltaY*radius/newLength;'. –  Steven Stadnicki Jun 27 '12 at 0:16
    
Basically, that code would point newDeltaX/newDeltaY in the direction of bX/bY (instead of in the direction of aX/aY), then trim to fit and add to the center just as you would to get aX/aY. –  Steven Stadnicki Jun 27 '12 at 0:17

Form a triangle using the two sides you already have (one side is from 'c' to 'o', the other from 'o' to 'a'), and the third side goes from 'a' to 'c'. You don't know where 'a' is just yet, just imagine there is a point there for now. You'll need trigonometry to calculate the angle of the angle that is opposite to the side 'd'. You have the length of the sides c<->o and c<->a, because they're both the radius of the circle.

Now that you have the length of the three sides of this triangle you cannot yet see, you can determine the angle that is opposite to the 'd' side of the triangle. Here's the SSS (side-side-side) formula if you need: http://www.teacherschoice.com.au/maths_library/trigonometry/solve_trig_sss.htm

Using the SSS formula you have the angle (which we'll call 'j') that is opposite to side 'd'. So, now we can calculate (aX, aY).

// This is the angle from 'c' to 'o'
float angle = Math.atan2(oY - cY, oX - cX)

// Add the angle we calculated earlier.
angle += j;

Vector2 a = new Vector2( radius * Math.cos(angle), radius * Math.sin(angle) );

Make sure the angles you're calculating are always in radians.

If you need to calculate the radius of the circle, you can use vector subtraction, subtract point 'c' from point 'o', then get the length of the resulting vector.

float lengthSquared = ( inVect.x * inVect.x
                      + inVect.y * inVect.y
                      + inVect.z * inVect.z );

float radius = Math.sqrt(lengthSquared);

Something like this should do, I believe. I do not know Java, so I guessed on the exact syntax.

Here's the image given by user Byte56 to illustrate what this triangle might look like: cao triangle

share|improve this answer
1  
I was making an answer, but this is it. You're welcome to use the image I made :) i.imgur.com/UUBgM.png –  Byte56 Jun 26 '12 at 23:06
    
@Byte56: Thanks, I didn't have any image editor handle to illustrate. –  Nic Foster Jun 26 '12 at 23:11
    
Note that the radius has to be computed too; there should be more straightforward ways of getting j than the full SSS calculation, since we have an isoceles triangle.) –  Steven Stadnicki Jun 26 '12 at 23:16
    
Yes, that seems simple, even to me! Android does not have Vector2 so I guess I can just use the values separately. Intrestingly I found Vector2 class manually created for Android here: code.google.com/p/beginning-android-games-2/source/browse/trunk/… –  Lumis Jun 26 '12 at 23:19
    
(I've tweaked my own answer to find the correct linear distance - the second calculation of deltaTheta there, as 2*asin(d/(2*radius)), is how you would find j here.) –  Steven Stadnicki Jun 26 '12 at 23:29

To make obj2 rotate around obj1 maybe try:

float angle = 0; //init angle

//call in an update
obj2.x = (obj1.x -= r*cos(angle));
obj2.y = (obj1.y += r*sin(angle));
angle-=0.5;
share|improve this answer
    
This doesn't show how to get the angle in the first place, and you appear to be showing how to orbit, instead of finding the coordinates like the question asks. –  Byte56 Jun 26 '12 at 22:43
1  
Lewis, thanks for showing how to orbit an object around a point. It may come useful... –  Lumis Jun 26 '12 at 23:49

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.