How I was able to finish Warrior Defense

As of writing, Warrior Defense is not really finished. Software is never finished. I’m still updating it. I haven’t released for iOS yet. Right now, I’m completing a web version. But the game is out there. Some people are probably playing it as you read this. I’m quite proud of that.

There’s a phrase in the game development world that says something like “It’s easy to start a game but finishing one is very hard.” This is very true. Just ask any game jam (our term for hackathon) participants. I’m writing this post in the hopes that it will help or at least inspire game developers to push on and finish their games.

Why?

I think the main driving force for finishing a game is the answer to the question “Why should I?” I always ask myself this every time I encounter a stumbling block be it technical problem, laziness, boring feature to implement, or loss of motivation. The answer to this question should be profound enough to make you jump up and feel motivated again.

“I should finish Warrior Defense because I want to prove that I can finish a mid sized strategy game on my own.” That was my reason. I want to present myself as a game developer in the strategy or action genre. My first game should probably be one.

Very early version. Naked dudes slugging it out.
Very early version. Naked dudes slugging it out.

Pressure

If you’re really committed to finishing a game, it shouldn’t be a problem if you let yourself to be in some pressure, right? Because if you don’t, then you’re not really committed. Guess what, you can impose pressure by yourself. I call them self imposed pressure.

The first in my list of self imposed pressure is joining the One Year Game Challenge last year with 5 times the minimum pledge value. 1YGC is a one year program by IGDA Manila to help developers finish their games. Each participant hands in a monetary value not less than 1000 to IGDA Manila. They get their money back if they released their game with a certain number of downloads within a year. If they don’t, the money is considered donation. I pledged 5000 for fun!

Another self imposed pressure is to tell people a release date consistently. Choose at least an exact month and year so you have an answer ready every time someone asks you. Believe me, a lot will ask you. Mine was June 2014 (but the game was released on August). What it does is it sets a deadline for yourself. You already told people, you better work on it or risk the perceived embarrassment.

Spending a lot for the game is another self imposed pressure. I bought lots of Unity Asset Store products that I could use in my game. Some of them are quite expensive. I also spent a big amount of my budget for artists and music. What this does is it tells you that there’s no turning back now. You’ve already spent this much, better finish that damn game!

Last of my list is posting the progress of the game to everywhere. I uploaded the game to Gamejolt then posted the game to various tech or gaming groups in Facebook. What you’re really doing is you put expectation to people that you’re making a game and you’re bound to show them a polished version eventually.

With these multiple self imposed pressure stacked on me, I should get my shit together and work on the game every time I can. If all fails, I go back to “Why should I?”

Release version. Looks spiffy.
Release version. Looks spiffy.

Luck

I can’t deny that I got lucky at times. Luck does have a hand in finishing my game. I was able to get really awesome artists to work with me for a “friendly” price. I’m very grateful to Robot with a Smile (Marvin and Shelly). I’m lucky enough to know Kane Aoki and the guys at Dualist. He makes awesome music for a very reasonable price. I hope you guys get more gigs after Warrior Defense.

I feel lucky that I found out about Full Mana Studios (Mars, Jaica, Mandy). They’re the reason that I was able to quit my full time job. They gave me a part time job so I could spend more time on my game. They also eventually funded the game and I was able to pay my artists in full.

To conclude, I hope this post gave you ideas on how to go about finishing your game. It’s hard I know, but it’s doable. Answer your “Why should I” and think of clever ways to impose pressure on yourself. Let’s finish more games!

Advertisements

Level Design Process of Warrior Defense

I put out a poll on Facebook asking people whether I should write about the level design process; or optimizations done for Warrior Defense. Level design process won. I think it won because I mentioned that I used a genetic algorithm in making levels. Maybe it sounded cool to them, but really, genetic algorithms are just glorified brute force. So don’t be impressed when someone brags that they can code a genetic algorithm. Here goes:

Let me begin by describing the level data model. A level is simply a collection of wave sets. Each wave set is a collection of spawn sessions. Here’s a level with 3 waves represented in XML:

