开发者

Sprite Elliptical Movement

I am trying to get a 2d sprite to move in an "arc" (half ellipse) instead of a straight line. I have X and Y starting and ending positions as well as a desired radius.

What is the best way to implement this?开发者_运维知识库


You would probably want to use the parametric form of the ellipse, formula shown here

http://en.wikipedia.org/wiki/Ellipse#General_parametric_form

since you have a starting pt, and ending pt, you need to solve for t at both ends,

then step from start to end in t, in relatively small increments.


If you want it to move in an ellipse, the easiest way that I know of would be to give the y values as a function of time with sin, and the x values as a function of time with cos. Assuming you are using the System.currentTimeMillis();, you would store the initial time in a variable, (e.g. double startTime = System.currentTimeMillis()) and then in each frame you would get the elapsed time by subtracting the current time from the start time. (e.g. elapsedTme = System.currentTimeMillis()-startTime). Then the y value would be (radius in the y direction)*sin(elapsedTime*speed) + y value of the center of your ellipse, and the x value would be (radius in the x direction)*cos(elapsedTime*speed) + x value at the center of your ellipse.

EDIT: If you have the starting X and Y coordinates but not the center of the ellipse, then I think the easiest way to get the center is too figure out the rest of the variables, and then plug them in to an equation. The math shouldn't get too hard there.


I think that this problem is best solved via a series of coordinate transforms. For notational simplicity, let's suppose the two points you have are u and v.

Suppose that you're working in a really simple case - the points u and v are at (1, 0) and (-1, 0), respectively, and the length of the major axis on the ellipse is 1. Then you're just tracing out a semicircle. Assuming you want to interpolate between the points at a constant speed, you can use this formula:

x(t) = cos(pi * t)
y(t) = sin(pi * t)

Of course, you're not necessarily lucky enough to be in this setup, and so we can do a series of coordinate transforms to bring you into this configuration. For starters, let's define the point w to be the halfway point between u = (x0, y0) and v = (x1, y1). That is:

w = (x2, y2) = ((x0 + x1) / 2, (y0 + y1) / 2)

Now, suppose that you translate u and v so that w is at the origin. This means that u and v are equidistant from the origin along opposite vectors. If we use matrices and homogeneous coordinates, then you can represent this as

     | 1  0 -x2 |
 T = | 0  1 -y2 |
     | 0  0   1 |

The positions of u and v after this translation are then given by Tu and Tv. Let's call these points u' and v'. They're given by

u' = (x0 - x2, x1 - y2) = (x0 / 2 - x1 / 2, y0 / 2 - y1 / 2)
v' = (x1 - x2, y1 - y2) = (x1 / 2 - x0 / 2, y1 / 2 - y0 / 2)

We're now closer to solving the original problem, but we have the problem that u' and v' are not nicely aligned with the x axis, as they were in the original problem. To fix this, we'll apply a rotation transformation so that u' ends up at (1, 0) and v' ends up at (0, 1). To do this, we'll want to set up a coordinate system where one of the basis vectors is in the direction u' and the other is in a direction perpendicular to it. To do that, we'll pick our unit vectors as follows:

e0 = u' / ||u||
e1 = perp(e0)

Where perp is some unit vector perpendicular to e0. One way to get this is to say that if e0 = (x3, y3), then e1 = perp(e0) = (-y3, x3). You can verify that this vector is perpendicular to (x3, y3) since their dot product is zero.

Given these vectors, we can define a transform that would map (1, 0) to e0 and (0, 1) to e1 by

|x3 -y3  0|
|y3  x3  0|
| 0   0  1|

(That last column is for the homogeneous coordinate system)

Of course, this is the opposite of what we want - we're trying to map from e0 to (1, 0) and from e1 to (0, 1). To get this matrix, we just invert the above matrix. Fortunately, since we chose e0 and e1 to be orthonormal, the above matrix is orthogonal, so its inverse is its transpose:

    | x3 y3 0|
R = |-y3 x3 0|
    |  0  0 1|

Now, if we apply R to u' and v', we end up with the vectors (1, 0) and (-1, 0), which is where we want them to be. The problem now is that the ellipse we want to trace out doesn't necessarily have unit height. If we call its height h, for example, then we'd be tracing out an elliptical path with semimajor axis h and semiminor axis 1. But this is easily rectifiable with another coordinate transform, this time scaling the height of the corodinate system by a factor of 1 / h so that the height of the ellipse we want to trace is 1. This can be done with the following scaling matrix:

    | 1  0  0 |
S = | 0 1/h 0 |
    | 0  0  1 |

The reason this setup is useful is that we know that if we take any point on the desired ellipse between u and v and then apply the matrix SRT to it, then we'll end up converting it to use the corresponding point on the unit circle, which is a path from (1, 0) to (-1, 0). More importantly, though, this works the other way around. If we apply the inverse of SRT to any point on the unit circle, we end up getting back the corresponding point on the original elliptical path between u and v! To seal the deal, we know how to find the points on the path from (1, 0) to (-1, 0), and so we have an algorithm to solve this problem:

  1. For a given time t, find the spot you'd be on the unit circle if you were moving from (1, 0) to (-1, 0) at time t. Call it p.
  2. Compute p' = (SRT)-1p.
  3. p' is the point you're looking for.

The question, then, is what (SRT)-1 is. Fortunately, we have that (SRT)-1 = T-1R-1S-1, and all of these matrices can be computed easily:

     | 1  0 -x2 |          | 1  0  x2 |
 T = | 0  1 -y2 |   T^-1 = | 0  1  y2 |
     | 0  0   1 |          | 0  0   1 |

     | x3  y3  0|          | x3 -y3 0 |
 R = |-y3  x3  0|   R^-1 = | y3  x3 0 |
     |  0   0  1|          |  0   0 1 |

     | 1  0   0 |          | 1  0   0 |
 S = | 0 1/h  0 |   S^-1 = | 0  h   0 |
     | 0  0   1 |          | 0  0   1 |

In short, the final algorithm is as follows:

  1. Given u = (x0, y0) and v = (x1, y1), let w = (x2, y2) = ((x0 + x1) / 2, (y0 + y1) / 2).
  2. Let u' = u / ||u|| = (x3, y3).
  3. At time t (for 0 ≤ t ≤ 1), let p = (cos(π t), sin(π t))
  4. Compute p' = S-1p = (cos(π t), h sin(π t))
  5. Compute p'' = R-1p' = (x3 cos(π t) - y3 sin(π t), y3 cos(π t) + x3 sin(π t))
  6. Compute p''' = T-1p'' = (x3 cos(π t) - y3 sin(π t) + x2, y3 cos(π t) + x3 sin(π t) + y2)
  7. Output p''' as your point.

Sorry if this is a lot of math, but your answer should (hopefully!) be given by the above procedure.


I believe you are looking for Bezier curves, check http://www.math.ucla.edu/~baker/java/hoefer/Bezier.htm. The source is also available in the same link.

If you are using SWT, you can check http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html#drawArc(int, int, int, int, int, int)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