3

I'm trying to move multiple sprites (images) in an elliptical path such that distance (arc distance) remains uniform.

I have tried

  • Move each sprite angle by angle, however the problem with this is that distance moved while moving unit angle around major axis is different than that while moving unit angle around minor axis - hence different distance moved.

  • Move sprites with just changing x-axis uniformly, however it again moves more around major axis.

So any ideas how to move sprites uniformly without them catching-up/overlapping each other?

Other info:

  • it will be called in onMouseMove/onTouchMoved so i guess it shouldn't be much CPU intensive.
  • Although its a general algorithm question but if it helps I'm using cocos2d-x

2 Answers2

1

So this is what i ended up doing (which solved it for me):

I moved it in equation of circle and increased angle by 1 degree. Calculated x and y using sin/cos(angle) * radius. And to make it into an ellipse I multiplied it by a factor.

Factor was yIntercept/xIntercept.

so it looked like this in end

FACTOR = Y_INTERCEPT / X_INTERCEPT;

//calculate previous angle
angle = atan((prev_y/FACTOR)/prev_x);

//increase angle by 1 degree (make sure its not radians in your case)
angle++;

//new x and y
x =  cos(newangle) * X_INTERCEPT;
y =  sin(newangle) * X_INTERCEPT * FACTOR;
0

I have written a function named getPointOnEllipse that allows you to move your sprites pixel-by-pixel in an elliptical path. The function determines the coordinates of a particular point in the elliptical path, given the coordinates of the center of the ellipse, the lengths of the semi-major axis and the semi-minor axis, and finally the offset of the point into the elliptical path, all in pixels.
Note: To be honest, unfortunately, the getPointOnEllipse function skips (does not detect) a few of the points in the elliptical path. As a result, the arc distance is not exactly uniform. Sometimes it is one pixel, and sometimes two pixels, but not three or more! In spite of the fault, changes in speed will be really "faint", and IMO, your sprites will move pretty smoothly.
Below is the getPointOnEllipse function, along with another function named getEllipsePerimeter, which is used to determine an ellipse's perimeter through Euler's formula. The code is written in JScript.

function getEllipsePerimeter(rx, ry)
{
    with (Math)
    {
        // You'll need to floor the return value to obtain the ellipse perimeter in pixels.
        return PI * sqrt(2 * (rx * rx + ry * ry));
    }
}

function getPointOnEllipse(cx, cy, rx, ry, d)
{
    with (Math)
    {
        // Note: theta expresses an angle in radians!
        var theta = d * sqrt(2 / (rx * rx + ry * ry));
        //var theta = 2 * PI * d / getEllipsePerimeter(rx, ry);
        
        return {x:floor(cx + cos(theta) * rx),
                y:floor(cy - sin(theta) * ry)};
    }
}

The following figure illustrates the parameters of this function:
getPointOnEllipse Parameters
cx - the x-coordinate of the center of the ellipse
cy - the y-coordinate of the center of the ellipse
rx - the length of semi-major axis
ry - the length of semi-minor axis
d - the offset of the point into the elliptical path (i.e. the arc length from the vertex to the point)
The unit of all parameters is pixel.
The function returns an object containing the x- and y-coordinate of the point of interest, which is represented by a purple ball in the figure.
d is the most important parameter of the getPointOnEllipse function. You should call this function multiple times. In the first call, set d to 0, and then place the sprite at the point returned, which causes the sprite to be positioned on the vertex. Then wait a short period (e.g. 50 milliseconds), and call the function again, setting d parameter to 1. This time, by placing the sprite at the point returned, it moves 1 pixel forward in the ellipse path. Then repeat doing so (wait a short period, call the function with increased d value, and position the sprite) until the value of d reaches the perimeter of the ellipse. You can also increase d value by more than one, so that the sprite moves more pixels forward in each step, resulting in faster movement.
Moreover, you can modify the getEllipsePerimeter function in the code to use a more precise formula (like Ramanujan's formula) for getting ellipse perimeter. But in that case, be sure to modify the getPointOnEllipse function as well to use the second version of theta variable (which is commented in the code). Note that the first version of theta is just a simplified form of the second version for the sake of optimization.

Javad Bayat
  • 160
  • 13