<Level initialResource="400">
  <Waves>
    <WaveSet notification="">
      <Spawn unit="Critter" time="0" number="22" rate="1.1" startGroup="START_1" goalGroup="GOAL_1" />
      <Spawn unit="Critter" time="10" number="22" rate="1.1" startGroup="START_2" goalGroup="GOAL_2" />
    </WaveSet>
    <WaveSet notification="">
      <Spawn unit="Runner" time="10" number="1" rate="1" startGroup="START_1" goalGroup="GOAL_1" />
      <Spawn unit="Critter" time="0" number="25" rate="1.2" startGroup="START_1" goalGroup="GOAL_1" />
      <Spawn unit="Runner" time="25" number="1" rate="1" startGroup="START_2" goalGroup="GOAL_2" />
      <Spawn unit="Critter" time="15" number="25" rate="1.2" startGroup="START_2" goalGroup="GOAL_2" />
    </WaveSet>
    <WaveSet notification="">
      <Spawn unit="Critter" time="0" number="27" rate="1.2" startGroup="START_1" goalGroup="GOAL_1" />
      <Spawn unit="Runner" time="11" number="1" rate="1" startGroup="START_1" goalGroup="GOAL_1" />
      <Spawn unit="Critter" time="13" number="27" rate="1.2" startGroup="START_2" goalGroup="GOAL_2" />
      <Spawn unit="Runner" time="24" number="1" rate="1" startGroup="START_2" goalGroup="GOAL_2" />
    </WaveSet>
  </Waves>
</Level>

Note that this is only a simplified version. The actual level XML contains a lot more tags. I just left what’s relevant for this article.

A spawn session describes how a series of a number of a certain enemy units are spawned into the level. Think of it as a specification of a magical portal where enemies come in. It has the following properties:

  • unit – What enemy unit to spawn
  • time – The time to start spawning the enemies from the starting time of the wave
  • number – Number of units to spawn
  • rate – Spawn rate of the units. It’s in spawn/second. It determines how fast the enemies are spawned. Higher number means faster spawn.
  • startGroup – The area where the spawned units start. It is named as ‘group’ here because it is a tile group.
  • goalGroup – The area where the spawned units are headed.

This is how a single level in Warrior Defense is described, at least the meat of it. We now head to the actual level designing. One important question that need to be answered was “How do we make waves that are ensured to have increasing difficulty?” Digging deeper, putting it in another way is “How do we quantitatively measure how difficult a wave is?” If we have the answer to this question, then it’s just a matter of increasing this value.

How do we quantitatively measure how difficult a wave is?

What I did is I assigned a hypothetical value measuring how tough a single enemy unit is. It’s the same as setting a difficulty level for each unit. The following are XML element description of three enemy units in the game:

<Unit name="Critter" type="Crawler" value="1" size="Small">
    ...
</Unit>

<Unit name="Runner" type="Crawler" value="2" size="Small">
    ...
</Unit>
    
<Unit name="Roach" type="Crawler" value="3" size="Small">
    ...
</Unit>

The value attribute represents the difficulty value of the unit. The higher the value, the more difficult it is.

With this, calculating for a wave’s difficulty is just a matter of addition and multiplication. For example, for this wave set:

<WaveSet notification="">
    <Spawn unit="Critter" time="0" number="22" rate="1.1" startGroup="START_1" goalGroup="GOAL_1" />
    <Spawn unit="Runner" time="10" number="15" rate="1.1" startGroup="START_2" goalGroup="GOAL_2" />
</WaveSet>

The difficulty value of this wave set can be calculated as:

difficultyValue = Sum of all spawn session difficulty
difficultyValue = (numberOfCritter * difficultyValueOfCritter) + (numberOfRunner * difficultyValueOfRunner)
difficultyValue = (22 * 1) + (15 * 2)
difficultyValue = 52

It’s a rather simple quantitative measure and probably not representative enough of the actual difficulty, but it works enough for my purposes for level designing. Choosing how to increase the difficulty value is easy. I could use linear, logarithmic, quadratic increase, etc… anything to my liking.

Combinations of enemies

