How to create slow random movement for enemy idle?

A place for people with an interest in developing new shmups.
Post Reply
User avatar
DyingIsFun
Posts: 35
Joined: Mon May 31, 2021 7:14 am
Contact:

How to create slow random movement for enemy idle?

Post by DyingIsFun »

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.

Flying bosses in Cave games also 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 https://youtu.be/ij5trBPbJPw?t=8739.

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 a random point on screen. (Though sometimes it looks like ships overshoot their arrival 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 spawn and arrival, maybe they also use some kind of Wander() steering behavior to achieve the small random movements in the enemy’s neighborhood? Does anybody have tips or techniques here, or specific ways different games have implemented this?

Examples

https://youtu.be/BH7ONV0OJ2o?t=93 - Do Don Pachi - notice how the boss (and the pods on their left and right) moves at its location
https://youtu.be/3HO5aMd1Osk?t=62 - 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
Ixmucane2
Posts: 760
Joined: Mon Jan 19, 2009 3:26 pm
Location: stuck at the continue prompt

Re: How to create slow random movement for enemy idle?

Post by Ixmucane2 »

Some obvious, non-exclusive approaches:
  • When steering behaviours apply forces or impulses, add a little random noise, which will be later corrected by more thrust.
  • Distinguish the "correct" waypoint, e.g. the location of an enemy, and a perturbed waypoint that is actually used for steering behaviours. It can be randomly displaced from the nominal location, deterministic (e.g. on a variable radius circular orbit) or a combination of both.
  • Whenever the waypoint is very close, apply a random force or impulse so that the enemy keeps moving even without a new waypoint.
Random forces, impulses and displacements need not be uniformly distributed: for example, there's a significant difference between shooting an enemy that swerves horizontally or vertically (one direction dodges most player bullet, the other only changes time of impact) or between forces that are forward, backward or sideways relative to current velocity (respectively accelerating, braking and turning).
User avatar
Leander
Posts: 62
Joined: Thu May 09, 2019 1:23 am
Contact:

Re: How to create slow random movement for enemy idle?

Post by Leander »

You can keep track of a fixed position the enemy should move around, and then set its actual position based on that position + modifiers.
For example if you are keeping track of time elapsed in game (or number of frames displayed), set x_position = fixed_x_position + (sin(time) * 10) to have the enemy sway back and forth 10 pixels (or whatever unit) either side of the desired fixed position.
User avatar
BryanM
Posts: 6117
Joined: Thu Feb 07, 2008 3:46 am

Re: How to create slow random movement for enemy idle?

Post by BryanM »

A robust way to implement sway would be thinking of the tether as an object itself attached to each unit. Min/Max X and Y coordinates, acceleration rate, a velocity threshold if you only want it to kick in when an object is moving slow...

I'm not sure if you particularly want noise or random movement - a helicopter bobbing up and down on a fixed tether works fine, formation shooters like Galaga where the formation "breathes" does a great job in making them look alive. A bit of a figure 8 pattern should naturally emerge from certain X and Y thresholds.
User avatar
DyingIsFun
Posts: 35
Joined: Mon May 31, 2021 7:14 am
Contact:

Re: How to create slow random movement for enemy idle?

Post by DyingIsFun »

Thanks for the ideas @Ixmucane2, @Leander, and @BryanM!

I now have a function called SeekWithWobble() which returns acceleration for wobble, and it only kicks in if the host's velocity is below a certain threshold. Brilliant idea @BryanM!

Code: Select all

    public Vector3 SeekWithWobble(Vector3 _waypoint, float _arrival_radius)
    {
        state = "seek_with_wobble";
        waypoint = _waypoint;
        arrival_radius = _arrival_radius;

        // get desired vel by looking at target position
        // use wobbledPoint only if magnitude of vel is below wobbleSpeedThreshold, otherwise use waypoint
        Vector3 vec_to_target;
        if (Vector3.Magnitude(host_controller.velocity) < wobbleSpeedThreshold) // wobble velocity threshold
        {

            vec_to_target = wobbledPoint - host_controller.transform.position; // NOTE: Seek wobbled point, not waypoint
        }
        else
        {
            vec_to_target = waypoint - host_controller.transform.position;
        }
        
        float dist = vec_to_target.magnitude;
        Vector3 desired_vel;

        // check for arrival
        if (dist <= arrival_epsilon)
        {
            // emit event
            _OnArrival();
        }

        // if arriving, slow down
        if (dist < arrival_radius)
        {
            desired_vel = vec_to_target.normalized * host_controller.speed * (dist / arrival_radius);
        }
        else
        {
            desired_vel = vec_to_target.normalized * host_controller.speed;
        }

        // get steering force
        Vector3 steering = desired_vel - host_controller.velocity;
        steering = Vector3.ClampMagnitude(steering, max_steering_acc);
        Vector3 acc = steering;

        return acc;
    }
For actually determing the wobble point, I just walk through some smooth noise and multiply by wobbleAmplitude_x and wobbleAmplitude_y.

Code: Select all

            // wobble code
            wobblePhase += Time.deltaTime * wobbleFrequency;
            wobbledPoint.x = waypoint.x + wobbleAmplitude_x * (Mathf.PerlinNoise(wobblePhase, wobblePhase) - 0.5f) * 2f;
            wobbledPoint.y = waypoint.y + wobbleAmplitude_y * (Mathf.PerlinNoise(wobblePhase + DECORRELATION, wobblePhase + DECORRELATION) - 0.5f) * 2f;
This seems to have potential, just requires a bit of fine tuning values to achieve the perfect look. If there's ways I can improve the above implementation, please let me know.

And if anyone else has other methods for wobble/sway effect, please do share :D
Post Reply