Nova Drift Dev Deep Dive: Dynamic Waves!

This upcoming update represents the first part of "Enemies 2.0", Nova Drift's largest ever overhaul. Enemies 2.0 will be rolled out in several parts, the first of which focuses on enemy encounters. Nothing is set in stone for these updates, but things may go a bit like this:

Enemies 2.0, Part 1:

  • Dynamic waves overhaul
  • Enemy formations
  • 5 new enemies & boss overhaul (Station Omega)
  • Battlefield zoom

See the previous blog for more details on this update.

Enemies 2.0, Part 2:

  • 5 alternate bosses
  • Boss Rush challenge mode
  • Draft challenge mode

Enemies 2.0, Part 3:

  • More new enemies
  • More classic boss overhauls
  • Further refinements to dynamic waves

And as usual, these updates will also contain a whole lot of new stuff to play with, balance changes, quality of life changes, localizations, and other surprises.

What are DYNAMIC WAVES?

Long story short, we've ripped out all of the enemy waves you're used to and replaced them with a system that procedurally generates them. Some people have expressed interest in exactly how they work, so let's break it down!

Step 1: Choosing The Enemy

The first thing the script does is decide which enemy to spawn by picking from a weighted list. Some enemies are a lot more common than others. Some enemies don't have a weight until you've reached a high enough wave to encounter them.

Note that "waveInfo" is outputting exactly what the spawner is doing to both the debug console and the in-game wave log, which is accessible with cheats. This will really help us balance these huge changes during the beta.

It's getting all of this spawn data from a different script where many parameters are defined for the enemies, enemy spawn types, enemy entrances, and enemy formations.

Each enemy has a defined set of ways it can spawn, enemies it prefers to be paired with, and followers it's capable of supporting in formations.

Step 2: Choosing Spawn Type

Next, the spawner must look up the different types of spawns that are valid for the selected enemy types and, again, choose one by weight (some spawns are more common than others). Spawn types determine the position and order enemies spawn in, whether they have followers, and if they do, how the followers behave.

Spawn types include: standard, arc, centered, fleet, pincer, v formation, v formation pincer, spiral, spiral double, spiral triple, box, cascading pincer, cascading pincer opposing, cascading fleet, cascading adjacent, cascading edges, formation: orbit, formation: flank, formation: rank and file, formation: trail, formation: protect, formation: support

Step 3: Choosing the Enemy Entrance

Some enemies can enter the battlefield via a portal or by emerging from the background. This time the entrance is selected randomly from the choices.

Step 4: Determining Enemy Quantity

A lot of factors determine how many enemies should be spawned. First, the script uses triangular distribution to get a base value from a low, mean, and high value derived from the spawn info. Then, it adds some random factor and scales it based on how far the player has progressed. Next it determines if this is going to be a paired wave. Paired waves mean that two dynamic spawns are happening at once, each with reduced enemy quantity.

Paired waves are usually inherently more difficult, so the two waves add up to 85% of the original quantity. (52.5% quantity for the first wave and 32.5% quantity for the paired wave).

Later, in step 6, it'll multiply quantity yet again based on which spawn type is being used.

Spawn types with a high quantity multiplier tend to be easier, distributed gradually, or more infrequent.

Certain enemy types either always repeat (like Saucers) or sometimes repeat (such as Interceptors or Shredder Mines). As the players reach higher waves, these waves are able to repeat more times. There is also a function here that determines how long the delay should be between a repeating wave. This is useful in some spawn types, too, like the spiral spawn.

Step 5: Enemy Followers

One of the most exciting features of Enemies 2.0 are enemy formations.

There's more: Protect (stay between the player and the leader) and Support (cower behind the leader).

To make a formation, enemy follower quantity is first determined using the same method as the leader quantities. Followers may also have the ability to cascade their actions. For instance, if a formation spawns with a leader orbited by 8 Wardens, they might fire one at a time, slightly offset, instead of firing all at once. This makes things a bit more interesting. Also, followers might decide to break off from their leader, one at a time. The values that drive those behaviors are defined here, too.

Formation break off is especially interesting with the new kamikaze unit, the Hammerhead!

Step 6: Creating Enemy Spawners

First, some default values are defined for the enemy spawner to use, many of which are overwritten by the unique needs of each spawn type. Now the script has generated all the variables it needs to tell the spawnEnemies() script what to do! A switch statement contains the spawn conditions for every spawn type that may have previously been selected.

This is code for 3 of the 22 different spawn types.

The Enemy Spawner

