• Register

Fractured State is an urban combat real time strategy game that takes place in the pseudo diesel/steam punk decay of the nation of Laperia. It features simultaneous interior and exterior combat to power building-to-building fights, cover and flanking, and weapon accuracy modelling and reloading.

Post news Report RSS State of the Fractured State

Kelso discusses troop movement, State Machines, and shows his code muscles.

Posted by on

So, lately I've been working on some of the low-level systems for the game. My ultimate goal is to get a very basic combat prototype up and running in both singleplayer and multiplayer as soon as possible. We're getting to the point in our round table discussions where we have a lot of cool ideas about how the game is going to play so it's going to be important to get some of these things implemented so we can see what works and what doesn't.

I started off with the most basic concept in any RTS game - issuing move orders. Player left clicks on a unit then right clicks on a spot on the map and the unit moves to that spot. While this basic bit of logic was simple to implement it didn't fit very well into the larger scope of the game. I scrapped what I had and tried to go a little deeper.

The first thing I did was write a new control scheme for the camera. Originally I was using the same one as the Editor in the hopes of keeping some commonality there. This soon proved to be more hassle than it was worth with all kinds of branching logic in the code to determine whether we were playing the game or using the editor, if we had units selected, if we didn't - on and on. There's also some things the Editor camera can do that the Game camera cannot (zoom is a good example of this) and I was opening us up to a lot of hotkey collisions between the two applications. This is all resolved now and, unsurprisingly, the amount of redundant code between the two control schemes is minimal.

From there I wrote a system that keeps track of all the units the player currently has selected. This was fairly trivial.

At this point I had to decide how units were going to maintain state. State Machines aren't overly complex as far as patterns go (unless you want them to be) but I wanted a system that was flexible and worked well within Unity's component based system. I came up with these goals:

  • The system should be "passive" whenever possible. That means:
    • No polling (asking - are you done yet? are you done yet? are you done yet? every frame).
    • In conjunction with the first point - use triggers whenever possible.

  • The system should always try to get back to an Idle state.
  • The Idle state should essentially do nothing and rely on triggers or player actions
  • Each state should work independently of the unit. (In Unity terms - a state is not a MonoBehaviour that acts directly inside a unit, it simply interfaces with one).

The Idle state and trigger bit might need some clarification. "Vision" in Fractured State is largely based around line of sight and not the traditional Fog of War method. What this means is that the entire map is revealed from the beginning of the game and whether or not the player can see an enemy unit is determine by if one of his units is within visual range of that enemy AND if that unit also has a clear line of sight to that enemy. Instead of polling each friendly unit every frame to check if any enemy units are in range we can set up a sphere trigger around the unit and use the OnTriggerEnter "event" (quotes because it's actually not an Event in the traditional sense, but we can think of it that way) to inform our unit that an enemy is within his sight range. This should save us some processing time on units that are sitting around not doing anything.

In Unity, the Update method is called each frame as a mechanism for...updating...an object in the world. A lot of Unity State Machine patterns use Update in order to update the current State of the object as well. This works just fine but there might be times where we don't want to update the state every frame or, in the case of our Idle state, we don't want to update anything at all! We also might want to chain states together. Going back to the bullet point about trying to achieve an Idle state at all times, it would be nice if we could initiate that Idle state automatically when another state completed. Think of a Move state. When the unit reaches his destination (assuming he hasn't been ordered to attack something) then he should exit his Move state and enter his Idle state without the system knowing (or really caring for that matter). We can do this with Coroutines.

Coroutines allow Unity to chunk (there's that word again) instructions out over a certain amount of time. Because all a Coroutine is is an IEnumerator type with some yield instructions thrown in we can use our State code to change a unit's behavior without having a big messy Update method with a bunch of if statements in it.

So this:

csharp code:
void Update()
{
    if (thisCondition)
    {
        // do something
    }
    else if (thatCondition)
    {
        // do something else
    }
    else if (anotherCondition)
    {
        // do another thing
    }
}

Or even a slightly cleaner but still not ideal version:

csharp code:
void Update()
{
    myState.Update();
}
 

Can be completely decoupled and remove the need for an Update method at all. So our state code completely controls what our unit does while in that state and the unit code just facilitates the hook into the engine.
Some state code:

csharp code:
public IEnumerator Do()
{
    while (!CloseEnough())
    {
        // move unit closer to target
        yield return null
    }
}
 

And the Unit code that hooks it:

csharp code:
public IEnumerator ExecuteState(IEnumerator state)
{
    while (state.MoveNext())
    {
        yield return state.Current;
    }
}
 

We can kick off the execution of the state (typically done as part of our state's Enter method) with one line:

csharp code:
unit.StartCoroutine("ExecuteState", Do());
 

This method allows us to monitor the conditions required to maintain the State from within the State itself instead of from the Unit. It also saves us some processing time by not having to poll each unit every frame. The State Machine manages itself completely and all of it's interactions with the world funnel through the same piece of code on the unit.

Combined with a few triggers to kick off state changes, units should be able to act much more intelligently to events in the world around them. The hope is that they can be smart enough to keep themselves alive long enough for the player to make decisions and give them further orders.

If anyone is interested, this is an awesome article on Coroutines in Unity by Richard Fine and this is a C# translation of Mat Buckland's Finite Sate Machine. Both of these articles saved my bacon when formulating the basics for the state machines in Fractured State. And of course, you are always welcome in our Forum and as a companion in the Twitterverse.

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: