1
\$\begingroup\$

I’ve noticed in a lot 2D shmups, after enemies accelerate onto screen and arrive at their arrival point, they don’t simply sit static at their arrival position, but rather begin to gently move in place in what appear to be slow random movements in their immediate neighborhood.

For example, airborne bosses in Cave shmups have this kind of slow seemingly random movement.

This movement helps to give them life, as if they were really floating in space and subject to the capriciousness of their thrusters.

This principle is discussed briefly here.

My question is what are some effective ways to implement this kind of movement?

I think that a lot of shmups use steering behavior methods to get their enemies to arrive on screen and move from point to point (especially for the types of enemies that need to appear and remain on screen, as opposed to popcorn that follow parameterized bezier curve splines). For example, I imagine an enemy spawns just off screen and does normal SeekWithArrival() behavior to their destination point on screen. (Though sometimes it looks like ships overshoot their destination point and get pulled back a bit—also curious how to implement this kind of thing!)

If this is true, and this technique is used for arrival, maybe you can also use some kind of Wander() steering behavior to achieve the small random movements in the enemy’s neighborhood to achieve this effect? Does anybody have tips or techniques here?

Examples

  1. DoDonPachi - notice how the boss and the pods on the left and right moves at their locations
  2. Cho Ren Sha 68K - notice how when this enemy arrives at a point, he moves gently in place before moving to a new point on screen
\$\endgroup\$
1
\$\begingroup\$

One way to do this is with a noise function.

wobblePhase += deltaTime * wobbleFrequency;

wobbledPoint.x = destinationPoint.x + wobbleAmplitude * Noise(wobblePhase);
wobbledPoint.y = destinationPoint.y + wobbleAmplitude * Noise(wobblePhase + DECORRELATION);

You keep destinationPoint consistent, but have your unit seek toward wobbledPoint, which continues moving each frame as you advance its phase.

Here wobblePhase is a measure of time, tracking how far along this object is in the wobbling pattern. You can initialize it to a random value for each unit, so that two units are unlikely to sync-up and wobble in exactly the same way, even if they spawned/arrived at the same time.

wobbleFrequency controls how quickly the point wobbles back and forth / how frequently it changes direction - higher values are more jittery, lower values are more gradual. wobbleAmplitude controls how far the object can stray from its destinationPoint - larger values allow it to drift further away.

Now for the noise function itself. You can use something like 1-dimensional Perlin Noise or Simplex Noise here. Or for something quick & dirty, you can add together a few periodic functions at different frequencies/amplitudes to get a slightly less regular waveform:

float SimpleTrigNoise(float t) {
    return 0.5*Sin(0.7*t) + 0.3*Sin(1.3*t) + 0.2*Sin(1.7*t);
}

The DECORRELATION constant is just an offset you apply so that the value you get for y isn't the same as the x - they're separated in time. If you choose a large offset (that isn't close to a multiple of the period of your noise function), then that should nicely decorrelate the two values so they have no apparent relationship to each other.

\$\endgroup\$
4
  • \$\begingroup\$ Fun fact: you can use this same trick with a high frequency and low amplitude to implement camera shake. 😁 \$\endgroup\$
    – DMGregory
    May 31 at 22:45
  • \$\begingroup\$ Thank you for the answer! I am toying w/ this implementation right now, trying to find values that look good for my use case. Let me ask, to implement this, is it preferred to use Seek() or SeekWithArrival()? If the latter, should arrival radius be the same as wobbleAmplitude? In either case, do you typically have to alter the enemy's default speed in line w/ the wobble values you choose to get nice looking result? \$\endgroup\$
    – DyingIsFun
    May 31 at 22:50
  • \$\begingroup\$ I haven't used these in conjunction with steering behaviours before, so your experiments will be a better guide than my speculation. \$\endgroup\$
    – DMGregory
    May 31 at 22:55
  • \$\begingroup\$ Fair enough, thanks again! I'll leave this open for a bit to see if anyone else has other implementation ideas. Meanwhile, time to toy w/ some values :D \$\endgroup\$
    – DyingIsFun
    May 31 at 22:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.