The enemy spawner objects are responsible for a lot, too, but covering how those work could be a whole separate blog post. In short, they do the following:

  • Determine, based on what was spawned and how many things were spawned, how much time should be added to the clock before the next wave automatically advances
  • Stores an array of all enemies to spawn, coordinates where they should spawn, and determines their rarity (meaning the enemy could upgrade to elite or champion)
  • Handles creating portals, warnings, etc
  • Sets timers to create the enemy objects at the correct time and place
  • Creates and arranges the enemy's followers, if any
  • Repeat for incremental or repeating spawns, as needed
  • And, finally, silently self destructs and experiences unending peace in the great beyond

But the Dynamic Waves script isn't finished yet...

Step 7: Creating Paired Waves (And Friends)

Almost done. Now, the script sees if a Cherub (a new support enemy) wave is paired with the wave (at no extra charge!). This is something that happens very rarely. Next, paired spawns are handled. If it was determined earlier that the wave would be a paired one, it runs the dynamicWaves script again, this time with instructions to create that specific enemy wave with reduced quantity (and with formations excluded).

What Else Can It Do?

Really mean fun stuff. I'll show you.

spawnEnemies("bulwark", undefined, undefined, undefined, undefined, 2, undefined, undefined, EnemyBehavior.Flank, "myrmidon", 8, 75, 24, undefined, 20, undefined, "hammerhead", 360, "cherub", 2);

This piece of code would do the following:

  • Spawn a Bulwark (big reflecting tank enemy)
  • Flanked by Myrmidon enemies (tanky followers that work well in formations)
  • That fire in a cascading pattern based on their formation position
  • Supported by a pair of Cherubs (support enemies that can create shields)
  • With the rule: Until the Bulwark is destroyed, spawn Hammerheads (kamikaze bullies) from the edges of the battlefield every few seconds

Of course, this is a pretty mean wave. Probably wave 200+ material...

SO! What's Left To Do On The Update?

While procedurally generated waves do add massive variety to Nova Drift, I ultimately decided that I did not want to have every possible wave (except bosses) be this unpredictable. There are some particularly mean or wacky things (like the above example) that are much better handled as "mostly curated" waves. I'd like to create a system that takes an array of such handcrafted waves and scales them based on a few simple variables like wave progression. The player would encounter these waves at wave multiples of 5 or 10, instead of Dynamic Waves, when the player is not facing bosses.

Also a bunch of other new stuff is coming that needs wrapping up...

Got a whole mess of achievements coming, too.
And there's more where that came from!
OMG HUEG

Want to Help Balance Dynamic Waves?

As you might imagine, this is a pretty hard system to get right. We want to add all kinds of variety without alienating new players. To that end, I'd like to run a long beta with both new and old players trying out the new system. I'll need all the help I can get, so if you're interested, please keep an eye out over the next month for announcements regarding a new build. We'll do an experimental beta followed by a more public beta. I'll share instructions on how to see all of the wave log output, and I will be very receptive to feedback and suggestions. Our Discord will have a channel dedicated for beta discussion. On that note...

Discord Upgraded! Come Hang

We've got quite an active Discord with a very chill and inclusive community. The dev team is also pretty active there. We recently upgraded the server to level 2 and opened some new channels like #ask-the-devs and #feedback. The latter allows you to submit feedback to a bot that lets players upvote or downvote the suggestion. I'm also quite proud of the #game-dev-help channel, which is a space for current and aspiring developers to learn and collaborate. So... come chat with us!

We also brainstorm some pretty crazy ideas there, sometimes:

Thanks For Reading

It's been very interesting and extremely challenging crafting this system with the enormous help of our resident "Code Elf", Ultraken. It pulled me wayyy out of my comfort zone, it took half a year to create all the related assets, and I learned an almost overwhelming amount. When I started Nova Drift 7 years ago I was coming from an art background and was barely able to code, relying mostly on Game Maker's "drag 'n drop" functions and forum-copy-pasta. I feel like I've come a long way, this may be the first time I wasn't entirely embarrassed to show the "guts" of our game. Thanks for reading the blog and being a part of that journey with me. I'd also like to thank the other coders on our Discord that were able to provide feedback and guidance.

Want to help Nova Drift grow?

Steam reviews really help! As a tiny operation, Nova Drift relies on its quality and reputation to sell, and reviews also encourage the Steam algorithms to show the game to more people. Please do take a moment to fill out a short review, even if it's only a few words. It just might make a big difference.

Best wishes,

Chimeric

Jeffrey Nielson