Similar to my first Cowboy Turbo project, Water Balloon Drop 2D features a variety of entities (pedestrians, cars, etc.) that spawn at certain times from certain positions. I’m not really confident that my implementation is very elegant or efficient, but here’s how it works in WBD:

ig.WaveManager is a custom ImpactJS class consisting of the hardcoded wave information for all levels in the game, the current level information, and a spawnOnTime() function. The hardcoded wave information is simply a bunch of objects–one per level–each with three properties organized as arrays: enemies, times, and sources. That means you’ll have a level1 object and a level2 object and so on.

The enemies array is just a list of strings, chronologically listing the types of entity to spawn (‘EntityEnemyCop’, ‘EntityEnemyPregnant’, etc.). The times array is what it sounds like: at which times (in seconds, relative to player spawn time, aka time elapsed since current level load) should those enemies spawn? And sources just lists whether it comes from the left side of the screen or the right side (either -1 or 1).

So for example, if I want to spawn the pregnant lady from the left side of the screen 3 seconds into the level, then a cop from the right side 1 second after that, it’d be:

That’s the hardcoded wave information. Now there are three simple arrays as well in the wave manager. You guessed it: levelEnemies, levelSpawnTimes, and levelSources. Let’s call these arrays the “current level info” trio. These store the most up-to-date values for the enemies that need to spawn on the player’s current level. They are initialized empty. At the start of each level, we need to look at the level1 object from above and dump all that information into the appropriate arrays. Who’s responsible for doing this? In my case, the loadLevel(levelNumber) and reloadLevel() functions from the indispensable Director plugin, which I modified slightly.

What happens is that every time a level is loaded or reloaded, the Director now populates the “current level info” trio using the appropriate level object (level1, level 2, etc.). The code isn’t pretty or elegant since it uses a hardcoded switch, but it looks a bit like this:

So if we’re loading level1 (or reloading level1), take the Wave Manager’s hardcoded level1 object we mentioned earlier and divvy up all that info into the three respective “current level info” arrays.

To actually take this info and use it to spawn enemies, the spawnOnTime() function is run every frame (in the main game’s update() function). It basically looks like this:

At this point, the level is loaded, and the three “current level info” arrays are filled correctly. Now every frame, this function checks to make sure that playerLifetime.delta(), or the amount of time in seconds since the start of the level, is greater than the first element in the levelSpawnTimes array to make sure it doesn’t spawn a billion of them the moment the level loads.

It also makes sure that there are indeed any enemies remaining in this level, or else it just chills out and skips the following.

From there, we just do a regular ImpactJS spawn call. Spawn the first enemy in the list at an X value on the left or the right side of the screen. That really ugly, long line is my attempt at somewhat elegantly handling the left/right side. Remember that sources are either -1 or 1, so I needed a way to either spawn an enemy at X-position 0 if it was -1, or at an X-position equivalent to the screen width minus 12 pixels (or just the screen width, if you remove the second half of that line). I just did the minus 12 to give a bit of that old-school “sudden choppy spawn” look, so the sprite partially appears suddenly on-screen, as opposed to sliding in from off-screen slowly.

The Y-position is just hardcoded as 24*7 (24×24 tiles; 7 tiles down from the top of the screen).

The final part is an object that ImpactJS by default refers to as settings, which is basically a placeholder argument for any extra stuff you want to pass to your init(x, y, settings) function for entities. In this case, I’m passing a quick leftright property, which just takes the -1 or 1 from sources. I like recycling variables.

This leads to our grand-daddy EntityEnemy class, from which all enemies (cars, people, etc.) in this game are extended/inherited. I just add a quick check to set a property called this.flipped to true if settings.leftright == 1. All my art assets by default face right, so if an enemy is coming in from the right side (sources would be 1, meaning leftright would be 1), then we have to flip it so the sprite is facing left. Later, in the draw() function, I do a simple check to set this.currentAnim.flip.x to true if this.flipped is true.

Finally, to wrap up the spawnOnTime() from earlier, we just use splice to remove the very first element from each of the three arrays. In other words, we’re done spawning that specific enemy at that specific time from that specific side of the screen, so remove that information forever because we don’t need it anymore.

Whew! Kind of long-winded and probably unnecessarily complicated for a wave manager, but I like to think it wasn’t bad for what was more or less my first game programming challenge during the whole process.

Leave a Reply

Your email address will not be published. Required fields are marked *