The next hurdle I encountered is where I used some programming. “How do I make combinations of enemies that total to a certain difficulty value?” For example, I want to make a wave set that has a DV of 100. What combination of units and how many of them should I include to make this wave set? What if I don’t want Critters to be in this wave? I want Tanks and Runners in this wave. How many of them should I use?

I turned to computer science. This particular problem can be best solved by a simple genetic algorithm. The idea is to generate random spawn session combinations and breed them to find better ones until the one with the exact difficulty value emerges. The code is also not that hard to make as I imagined it. So I decided to put a little time to make this little tool I call Wave Set Generator.

Forgive the UI. It's long.
Forgive the UI. It’s vertically long.

Continuation from previous image
Continuation from previous image

This tool generates a set of spawn sessions that matches a number of criteria:

  • Spawn Data Count – Number of spawn sessions
  • Target Unique Count – The number of unique units. Spawn sessions may or may not have the same enemy types.
  • Target Total Value – The difficulty value of the whole set in a certain range
  • Target Unit Count – Overall number of units in a certain range
  • Exception Set – Units that should not be included in the set. In later waves, easier enemy types might no longer be necessary. Or I would want waves with no flying units.
  • Required Set – Units that should be included in the set. In some waves, I may want only flying units, or only heavy units.

When the Start button is clicked, the tool generates random combinations at first then interbreeds them until the combination that meets all criteria has been generated. The tool stops as soon as one combination is found. There are more than one possible combination. If I don’t like the generated one, I can just press Start again to look for another combination. How cool is that? This is the most time saving tool I’ve ever made. After a good combination is generated, I’ll just have to put them to the level xml.

Just imagine what I was doing without a tool for this. I choose some units, then I would assigned an arbitrary number to one unit and calculate for the rest. If I didn’t like the resulting combination, I’d have to recompute for other units, or worse, start all over again. In short, it takes a lot of time just to compose waves.

The genetic algorithm I used goes like this:

  1. Represent the spawn session set as a list of bytes (also known as ‘chromosome’).
  2. Provide a fitness calculator that checks if a chromosome meets all the criteria. If not, it assigns a fitness score to a single chromosome.
  3. Generate a fixed number of random chromosomes. Say this is N.
  4. Using the fitness calculator, check each chromosome if it is already the fittest candidate. If one is found, stop the process. Else, assign the fitness score to each chromosome.
  5. Generate a new generation of N chromosomes by mating the current ones. Ensure that the fitter chromosome have more chances of mating.
  6. Repeat step 4 until the fittest chromosome is found.

Implementation details is already out of scope for this post. I’ll write about that in another one. The concept of the algorithm is it looks for the best solution by culling the unfit solutions to generate better solutions in every generation. It’s kind of like how evolution works. I say it’s a glorified brute force because there’s not even an actual solution. It looks for it blindly.

The game so far

It’s been a long time since my last post. I’m so busy working on the game. It’s either I have no time to post or I’m too tired to post. Sometimes the internet is crappy when I’m editing a post. Images don’t upload. I give up and the draft just gathers dust that it eventually becomes too late to publish. I gathered a lot of backlog posts about updates, but I don’t think they are relevant now. The game has already been RELEASED! I wasn’t even able to post a formal press release.

The game is now available in Google Play. Grab it now. Download and finish all 30 levels! Some people already did finish all levels. They did it not more than 3 days. A part of me is happy. Some people do enjoy the game and are egging me for the next part (it has a good hook in the ending). A part of me is disappointed. I thought I was making challenging levels. It turns out it’s not so. But generally I’m happy. There’s this cocktail of emotions of relief, worry, and that feeling of a start of a much more difficult undertaking.

Game screenshot
War!

I’m talking about “marketing”. Marketing is a different beast. I may be good at programming but I’m a newbie at this thing. I think I could have done more marketing while the game is still in development. I released early versions of Warrior Defense in GameJolt hoping to find audience. I did found some but not in great numbers. But hey, I’d be glad if I could make 10 people happy with my game.

In summary, I failed at looking for the watering hole. I went full blast with development without ripening my target market. I guess I didn’t have the time or I just didn’t know how. I did scour at numerous forums but it seems that nobody is taking notice. I kind of rage quit on it and focused my resources instead to “just release the game”. If you’re a marketer and you like my kind of games, maybe we could partner up.

White, red, and orange

Now that the game is out there, I found myself spending half of my time for marketing. I hate it, of course. It’s not my forte. But I want to build this habit of doing at least one marketing task per day. It could be a twitter post, facebook status, forum entry, blog post, anything. A contemporary game dev hero of mine, Cory Trese (@corytrese), also gave me a sound advise. He said to release updates every week and to put your personal email in every forum post or market place. Now I’m about to do that.

Mar 17-22 Updates

New Tutorial Sequence

I’ve shown the game to a lot of people by now. When I’m showing it, I don’t want to specifically point out to the player what he/she should do. I’ve always wanted my games to show itself how to play it. In this aspect, my evaluation so far is not so good. I’ve spent the beginning of this week changing the initial tutorial.

3-22-2014a
Now it says where to tap
Tap the dangerous button when you're ready
Tap the dangerous button when you’re ready

The tutorial sequence looks better to me now. But it means nothing unless actual players verify it.

Tiles that shouldn’t be walked upon

Now that I have the actual tile assets with houses, rivers, and other stuff, it’s also the time to update the tilemap data that some objects are no longer ‘passable’. It’s kind of like setting collision boxes to these tiles but instead of boxes, a number is just assigned to each of these tiles which signifies that units can’t pass through here. Here’s a screenshot from Tiled:

Black means 'no'
Black means ‘no’

For those of you who doesn’t know, Tiled is a free software that is used mainly for tile based games or applications. I’m using a different layer with solid colored tiles that acts as a data file for the game to determine which parts are passable or not. Now that the map has blocked areas like houses and trees, I changed the solid tiles to transparent ones so that I can see through it. All I have to do then is set this layer as the topmost and paint those blocked areas with a special tile (the black one). This special tile is read as ‘unpassable’ in the game.

Unit Upgrade Items

The Upgrade screen is far from complete. For me, it’s actually the most boring thing to do. Maybe because it’s all just UI and data. It’s like working with a web application all over again with MVC patterns and shit. It’s so unexciting but it has to be done. Right now, the ‘unit upgrades’ is work in progress.

Unit upgrades using dummy icons
Unit upgrades using dummy icons

Mind Museum

I also have great news. I was invited to have a game exhibit in Mind Museum! I will showcase Warrior Defense, of course. It’s all happening on March 30. Other Filipino game developers will be there as well.

Lots of Updates

I’ve made a lot of updates since the last two weeks. But I still can’t release these babies as there are missing essential art assets.

New Tile Sets

First of on the barrage of updates is the new tile set. The first level is a mess since we started working on this. This is how it looks like now:

Look! It's a town!
Look! It’s a town!

The image for the empty slot is still in the works so I still used the old one. But looking at it now, it’s hard to determine whether that thing is ‘tappable’ or not. I also requested to my artists to change the color of the corrupted ground from brown to something else as most of the enemy units are brown, too. This is the main reason why I can’t release the updates yet. New players may not be able to play it properly.

Persistence

In layman’s terms, persistence is basically ‘save progress’ feature. I’ve written a separate article about the system that I’ve used. Everything that needs to be saved are already implemented (level progression, upgrade resources, upgraded items, etc). I’ve used an SQLite wrapper called SimpleSQL. I specifically selected this one among the many other SQLite solutions as it is cheap and works well in Unity free version.

New Level Select Map

The level select map looks so much better now. The image is bigger than the game screen so I had to implement camera panning to it. This is how it looks now:

Bigger map! Looks pretty too!
Bigger map! Looks pretty too!

I hope from here, the player could visualize the journey of the game’s protagonists. I can’t tell anything about the story yet.

Critical Strikes and Camera Shake

I really love camera shakes and I feel stupid that I haven’t implemented it in this game. I added the concept of critical strikes to defender units. I used a floating number that represents a chance to hit a critical strike for each unit. It gets resolved whenever they attack. The camera shakes when they perform a critical strike. In addition, the game also shows a more fanciful graphical effect on attack to its target. I also made the camera shake on use of some spells. It’s a cheap trick but it made the game look more exciting.

