Thursday, December 3, 2015

c# - Vary speed of enemy smoothly


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!



Answer



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.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...