Playing and Submitting Community Missions

How to Play a Mission

  1. Open the Community Missions page
  2. Launch a mission by clicking Play This Mission under one of the mission headings
  3. Select New Game or select Continue if you’ve played (and saved) before
  4. Select Mission Editor
  5. Select Load Link
  6. Select Launch

How to Submit a Mission

  1. Click here to launch Zyrian in the browser
  2. Select New Game or select Continue if you’ve played (and saved) before
  3. Select Mission Editor
  4. Select Edit
  5. Choose the Level, Faction, Rarity, Size, and Speed
  6. Place the Enemies, Obstacles, and Drops
  7. Select Submit
  8. Fill out the form and click Submit

Episode 20: Controls

While I initially set up some simple keyboard controls to select menu options and move the player’s ship, I realized early on that things were gonna get a little complicated. This was for two reasons: first, that I was going to need different controls for each of the game’s states, and second, that the game was going to need mobile-friendly controls.

The Control Factory

Like most other systems, I created a factory to manage the game’s controls. This factory does a few things:

  • Loads the controls based on the game state and device type
  • Checks for input based on the current controls
  • Executes appropriate code based on input
  • Draws and updates the joystick and buttons
  • Enables or disables input

Loading Controls

I probably could have written one function that accepted the state key as a parameter, or just checked the current state, but I broke it up in to different functions to make it more readable. This is the function that loads the controls for the play state:

Checking Input

We then check controls by calling checkPlayControls from the update function in the play sate. This function is really long and complicated, but it basically does the following:

  • If input is disabled, return
  • Update the joystick and check its position
  • If the joystick is being dragged, move the player’s ship
    • Otherwise slow/stop the player’s ship
  • If we’re using a desktop, check if the keys or buttons are being pressed
    • If they are, move the player’s ship, fire weapons, etc.
  • If we’re using mobile, just check if the buttons are being pressed
    • If they are, move the player’s ship, fire weapons, etc.

These steps are executed in a complex series of conditional statements. This was a little tricky because the player could be entering multiple inputs, so we want the right things to happen in the right order.

For example, if the player is pressing the up arrow key, we want to move the player’s ship up and slow it’s lateral movement. But if the player is pressing the up arrow and left arrow keys, we want to move the player’s ship up and left. On top of that, we want to animate the joystick and buttons based on this input. This is why I used the isDown flag on my buttons rather than have them manipulate the game directly.

The Buttons & Joystick

Buttons are a built into Phaser and work in a pretty simple way. You basically create it the same way as animated sprite, except the different frames are drawn based on the button’s state (hovered over, not hovered over, pressed, or unpressed). Here’s how the code looks:

Once the button is created, you bind some callback function to the button, which executes when you press it. This is what we did earlier with the setButtonEvents function.

One decision I made was to have the buttons and joystick animate when the player entered keyboard input. I basically just felt like this would make the game more interesting to look at. Plus I had to draw those things anyway, so why not make them do something?

I was originally using four directional buttons for movement, but I thought it would be better to use a joystick. I looked around at some examples and eventually decided to make my own. I did this for two reasons: first, because I wanted to learn how a joystick would work, and second, I needed the joystick to get both analog and digital input.

The analog input is used to let the player move in multiple directions at once and to accelerate at different rates depending on how far the joystick was dragged from its center position. So if the joystick moved all the way to the left and slightly up, then the player’s ship would accelerate left the maximum amount and accelerate forward slightly.

The digital input is used to navigate the menu and also to reflect keyboard input, which uses the arrow keys, so it can’t be analog. The joystick functions more like a set of four directional buttons in this scenario, because we want to register four different behaviors (move up, move down, move left, move right). Basically it works like this:

If the player drags the joystick or clicks in the joystick area, the joystick will move to the nearest directional area (top, bottom, left, or right) and execute the appropriate function. If the player presses an arrow key, the joystick will move to the corresponding directional area.

One other note: pressing multiple keys at the same time will cause the joystick to move to a location representing both inputs. For example, if the player presses the up arrow key, the joystick will move to the top-center area and fire the moveUp function (or whatever function it’s set to execute). If, while still holding the up arrow key, the player then presses the left arrow key, the joystick will move to the top-left area and fire the moveLeft function (or whatever).

The joystick ended up being a little complicated to build. Here were a few other things I had to deal with:

  • What happens when the player drags the cursor outside of the joystick area?
  • What happens when the player presses opposing directional keys at the same time?
  • What happens if the player holds down two buttons at the same time?
  • What happens if the player holds down the joystick or a directional key in the menu?
  • What happens if the player holds down the joystick and a directional key?