Voice Response on Unit Creation

Remember in RTS games where the units say something when you select them? I really like that. It gives the units more life and personality. I can’t help but add some to this game. As of now, they say something as soon as they are created. I plan to add more like when they die, upgraded, or when the player assigns a new rally point.

Excited?

Honestly, I can’t wait to release these updates! I just have to wait for some key art assets. A new game version should be up in no time by then. So stay tuned.

Finite State Machine vs Behaviour Tree, A True Story

I needed behaviour trees because I wanted to manage the complexity of the units AI in Warrior Defense which originally used finite state machines (FSM). FSMs are great because they are so simple and intuitive. When they get big, however, they become so complicated. It got to a point where I’m afraid to change the configuration of the FSM because the working AI easily breaks. In other words, it’s brittle. I don’t like that. I still have lots of features to add later on that deal with unit interactions.

A part of Defender unit FSM. Looks like spaghetti, yum!
Just a part of Defender unit FSM. It looks like spaghetti, yum!

I decided to make my own behaviour tree tool because I can’t find a decent one in the Asset Store. I’m one of those lazy guys that buy a lot of tools. I’ve tried RAIN, which is free. I hated it. The changes to the tree did not reflect in the game right away. It just frustrated me. I’ve bought React while it was on sale but I didn’t like how it worked. Adding actions to it was clunky. It uses functions and runs it in a coroutine. It doesn’t support parameters, as well.

Thus, the journey to developing my own behaviour tree tool began. I called it Banana Tree. I completed the core behaviour tree framework (nodes, sequence, decorators) some months ago. But this is pure code. There’s no visual editor. I’ve made AI behaviours that are hardcoded in classes. Production wise, this is not very efficient. AI behaviours changes a lot. Something that changes a lot should not be code, they should be data. I thought that I should probably make a data driven behaviour tree tool. Looking at the FSM above, I think I’ll be making a lot of code anyway using the current framework.

So I did make a data driven behaviour tree with its own visual editor. I just finished the bare essentials that could create a working behaviour out of data. I’ve successfully ported the complex behaviour above to its Banana Tree equivalent. The high level behaviour looks like this:

High level behaviour nodes
High level behaviour nodes. It looks very neat.

Don’t be fooled, though, because it looks like this underneath those parent nodes:

Worms under the stone
Worms under the stone

What I’d like to point out is that it’s still complicated, but it’s now more manageable. I can complete one branch of the behaviour and forget about it once it works and begin working on another one. My brain is not strained so much to the amount of visual objects because I can just fold the node and stop thinking about the things beneath it. It’s so unlike FSMs where you can’t help but see the states and their wiring.

Using behaviour trees feels more like programming. Each node feels like a function. Working with an FSM feels like wiring. It is static as “wire one event to one transition state”. I now understand what articles about behaviour trees mean when they say FSMs are constricting (I didn’t get it before). Behaviour trees don’t have wires. They execute on how the tree is structured. What to do next is not explicitly stated in the behaviour tree. It can select a lot of possible action branches to execute. In FSMs, you have to explicitly specify what state it should go next. This could be limiting because there may be a lot of possible states to go to. It’s possible to make it work like a behaviour tree but you’ll need more work and maintenance to the FSM. You’ll most probably end up with an FSM like the one I showed.

I can’t say that behaviour trees are better, either. They also have a major disadvantage: steep learning curve. You have to think like a compiler when working with BTs. You have to know what its elements mean like Sequence, Selector, Parallel, Decorator, etc. Like every skilled programmers say, “Know your tools. Know where it’s best used.” Behaviour trees has been proven to work on complex behaviours, but I probably can’t give this to a non programmer and let him/her figure it out on his/her own. FSMs are much more easier to understand. They are far more intuitive, too. FSMs are probably the way to go if you only need to model simple behaviours.

Banana Tree Success!

