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.

Hey. I'm trying to create an enemy that travels at a speed s on screen but I'm having trouble with trying to make the speed vary in a smooth motion. Basically, I can get the enemy to move at a constant rate but at random intervals, I'd like it to speed up then slow back down to it's original speed, or slow down then speed up to it's original speed. I imagine I'd have to use a graph function of sorts, but I'm not great at maths so are there any suggestions on how this can be achieved? Feel free to let me know if my question is unclear.Thanks!

share|improve this question
add comment

4 Answers

up vote 6 down vote accepted

What you're talking about here is commonly called a "lerp", short for interpolation. You can find a lot of different implementations and specifics about this sort of operation on Google by searching for "lerp", but here's a very basic one:

T Interpolate(float fraction, T startingValue, T endingValue)
{
  T delta = endingValue - startingValue;
  return startingValue + (delta * fraction);
}

In this, "fraction" should be a value between zero and one (inclusive), and the type T is anything that supports +, -, and * operators (So floats, vectors, etc). In your case, you'd use one with 'float' instead of T, but this same approach works for interpolating lots of other mathematical types, so this will almost certainly be useful to you again in other situations in the future.

The simplest way to get your speed to increase smoothly will be to simply interpolate from the object's current speed toward its target speed each frame, using a low fraction value for the interpolation. For example, like this:

