Galaxy Busters

Inspiration

Galaxy Busters is a bullet hell (like the Touhou Project) space shooter, where the player character is a spaceship at the bottom of the screen shooting at enemy spaceships which either come from the top or sides of the playable area. The player can collect items to become stronger.

This was done for a game development course as my final (team) project. In the first five weeks we learned about the game engine we would be using, Unity. In the following five weeks we would brainstorm, design, prototype, and build our game. To keep us on pace there was a prototype demo along with alpha and beta playtests. The final playtest came the week after where we were expected to deliver a full, fun, bug-free game.

GamePlay

Players progress across 5 levels where enemies get progressively stronger. To combat this, they'll receive special items at the end of each level that increase their stats or give them special abilities. In the final level, the players will face a boss that tests their skill and item collection. Players will have to develop a good understanding of the patterns in each level and also lighly test their luck in order to get the highest score in the game.

My Contributions

I worked on several aspects of the game, such as the menus and refining certain bits of the UI, but my main contributions were to the implementation of the player characters and the inventory system.

Component-Based Architecture for Player Characters

In Unity, everything (cameras, buttons in the UI, NPCs, player characters, etc.) are represented by GameObjects. GameObjects are composed of several Components, which collectively define the behavior for that GameObject. For example, for a player character, you could have a

  • A Transform component to represent the GameObject's position and orientation.
  • A Sprite Renderer to display the character's sprite.
  • An Audio Player if the character plays some noise.
  • A 2D Collider for handling collisions with other GameObejcts.
  • etc...

The ones mentioned above are some of the many components that Unity comes with. You can create your own components by writing a script that can do almost anything. You can access the Transform component to move the character, you can change the sprite through the Sprite Renderer and you can create event handlers for when a collision occurs, etc. For the characters in our game, I needed to code the following:

  • Movement for the characters.
  • Event handlers for collisions (running into an enemy, getting hit by a bullet).
  • The weapon for the character.
  • The basic and ultimate abilities for the character (e.g., spraying bullets in a cone, a shield, etc.).

This all could be done in one script as a single Component, but this isn't ideal for some reasons. One of them is that the script would be massive as it's incorporating all the character's behaviors, making it more difficult to maintain. Also, the game has 3 different playable characters, some of which behave the same, while others behave differently. For example, the code for the movement and weapon was the same for all of them. Creating 3 different scripts, one for each character, with the same code for these parts isn't ideal because if you encounter a bug, you need to fix it in 3 places.

So, I decided to split these parts into their own components - movement, colliders, basic and ultimate abilities, and the weapon. I also put a parent component containing references to all the children. If a child needs to communicate with one of its siblings, it must obtain a reference to them through the parent first.

This approach was nice because as mentioned before, the code for the movement and weapon are the same across all three characters. This allows us to create one script defining these behaviors, encouraging reusability and improving maintainability.

Inventory System

Another of my contributions is the inventory system. As I mentioned before, at the end of each level players can choose a new item that could boost their stats or grant them some special ability. I needed to make an inventory system that keeps track of these items and performs the necessary upgrades to the player character. I also needed to find a way to represent the items themselves.

Representing Items

To represent items, I used a feature of Unity called ScriptableObjects. In a nutshell they're data containers that can be used to save large amounts of data. Here, I used them to represent my items. They had a few pieces of data that would collectively form a unique ID that could be used to tell what type of item it was and what sort of upgrade to perform (be it a simple stat change or modifying the behavior of the weapon, for example).

The Inventory

Adding an item to the inventory was very straightforward. First we check if the item is valid, then, we can use the item ID to determine what upgrade it needs to make to the player. This was just a matter of going through a if/else if/else clause.

Some of the feedback I got from my peers when they were adding items was that working with this system was very easy. All they'd have to do is create the item, specifying a unique ID, and updating the if/else if/else clause in the inventory to account for the new item.

Challenges

Here are some of the challenges I faced.

Merging Code

One of my responsibilities was merging all our code (we used Git and GitHub). Git would always merge code files just fine. However, any of the special Unity files almost always caused merge conflicts. Usually, these files are binaries but you can set Unity to use text-serialization instead for VCS. Even then, when you made changes to these files, Unity would shift things around in a way that Git wasn't able to make sense of and resolve.

What I ended up doing for each conflict was keeping what was on the master branch, commit the merge, then enter the Unity Editor to fix the assets with conflicts and commit those fixes before I could finally merge. It was a strange issue, but this gave me more experience with manually resolving merge conflicts.

Player Characters

Another challenge I had was with the components for the player characters. As mentioned before, the player characters had a parent component and several child components that would collectively define the behavior for the player. Something I assumed when implementing this is that the parent component would be up before the children, but Unity doesn't guarantee anything in terms of the start-up order of components. This meant sometimes a child component would start up and expect the parent component to be up when it wasn't, causing several errors. The fix was relatively simple (having the children wait for the parent to be up), it just took time to determine the issue.

Item Selection Screen

One of the other things I worked on was the Item Selection Screen that was presented to the user at the end of each level. The main challenge here was determining what items to present to the user, keeping track of which items they already had.

What I did to solve this was create an Item Catalog to go along with my Item Inventory. The catalog kept track of all the items that weren't in the player's inventory, and had methods to retrieve a random set of items to be presented during item selection. So at the beginning of the game the catalog is full and the inventory is empty, and as the game progresses, the catalog loses items and the inventory grows.

One of the downsides to this approach is that the catalog loads all the items into memory, which wasn't a problem for our small set of items, but if we had a large set of items it could result in longer loading times for the game and more memory usage. A better approach would be to load in more items when the catalog runs low, rather than all at once.

What I Learned

Learning Unity was a fun experience. Similar to something like Git, it has a steep learning curve and in the end you develop a love-hate relationship with it.

This project was also a completely different experience than what I'm used to. Normally, for school assignments and most of my personal work I'm given a blank canvas: I can come up with my own design for the software with the only limitations being those of the language itself. In Unity, it's different. There are several systems such as the GameObject and Component that have their own constraints and force you to do things a certain way. This provided an interesting challenge that required more research and planning than usual.

Play the Game!

You can play the game here, view the trailer here, and view the source code here!