Pausing

One more thing I had to deal with was pausing the game. Phaser has a built-in pause feature, but there’s just one problem: Phaser’s buttons and keys don’t work when the game is paused! The left me with two options: pause the game myself or find another way to check for input. I ended up going with the second option.

The reason I didn’t try to figure out how to pause the game without pausing it is that I didn’t feel like I understand the physics, rendering, timers, and other systems well enough to mess around with them. Also, I had a much better understanding of getting user input in JavaScript. Anyway, here’s what I ended up having to do:

  • Add input events to check for tapping or clicking
    • If a tap or click event occurs, check if a button was pressed or if the joystick was moved, and if so, execute the callback function
  • Use setInterval to check for joystick input every 60th of a second (because update functions aren’t called when the game is paused)
  • Check for keyboard input by checking event.keyCode
    • If a key was pressed, execute the callback function

Unfortunately audio and animations are also disabled when the game is paused, but the only time I actually pause the game is to bring up a menu that lets the player abandon a mission, so it’s not a huge deal.

Episode 19: Loading Assets & Settings

In earlier episodes I talked about loading assets and storing magic numbers in JSON files. I’m going to expand on that a bit by showing how I chose to load assets.

I began by moving asset names into a JSON file and divided them by which state they would first be used in. There are a few reasons why I did this:

  1. I didn’t want any magic words in my code
  2. I wanted to keep a single list of the game’s assets, so it’s easy to update
  3. I only wanted to load assets when I need them

Magic Words

Here’s an example of the first issue:

Just like we don’t want to hard-code numbers into our game, we don’t really want the fireWeapon function to choose the sound to play when a weapon fires. What we want is to have the code to play whatever sound we’ve decided should play when a weapon fires, which should probably be stored in a JSON file. Here’s what that would look like:

It works, but what if we wanted to have different weapons make different sounds when they fire? Well then we simply store the fireSound in the weapon’s preset and attach it to the weapon object when it gets created by the Item Factory. Then we can do this:

Assets.json

As for storing all of my assets in one place, I ended up putting them all in a JSON file called assets.json. This file contains an object with an object inside it for each state (or each state that requires assets). And each state object has an array for each type of asset. Here’s how it looks:

Now all of the keys I use to access the assets are in one place. This lets me easily refer to the list when choosing assets such as a weapon’s firing sound.

Loading

Part of the reason for the structure of the JSON file was to easily allow it to be traversed by a loading function. I wrote just such a function in the Load Factory. Here’s how it worked:

It’s a little ugly, but it does the trick.

States

The file structure and loading function allow me to choose which assets are loaded in a given state. So rather than having the player waiting around at the start of the game for all of the assets to lead, they just wait a shorter time between states.

Any asset loaded into the game stays there (as far as I know), so assets are only loaded once, and they remain in memory across states.

I probably could have done this more efficiently by loading smaller versions of assets when the full version isn’t required, but my game doesn’t take long to load, so it didn’t seem worth it.

Settings

I’m gonna take a bit of a turn and mention how I loaded the game’s settings. As I mentioned, I moved all of the magic numbers into settings files. But in addition to this, I also changed settings to be based on the game’s screen size (when applicable), so that the game could be easily scaled.

So rather than have a sprite’s width be 10 pixels, it would instead be 2% of the game width. I’m not sure if this was a good idea, but it made more sense to me than hard-coding sprite sizes, world boundaries, etc. into the settings.

Episode 18: Drops

Alright. We just covered enemies and obstacles in the last two episodes. Now I’ll talk about drops – the final and least interesting type of entity.

Purpose

Drops are like obstacles in that they basically just scroll past the screen. However, instead of dealing damage, drops reward the player for flying into them. Drops make the game more interesting in a few ways:

  1. They give the player something different to do other than blowing things up
  2. They create the chance of something rare and awesome happening (like picking up a super valuable drop)
  3. They require the player to think about which drops they pick up (more on this in a minute)

During the mission, drops are either generated by the mission itself or by blowing up enemies and obstacles. The player can then pick them up by contacting them with their ship, then cash them in (or in the case of items, equips them) after the mission. But here’s where the strategy comes in.

Cargo

Each drop the player picks up takes up one cargo slot on the ship. The number of cargo slots on a ship is determined by its Cargo Space stat. The max number of cargo slots, like other things, is limited by the menu at 15, but it’s very rare to find a ship with 15 cargo slots. So depending on the player’s available cargo slots, they may decide not to pick up a less valuable drop in the hopes of finding a better one later in the mission.