float speedFraction = 0.1f;
object->SetSpeed( Interpolate( speedFraction, object->GetCurrentSpeed(), object->GetDesiredSpeed() );

This will give what is referred to as a "slam in, ease out" curve; the object will get a sudden burst of acceleration when it changes its desired speed, but that acceleration will taper off as it approaches that destination speed. If you make speedFraction higher, the object will approach its final speed faster. Lower, and it will reach that speed slower.

This is almost certainly all that you'll need to have your character change speeds smoothly enough for you. But be aware that this will produce different behaviours if it's being done 30 times per second than if it's being done 60 times per second.

Advanced: If you do want the object to accelerate even more smoothly than this (that is, to start accelerating slowly, instead of quickly), or if it's important that the object actually reaches that final speed (rather than approaching that speed asymptotically), or if you want to make sure that the acceleration works the same no matter what frame rate you're running at, then you can use that same interpolation function differently.

If you keep track of when the object began to accelerate, and know how long you want the acceleration to take, then you can calculate:

if ( object->IsSpeedingUp() )
{
  float fraction = (timeNow - object->GetAccelerationStartTime() ) / object->GetFullAccelerationDuration();
  float easeInEaseOutFraction = 3*fraction*fraction - 2*fraction*fraction*fraction;
  object->SetSpeed( Interpolate( easeInEaseOutFraction, object->GetSlowSpeed(), object->GetFastSpeed() );
}

In this, easeInEaseOutFraction is using a hermite interpolator on the value "fraction", to convert it from a linear transition from zero to one, into a very smooth curve over that same range. And since it still varies only between zero and one, that means that we can pass it into the same Interpolation function we've already been using, to make the interpolation extremely smooth.

You may find that you prefer this smoother behaviour over the simpler approach that I gave earlier. Or you may find that it makes no difference in your case. My advice is to do the simpler version first, and stick with it if it's good enough for your purposes.

If you do go with the more advanced variation, just be aware that it's critical that the "fraction" value remains between zero and one. If you're using interpolation in this way (by passing in a variable "fraction" value that changes over time), then you must stop interpolating once the object has actually reached its target speed, or else you'll get unexpected behaviour as "fraction" exceeds 1.

share|improve this answer
2  
Lerp, short for linear interpolation :D –  Jonathan Connell Jun 8 '11 at 14:52
1  
True. But "linear interpolation" would have required extra explanation about what "linear" means in this context, and my response was already turning into a small novella. ;D –  Trevor Powell Jun 8 '11 at 23:46
    
That is what good answers be made of! –  Jonathan Connell Jun 9 '11 at 7:41
add comment

You might consider attributing an acceleration value with an enemy. So for each enemy you might associate a velocity(v) and acceleration(a) variable. This would handle the case of the enemy smoothly increasing speed from an initial velocity of zero. Of course you would then need to also associate a maximum speed for the enemy. As for slowing down, there are many ways one could handle this. Simply use the negative of the acceleration, or create your own deceleration variable.

share|improve this answer
add comment

You want the speed to increase smoothly from an initial value, let's call it s1 and reach a final value, let's say that's s2.

That sounds like a Logisic Function to me! A simple logistic function -- a smooth transition from 0 to 1 is P(t) = 1 / (1 + e^(-t)). It looks scary! Don't be afraid! Math is your friend! Drawing it might make it clearer -- you should get an S-shape with asymptotes (values of P(x) that the curve tends towards) of 0 and 1.

Set up an increment for some variable, let's say t, from a small value (say -10; this will cause P(x) to approximate 0) to a large value (say 10; this will make P(x) approximate 1) and every step of the game, run something like

s = s1 + P(t) * (s2 - s1);

until t == 10 or whatever you chose the large value to be. At this point, you should also set s = s2 since s will only be an approximation -- remember that the graph only tends toward 1, never quite reaching it.

This is all based on the fact that when t reaches 1, P(t) will be roughly 1 and the the expression on the right of the assignment will evaluate to roughly s2.

The same code works if you want to reduce speed.

EDIT: I'd made a logical error with what to choose for t. Fixed.

share|improve this answer
4  
A much simpler (and faster to calculate) function which does the same thing is the basic hermite interpolator: P(t) = 3t^2 - 2t^3 –  Trevor Powell Jun 4 '11 at 2:01
1  
As the wiki article mentions, the term for these is "sigmoid" functions (the logistic function I mean, not Trevor's function, which does not do the same thing; his eventually decreases at an unbounded rate). An even simpler sigmoid function to understand is atan(). –  BlueRaja - Danny Pflughoeft Jun 4 '11 at 4:48
    
@Danny I think Trevor meant his function to be used only for x = [-1, 0] I'm sure even many others would work or be easier to look at or yield more accurate results, but you can find new ones forever. I believe our point has been made. Thanks for the additions though! –  Anko Jun 4 '11 at 9:09
    
Also, @Trevor, why not submit yours as an answer? The interpolator is superior to mine and Danny's functions as it equals 1 at one end and 0 at the other and is hence much cleaner to use. –  Anko Jun 4 '11 at 9:19
    
Whoops! I only casually browsed your answer, and just assumed that the one I was thinking of was basically identical to yours. But on re-reading, it does sound a little different. I'll write mine up as a full answer as well. Thanks for pointing this out! –  Trevor Powell Jun 4 '11 at 9:47
add comment

Here is a function from our engine that I've used over the years. Maybe it can help. It will take aValue up to aMax by aAmount. If you then pass aAmount=0, it will then take it down to zero by aDrag amount... all smoothly. If you view the AstroBlaster video/demo on our site, it uses this routine to control the ship speed. Works great for us. It's Object Pascal but you should be able to adopt it easily to the language you use.

procedure TSvMath.SmoothMove(var aValue: Single; aAmount, aMax, aDrag: Single);
var
  Amt: Single;
begin

  Amt := aAmount;

  if Amt > 0 then
  begin
    aValue := aValue + Amt;
    if aValue > aMax then
      aValue := aMax;
  end else if Amt < 0 then
  begin
    aValue := aValue + Amt;
    if aValue < -aMax then
      aValue := -aMax;
  end else
  begin
    if aValue > 0 then
    begin
      aValue := aValue - aDrag;
      if aValue < 0 then
        aValue := 0;
    end else if aValue < 0 then
    begin
      aValue := aValue + aDrag;
      if aValue > 0 then
        aValue := 0;
    end;
  end;
end;
share|improve this answer
add comment

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.