25 Jan 2016

LibGDX Jam Retrospective: Always Have A Plan

I recently entered the LibGDX Jam on, and I've decided to do a write up on my experiences working with the engine and with developing in the month-long jam in general. I wound up with a great learning experience for the LibGDX engine and reinforcing some general software design lessons, which all in all made for a very worthwhile thirty days. With that out of the way, it's time to dissect my experiences developing for the jam and relay those important lessons!

Planning is Essential

This is the first and most important lesson of this jam, and basically any project: Always have a plan, make a design for your project before you begin coding. The design may change while you're working if you find parts of it unworkable, but it's important to have a blueprint with a feasible set of deliverable features that you can work off of. Without that, development can quickly become disjointed, muddled, and disorganized. Relating this specifically to my project, this was my first time using LibGDX in any capacity and I haven't written any Java in years. So I downloaded Android Studio, set up my LibGDX project and started following some simple tutorials on setting up a Gradle desktop build. 

I learned how the LibGDX project was laid out and how to use various engine features like Screen interfaces and the Gdx object, but I didn't enter with a solid design for what I was going to make. One of the rules of the jam was that all source code has to be open sourced and linked to the jam submission page. My Github repository can be found here

Plan within your means

If you play Antares Trail and then look at the code in my repository, something you might notice quickly is that there are building blocks for a number of unimplemented features in the game. You see, Antares Trail was originally conceived as an amalgam of classic Galaxian shooters like Galaga or Gradius, and Oregon Trail which was a popular American educational game about crossing the old American west in a horse drawn wagon to reach Oregon. The player would sele
ct one of three ships and stock up on supplies for the journey, and then fly ahead of the transport ship fighting space pirates between stops en route to a colony in the Antares system.

In the released version of the game, the player chooses one of three ships and then flies between a few locations fighting space pirates before finally arriving at your destination. The purchasing of items for the passengers on the transport, the possibility that passengers would become sick or otherwise injured during space travel, and the requirement to manage the passengers in between space flight levels during stops all wound up not in the game. They are in the code, though:

This is the GameData JSON object in a save file for Antares Trail
As you can see above, the save data for an Antares Trail game tracks a bunch of information about fuel, food, passengers, money, etc. All kinds of things that would've been important to the management side of the game that I didn't get to put in, which brings me to the point about planning within your means:

If I could say anything to myself circa 18th of December, 2015 it would be this: "You only have a month". Over the course of the jam were holidays with family, a day job, home upkeep, and generally everything else that can come up in day-to-day life. This was my first time using LibGDX and I had also decided to personally create all of the assets for my game (aside from the font). I planned a very ambitious game for my first outing with a new engine, and I didn't do myself any favours with a thirty day deadline.

It's very important to be realistic in setting goals for a project. Weigh out the deadline, what features you want, what your level of skill and knowledge is with the technologies involved, and build your design accordingly.

Make Plans From the Ground-Up

One issue that came about in developing my project was that what plans I had all revolved around a set of gameplay features, but didn't account for making a good object-oriented design using patterns. The result was a set of features haphazardly implemented in the order I learned to implement them, and (thanks to a lack of familiarity with Java) some bizarre looking design choices later in development. The most glaring among these choices came in the refactoring of my ISpaceship interface, which was helping to build code for enemies and the player around behaviours rather than one concrete implementation, in a Spaceship  parent class from which all others derived. This all would have been avoided if I'd been aware of Java's "instance of" keywords. Alas, I only knew of ".getClass()".

The reason that bit of code was so important was for collision detection. Every object in the space flight portions of the game implemented an ICollidable interface, and had a Rectangle object used to check for overlapping objects in the CollisionSystem during gameplay. The colliders needed to be able to report what type of object they were attached to in order to call it's appropriate methods when their "OnCollision()" method was called. I refactored the interface into a parent class because ".getClass()" always failed checks against ISpaceship, as it wasn't a class at all. This cost a lot of time and made for a lot of incongruity in the way each object type was being designed. 

Getting back to the point, the lesson here is to make your design using the building blocks of what your finished project will be. That may sound pretty obvious, but it's very easy to overlook the importance of selecting a set of patterns that will be the foundation of your design. It's also extremely easy to become overwhelmed at the first sign of difficulty and to abandon a better design for something that seems more obvious in the moment. Which leads into the next teachable moment of my jam development...

You're Probably Not Going Back To Fix That Thing

This is the code of someone in denial

Looking at my project now, I can count the times writing a class that I hard-coded a value and said to myself "It's fine, I'll go back and fix it later. This is just to see how it works", I'm not going to count them though, because I did it a lot. Past-me was in serious denial about going back and correcting a poor practice later, and should've just made variables in every instance. Instead I wound up with code that was more and more difficult to expand upon because every time I had to update a value in testing it had to be updated in several different places. 

This applies to more than just variables. As I mentioned earlier, there's a lot of groundwork code present in the project files for unimplemented features. Starting with the core components of a game (or any application, really) and adding optional features later is a much better way to go, rather than throwing out a series of half-baked ideas and determining to finish them up later when there's time. There might not be time, especially when you're looking at a one month development cycle.

Use Patterns

Originally I'd planned to use the Strategy pattern to separate behaviours from the objects implementing them using interfaces. I wound up using a Component pattern later in development than I should have to get collision detection working. The game would've benefited from using Component from the start, specifically Entity Component System, but really any pattern oriented design would've helped keep things simpler and cleaner. Patterns make life better by making code easier to maintain as development wears on, and they should be kept in mind whether a project is using unfamiliar tools or your favourite language.

In Summary

Plan. Always plan. If you don't have a design plan, make one immediately. Make sure to consider at least a few options for tackling development goals, and then make a plan using the one that seems best. Planning out projects ahead of time is essential to good, maintainable development that can be used and reused going forward. 

With all of that said, I made a bunch of assets for the jam, including music. So to cap off the retrospective, here's the soundtrack of Antares Trail. Thanks for reading!