Types

There are two types of drops: material drops and item drops. Material drops basically just have value that the player gets for cashing in the drop after the mission. Item drops, on the other hand, contain an item that the player can view, then equip or sell. Here are the drop presets:

  • Carbon: a chunk of very cheap material
  • Titanium: a chunk of cheap material
  • Platinum: a chunk of valuable material
  • Berillium: a chunk of very valuable material
  • Diamond: a chunk of precious material
  • Adamantium: a chunk of very precious material
  • Item Capsule: a capsule containing an item

And here’s what they look like:

Rewards

Drops only have a few characteristics: level, faction, and rarity. These are then used with the preset to determine the value of the drop or its item. Each material drop preset has a minimum and maximum value, and the value chosen is determined by the drop’s level. That chosen value is then multiplied according to the rarity, like so:

The chance for a drop to spawn after destroying an enemy or obstacle is based on the entity’s preset, with larger, more powerful enemies having a higher chance for a drop. The entity factory then picks a preset based on the chance each preset has to spawn, just like rarity, so the more valuable presets have a lower chance of getting picked.

Cashing In / Bounty

As for cashing in the drops, the player is presented with a drops menu option after the mission (if they completed the mission and picked up any drops). I did this because I thought it would be fun and satisfying for the player to reveal what they collected during the mission.

I also created a bounty system that works in the same way. Instead of instantly giving the player a reward for destroying an enemy or obstacle, I just add that value to the mission’s bounty. This allows the player to view their total reward at the end, which is more satisfying, but also prevents the player from keeping the money if they fail the mission.

Other Ideas

I also dabbled with letting the player shoot and destroy drops, which would make the player more careful of where they’re shooting. But this seemed too annoying for the player, especially since drops are created after blowing up enemies and obstacles.

It was also hard to decide how to convey a drop’s value to the player through its appearance. I tried using glows and outline sprites, but I eventually just tinted the drop according to its rarity.

As for the items, I initially had the player choose between equipping and discarding the item, but I figured they should be compensated for finding an item even if they didn’t want to use it. I also wanted the drop’s value to be consistent with other drops, so the player only gets a fraction (about 10%) of what they would pay to buy the drop from a vendor.

Episode 17: Obstacles

In the last episode I expanded a bit about enemies, and now I’ll do the same for the second type of entity: obstacles.

Obstacles are like enemies in the sense that the player can shoot them and crash into them. But unlike enemies, obstacles don’t have items, a rarity, or a faction.

AI

They do have AI, but it’s pretty simple. Obstacles get a y velocity so they move past the player, and some of them rotate along the way.

Some obstacles move faster than others. The speed is determine by the base scroll speed and, as with enemies, a multiplier in the obstacle’s preset. Here’s what the code that sets the AI looks like:

Presets

In addition to speed and rotation, obstacles have a few other stats like size, health, invincibility, reward value, and crash damage. The shape of the sprite is also important. Here’s a list of the obstacles I ended up with:

  • Junk: a small, fragile piece of space garbage
  • Wreckage: a large, durable fragment of a destroyed ship
  • Comet: a small, fast space projectile
  • Asteroid: a medium-sized space projectile
  • Meteor: a large, slow, and durable space projectile
  • Explosive: a medium-sized, durable, and somewhat-powerful space mine
  • Frag: a medium-sized, somewhat-fragile, and powerful space mine
  • Detonator: a large, durable, and very powerful space mine
  • Gate: a medium-sized, long, and durable structure
  • Barrier: a somewhat-large, long, and very durable structure
  • Hub: a medium-sized, durable space dock
  • Terminal: a very large, durable space dock
  • Vault: a large, very durable structure
  • Saw: a large, powerful spinning device
  • Nebula: a large, powerful, and invincible space phenomenon
  • Obelisk: a long, powerful, and invincible structure

Purpose

These obstacles serve several functions. First, they provide a threat to the player. Second, the player gets a reward for destroying them. But third and most importantly, they add different settings and challenges to the missions.

By filling some missions with asteroids and wreckage, we can create the apparent aftermath of a fleet colliding with an asteroid belt.

Structures can be used to give the feeling of attacking an enemy base.

We can also use durable or invincible obstacles to build a path that the player must navigate. Some obstacles, like gates, barriers, and spires, are long and narrow, so they are good for making paths. Also, some obstacles that don’t spin can be rotated, which lets us control their shape.

Tune in next time when we talk about the third and final type of entity: drops.

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.