Episode 16: Enemies

In episode 9 I talked about the entities in the game. In the next few episodes I’m going to dissect them in a bit more detail. Note that I already discussed enemy AI in episode 12, so I’m going to skip it here.

Presets

As I mentioned in other episodes, enemies are vessels that are created by the EntityFactory from presets. Because I wanted to keep the game simple, and also because the menu only has room for 16 items, I tried to come up with a naming scheme for the presets that would be kinda consistent and give a hint as to what the enemy is. Here are the presets I ended up with (plus a little description of what they do):

  • Scout: a small, fast ship that flies past the player and has little or no weapons
  • Patroller: a small, moderately fast ship that flies past the player, turns toward the player, and has decent weapons
  • Raider: a somewhat small ship that moves laterally toward the player, stops in front of the player and fires its weapons for a bit, then flies past the player
  • Instigator: a medium-sized ship that moves laterally toward the player, stops in front of the player and fires its weapons for a bit, and fires powerful weapons
  • Frigate: a medium-sized ship that moves laterally toward the player, flies past the player, and fires powerful weapons
  • Cruiser: a large, slow ship that flies past the player while firing powerful weapons
  • Destroyer: a large, slow ship that doesn’t move forward at first, moves laterally toward the player while firing its powerful weapons, then flies past the player
  • Warship: a very large, slow ship that doesn’t move forward at first, fires very powerful weapons, then flies past the player
  • Kamikaze: a somewhat small, fast ship that flies past the player, moves laterally toward the player, turns toward the player, and has no weapons
  • Transport: a large, slow ship that flies past the player and has no weapons
  • Barge: a very large, slow ship that flies past the player, moves laterally away from the player, and has no weapons
  • Mortar: a small turret that scrolls past the player while firing decent weapons
  • Artillery: a somewhat small turret that scrolls past the player, turns toward the player, and fires powerful weapons when facing the player
  • Gunpod: a medium-sized turret that scrolls past the player and spins in circles while firing powerful weapons
  • Howitzer: a medium-sized turret that scrolls past the player while firing powerful weapons

Here’s what some of the enemies look like:

Scout

Frigate

Warship

Kamikaze

Barge

Artillery

 

Items & Stats

The items that these enemies use are actually the same items that the player uses (except turrets, which have ships the player can’t get). However, I had to make it a fair fight, so I added multipliers to make the enemies weaker. For example, I made the enemy weapon damage about 10% of what it would normally be. Here’s how it worked:

This was a decent solution, but I ended up getting rid of the multipliers and going with minimum and maximum stat values based on the enemy’s level, then multiplied according to its rarity. This let me more tightly control the stats of enemies more precisely. Here’s how it works:

Enemies also have a value, which is the amount of money the player receives for destroying the enemy. They also have a chance to create a drop when killed. I’ll talk more about the game’s economy and drops in coming episodes.

Episode 15: Factions

I briefly mentioned factions in the first episode, but I feel like I should explain exactly what they do and how they work.

Overview

There are five factions in the game, and they influence the story, visuals, items, and economy of the game. Each faction has its own history, color, music, item names, stat boosts, relationships, names, and currency (except the Freelancers). Here’s a brief overview of the factions and a few of their features:

  • The Empire
    • Story: Has ruled the galaxy for a long time and seeks to maintain order
    • Color: Blue
    • Currency: Credits
  • The Revolt
    • Story: Rose up to fight against the tyranny of The Empire
    • Color: Red
    • Currency: Crystal
  • The Freelancers
    • Story: Embraces freedom and economic development
    • Color: Yellow
    • Currency: N/A (Accepts all other faction currencies)
  • The Preservers
    • Story: Seeks to live in isolation and govern itself
    • Color: Green
    • Currency: Honor
  • The Body
    • Story: Seeks to extinguish all life in the galaxy
    • Color: Purple
    • Currency: Blood

Color

The color of the faction is used to determine the color of some game text as well as item sprites. Here are a few examples:

  • Projectile sprites and particles
  • Ship sprites
  • Shield sprites
  • Engine particles
  • Currency text

Item Names and Stat Bonuses

Factions also affect items. Item names are kinda complicated and give a window into the items function. The name is actually derived from the rarity (and bonus), faction, and preset. For example, an Elite Missile of the Empire is a weapon with the Legendary rarity, the Empire faction, and with the Missile preset.

As for the stat bonuses, the faction increases and decreases certain stats on each of the item types and presets. These changes are minor, usually only 5 or 10% of the maximum stat value, and they apply to all items of the same type (all weapons get the same faction bonuses). The changes also don’t really follow a pattern across item types, but merely serve to make items more diverse. Let’s look at the Empire’s stat bonuses for weapons:

  • Hull Damage -10
  • Shield Damage +10
  • Rate of Fire -10
  • Range +10
  • Blast Radius -15
  • Projectile size -15
  • Projectile Speed +15
  • Efficiency +15

Currency & Relationships

I also had an idea for a money system that uses multiple currencies, kind of like Secret of Evermore does. Basically each faction has its own currency, and they value each other’s currency based on how they feel about that faction. As an example, The Empire hates the Revolt, so they value Revolt currency lower than their own. So if you want to spend Crystal at an Empire world, you’re going to have to spend more to buy what you want.

There is an exception. Freelancers have no currency of their own, but they accept all other forms of currency.

The goal of this system was to make a version of a kind of reputation system you’d see in an RPG. It discourages the player from simply farming one form of currency by beating the same level over and over.

