views:

459

answers:

7

I'm looking for a way to smoothly increase or decrease the speed of a circular movement.

Using the parametric equation of a circle, I can move an object in a circle over time:

x = center_x + radius * sin(time * speed)
y = center_y + radius * cos(time * speed)

The problem with this approach is that I can't simply do speed = speed + 1 to accelerate the object because it results in jerky movement. This makes sense because the x and y values are being recalculated every frame based on absolute values rather than relative to the object's previous position.

Another approach might be to use a vector that represents the velocity of the object, then apply a circular motion to the vector instead:

v_x = radius * sin(time * speed)
v_y = radius * cos(time * speed)
x = x + v_x
y = y + v_y

The problem with this approach is that if I change the speed then the radius will grow or shrink. This makes sense because the movement is relative to the current position and so time is essentially skipped if I alter the speed.

One implementation I can think of that might work is using a vector that points from the object to the center of the circle. Then I could calculate the tangent of the circle at the object's position by using the perpendicular vector, normalize it and scale it by the speed. I haven't implemented this yet because it seems like overkill for a problem like this, so please let me know if a simpler solution exists. Thanks!

+6  A: 

The speed is the rate of change of the angle, a change of speed only affects the change of the angle for the last interval, so:

delta = time - lastTime
angle = angle + delta * speed

x = center_x + radius * sin(angle)
y = center_y + radius * cos(angle)

where lastTime must hold the time of the last cycle, understand?

tekBlues
This has the same problem as my second implementation. Try it for yourself.
Kai
@Kai: umm, no it shouldn't...
ephemient
Ah, my implementation was wrong, not this algorithm (way to look stupid). Thanks!
Kai
+2  A: 

You're not using acceleration correctly if you're simply doing speed = speed + 1. More generically, you want to do this:

accel = 1;
speed = speed + (accel * timeDelta);

Also, accel = 1 is quite a large angular velocity change in radians - try a smaller value, say PI / 16. If you need the acceleration to be that large and want to minimize the visibility of jerky movements, you may want to try out using some motion blur.

Not Sure
A: 

You need to think about this in terms of angular velocity. You're calculating the angle, theta, as time * speed which doesn't make sense if speed is the conventional sense of speed, ie distance/time. Angular velocity is angle/time (ie, radians/sec or degrees/sec). Conventional velocity will be the distance between start and end points/time after applying angular velocity.

plinth
+3  A: 

For a smooth increase in angular speed, you need to add an angular acceleration

x = radius * cos(theta)
y = radius * sin(theta)

theta(t) = theta(0) + omega(0)*t + 0.5 * alpha * t^2

where t is time, theta(0) is the angular position at time 0, omega(0) is the angular speed at time 0 (would be equal to your speed parameter) and alpha is the angular acceleration parameter, which you pick to be something suitable.

This assumes alpha remains constant. Of course, you could do it in a piecewise-continuous manner - run it up to a time T, and then re-initialize at that state.
Mike Dunlavey
A: 
x = center_x + radius * sin(time * speed + offset)
y = center_y + radius * cos(time * speed + offset)

def change_speed(new_speed):
    offset = time * (speed - new_speed) + offset
    speed = new_speed

offset can start off at 0 or really any value... it's used to maintain continuity, as

time * old_speed + old_offset == time * new_speed + new_offset
ephemient
+3  A: 

You said it yourself: you want to change the angular velocity. Now change in angular velocity, in the real world, is limited by the angular inertion of an object. This means it cannot go 'discrete' in steps of 1.

Rather, the angular velocity is the integral of the angular acceleration. The angular position is the integral of the angular velocity.

So for a constant angular acceleration, you can say

velocity( t ) = t*acc + vel[t=0].

and

angle( t ) = t 2 * acc/2 + vel[t=0] * t + angle[t=0].

Then you can calculate your carthesian position with the sin and cos of the angle.

The angular acceleration can vary (quite) discretely.

xtofl
This is a great explanation of what I was doing wrong, thanks for helping me understand this!
Kai
+2  A: 

Using

time * speed

to indicate how far the circle has traveled is just wrong. It only makes sense if the speed never changes.

Instead, you need to store the distance traveled in a variable, and then increase the variable by an amount depending on the current speed and the time interval since the last draw.

The suggestions others have to use acceleration are good, too. Try something like

v = v + a;
d = d + delta * v;

x = center_x + radius * sin(d)
y = center_y + radius * cos(d)
UncleO
`time * speed` does sometimes make sense, when your time steps aren't always equal (for example, if you frameskip).
ephemient
You have the right idea. You might want to have a variable time-step, as in d += v * delta; v += a * delta; Of course, that's Euler integration. Runge-Kutta would be more accurate.
Mike Dunlavey