I think Banana Tree is complete, for now. Software projects can never be completed. Little UI elements and responses are still missing. No copy-paste yet, no undo-redo. But for its purpose and intended use, it does it very well. I was able to replicate the behaviour of ground and flying crawlers using the tool. By doing this, I also uncovered and fixed some major implementation holes.

Ground enemy behaviour using Banana Tree
Ground enemy behaviour using Banana Tree

The biggest stumbling block for me is when I found out that Unity can’t serialize a nested field that has depth of seven or more. It’s so disappointing. My node tree data was serialized like this:

[Serializable]
public class NodeData {

    [SerializeField]
    private string label;

    ... // more data

    // this is a nested serialized field
    // Unity can't handle this if it's too deep
    [SerializeField]
    private List<NodeData> children;

}

public class BananaTreeBehaviour : MonoBehaviour {

    // I thought I'll never have problems with this
    [SerializeField]
    private NodeData rootNodeData;

    ...

}

I went to the forums and found out that others are complaining about it, too. I found this out last Monday night. I was so excited that I could finally see Banana Tree in action in an actual game, then this happened. Tuesday morning was spent on fixing it. It’s not an easy fix, either. Fortunately, my habits worked for me. Layers of indirection made refactoring easier.

My fix is to have a master list of all node data. I added an extra property named masterId which works as a primary key for the node. Child nodes are then kept as a list of integers where each entry represents the masterId to the actual node instance. I made a custom container class to manage the list of all nodes.

[Serializable]
public class NodeData {

    [SerializeField]
    private int masterId;

    [SerializeField]
    private string label;

    ... // more data

    // no longer nested to circumvent the limitation
    [SerializeField]
    private List<int> children;

}

[Serializable]
public class NodeDataMaster {

    // all nodes of the behaviour tree are collected here
    [SerializeField]
    private List<NodeData> nodeList;

    /**
     * Searches for the node with the specified master id
     */
    public NodeData FindByMasterId(int masterId) {
        for(int i = 0; i < nodeList.Count; ++i) {
            if(nodeList[i].MasterId == masterId) {
                return nodeList[i];
            }
        }

        Assertion.Assert(false, "Can't find node with master id = " + masterId);
        return null;
    }

    public NodeData AddNode() {
        ...
    }

    public void RemoveNode() {
        ...
    }

    ...
}

public class BananaTreeBehaviour : MonoBehaviour {

    // all node manipulation now goes through here
    [SerializeField]
    private NodeDataMaster dataMaster;

    ...

}

I designed the tool to be flexible and extensible without editing any of its source code. This is done through reflection. Custom actions should be implemented as classes that inherits a certain class. By doing so, the editor identifies these classes and adds UI controls for it. Thus, replicating the existing behaviour means porting a lot of function calls to their action class equivalents. My action browser looks like this:

Custom actions
Custom actions

Custom action classes look like this:

    [ActionGroup("Game.Crawler")]
    public class CrawlerHasBlocker : CrawlerAction {

        #region implemented abstract members of BntNodeAdapter
        public override BntResult Start() {
            return Evaluate();
        }

        public override BntResult Update() {
            return Evaluate();
        }

        public override BntResult FixedUpdate() {
            return Evaluate();
        }
        #endregion

        private BntResult Evaluate() {
            if(GetCrawler().HasBlocker()) {
                return BntResult.SUCCESS;
            }

            return BntResult.FAILED;
        }

    }

    [ActionGroup("Game.Path")]
    public class GetTargetPathPosition : BntCachedComponentAction<PathCrawler> {

        [UseVariable]
        public BntVector3 Result {get; set;}

        #region implemented abstract members of BntNodeAdapter
        public override BntResult Start() {
            Result.Value = GetCachedComponent().GetTargetPathPosition();
            return BntResult.SUCCESS;
        }

        public override BntResult Update() {
            return BntResult.SUCCESS;
        }

        public override BntResult FixedUpdate() {
            return BntResult.SUCCESS;
        }
        #endregion

    }

It sounds silly that a single function call need to be promoted to a class to be used in an editor. But I like this better. I like the idea that you can extend the functionality of an editor by just adding classes. This adheres to the Open-Close principle of software engineering.