Vendor and World Names

Another way that factions flesh out the game is through the names of the item vendors and world names.

The worlds are named based on which faction they come from. I tried to give them each unique themes. For example, the Empire’s worlds all take their names from ancient cities.

The vendor names are randomly pulled from two pools of names, first and last, and follow similar themes.

Episode 14: Saving & Loading

Hopefully this episode is short, because all I’m going to talk about is saving and loading player data.

Web Storage

As I mentioned, I used JavaScript to write this game, and it turns out that modern browsers support a feature called Web Storage, which lets you save small chunks of data on the user’s computer. Here’s how saving works:

And loading:

I love it when things are simple and easy to use.

The next step was to organize these functions in a factory, as I’ve done with all my other code. I ended up with these functions:

  • new
  • load
  • save
  • reset

I later realized that I didn’t need reset, because it’s just a combination of new and save.

Anyway, once I had the factory working, I just set up the calls to the the appropriate functions during gameplay (mostly from the menu).

One tricky thing is keeping track of the data you are saving and loading. After all, you may want to store a bunch of different things, and you may want to save or load each of them separately. Here are some of the things that I store:

  • Progress
  • Items
  • Money
  • Custom missions
  • Settings

Potential Problems

One choice I made that was probably a mistake was to store the player’s data in a global variable. I probably should have written functions in the storage factory that gave access to this data. Perhaps I’ll change it in the future.

Another problem I’m anticipating is that game data may become corrupted as I make changes to the game. I imagine a good solution would be to add a version name to the local storage key (like Zyrian 0.41), so that any player who accesses an updated version of the game starts with new data.

This could end up aggravating players who lose their data, so it would probably be smart to write some code that checks if the existing Web Storage data is compliant with the current version of the game. Now you can see why I haven’t done this yet.

Episode 13: Movement

In episode 3 I gave an example of how the player might be moved around the screen, but this isn’t exactly what I ended up doing. While I initially had some directional movement functions, moveUp, moveDown, moveLeft, moveRight, I realized early on that this wasn’t really the best way to approach things. Before I explain why I had to change things, let’s take a look at how my moveUp function initially looked:

Now there’s nothing wrong with this code if you just want to have a sprite move around the screen. But what happens when you rotate the sprite?

The Rotation Problem

Think about it – when the player moves up, the player’s vessel is moving forward (because it’s facing up). But if enemy vessels are facing the opposite direction, then the moveUp function would move them backward.

One solution could have been to simply have the enemy moveUp and moveDown functions do the opposite of the player’s functions. I could have also just switched the calls to the moveUp and moveDown functions in the AI logic. But what if wanted to have a vessel that isn’t facing up or down? What if an enemy is flying in from the side?

If it hasn’t become obvious what needed to be done, think about this: does a car navigate using cardinal directions (north, south, east, and west)?

The answer could be yes, but it’s actually no. A car can’t really drive west. There’s no button or lever to pull that makes the car suddenly go west. The car does have a steering wheel and a gas pedal, which can be used to point the car west and then accelerate it, but the car’s actual functions in this case are turning and accelerating.

So this is what I needed to do to my vessels. I needed to give them functions that matched their actual abilities. A vessel doesn’t fly down, it flies forward. And when it flies left, it flies to its left, not to the left of a player overseeing its movements. Here’s what a moveForward function would look like (warning: math ahead):

You’re going to have to put your trigonometry hat on, but what we’re basically doing here is taking the sprite’s vector (direction and magnitude) and breaking it down into its x and y components, then applying those components to the sprite.

After I got this working I was super proud of myself for being smart, but this was followed by a lengthy period of feeling stupid. You see, there’s actually a problem with this code, but only because of the way I used engine stats to control the vessel’s maximum speeds.

Maximum Velocities

The problem is that there isn’t just one limit on how fast a vessel can travel, there’s two. Because engines have vertical and horizontal stats governing acceleration and max speed, vessels can actually max out their speed laterally without maxing out their vertical speed, which means each limit needs to be applied individually.

I have to admit, I was stumped. This is actually the first and only problem I ever posted on a forum (you can read the thread here)

Here’s how it ended up working (in general):

Now my vessels all moved properly no matter what direction they were facing! Hurray!

Episode 12: AI

In addition to building entities, the entity factory also sets the AI (artificial intelligence) of said entities.

I have to admit that I knew nothing about AI programming heading into this, and by the end of it I still didn’t know anything, but I did have a working AI system!

The way I implemented AI was to simply write functions for each type of entity that checks the AI settings in its preset to see how it should behave. Here’s an example of the AI settings of an enemy:

The AI function of the entity is executed by the play state’s update function (once per frame). The function looks like this:

Using nested conditionals and switch statements usually isn’t a good way to do anything, but it was the most obvious solution. Plus the behavior is pretty basic, so I didn’t want to waste too much time on it.

Here’s how the preset at the top of the page would end up working:

  1. The entity moves laterally to line up with the player
  2. The entity moves forward
  3. Once the entity is close to the player, it stops moving forward and starts firing

Pretty neat, huh?

The obstacles and drops just scroll across the screen, but the enemy AI seems to work pretty well. I added a few more features, like having enemies try to use their modules or turn toward (or away from) the player.

I’m also pondering whether or not to have enemies use their secondary weapons. Enemies are vessels, and vessels have both sets of weapons, but how would they decide what set to use? I guess they could alternate between the two, but what would determine when to alternate? So many questions.