Unity TaskManager

Inspired by Renaud’s neat little waiting system, I decided to have a go at making a nicer task/timing system in Unity. I had three basic requirements:

  1. Garbage should not be allocated from the core. Use of lambdas will cause some garbage but that should be up to the caller to decide if they want to use it.
  2. Task management should be scoped. If I learned anything with my old timing systems is that there are times when you need some things to stop while others keep going. For example, the game may be using some timers but you want them to stop when the pause menu comes up (which may use its own timers).
  3. It should be easy to chain together tasks without huge nested lambdas.

To that end I worked over the last day or so trying out different systems. Eventually I landed on my current TaskManager system which feels pretty good to me. It’s a fluent API to make things fun and interesting and meets all the criteria above. The only thing that’s not incredibly convenient is repeating tasks, but they’re easy enough to implement with named functions.

To solve the scope problem I made the main TaskManager a component you can attach to any object you want. So you can have multiple task managers and scope them however you want. However to solve the easy/prototype case of having global access, the first TaskManager that gets Awake() called on it will be considered the “main” task manager and can be accessed through the static “main” field on TaskManager. I’m sure there are better ways of flagging the task manager as main, but this was the quickest and easiest solution.

For my demo I have three scenarios I coded up. The first is about the simplest it gets. I simply start a timer for 2 seconds and when that’s done I turn off the light.

public class Demo1 : MonoBehaviour
{
    void Start()
    {
        // wait for two seconds and then turn off the light
        TaskManager.main.WaitFor(2f)
                        .Then(() => DemoManager.demoLight.enabled = false);
    }
}

The second demo builds on the first using a named method in order to create an infinitely repeating task to toggle the lights. The nice thing here is you could easily put checks into ToggleLight for things like repeat count so that you repeat only as long as you’d like. As I mentioned above, it’s nice but not the most convenient. However I couldn’t figure out how to add repeating into the fluent API in a clear way.

One other note is that you can use the Then method as many times as you’d like and the callbacks fire in that order. This is nice because you don’t need one big lambda with all your callback logic but can instead chain up multiple methods. It makes it nice if you ever want to take out just some small piece of callback logic.

public class Demo2 : MonoBehaviour
{
    void Start()
    {
        // call our method to start the loop of actions
        ToggleLight();
    }

    void ToggleLight()
    {
        // wait for two seconds, toggle the light, and then call
        // the method again so it repeats forever
        Light l = DemoManager.demoLight;
        TaskManager.main.WaitFor(2f)
                        .Then(() => l.enabled = !l.enabled)
                        .Then(ToggleLight);
    }
}

The last demo shows some of the real power of the fluent API. In this demo I wait for two seconds and then change the light color to some random value. Next I use the WaitUntil to animate the cube in a sine wave on the X axis. Once that completes the light turns off. The fluent API makes this pretty readable and avoids nesting a bunch of lambdas which can be quite annoying and hard to manage.

public class Demo3 : MonoBehaviour
{
    void Start()
    {
        // wait for two seconds, then change the light to a random color,
        // then wait for the cube to make a little sine pattern,
        // and then turn off the light
        Light l = DemoManager.demoLight;
        Transform c = DemoManager.demoCube;
        TaskManager.main.WaitFor(2f)
                        .Then(() => l.color = RandomColor())
                        .ThenWaitUntil(t =>
                            {
                                c.position = new Vector3(Mathf.Sin(t) * 3f, 0.5f, 0);
                                return t >= Mathf.PI * 2f;
                            })
                        .Then(() => l.enabled = false);
    }

    Color RandomColor()
    {
        return new Color(Random.value, Random.value, Random.value);
    }
}

Pretty neat, huh?

You can either snag TaskManager.cs or you can import TaskManager.unitypackage which contains all the demo code and a simple drop-in prefab for the TaskManager.

Simplify your text assets

I was working on a game tonight which had an XML file that described a lot of parameters used by a procedural level generator for the different levels in the games. This XML file was 15KB but was previously chomped on by the XNA Game Studio content pipeline which means in the end it came out as a serialized binary blob. In short it didn’t matter how big the XML was because the game on the user’s machine never saw it.

I’m currently working on moving this game to Unity and don’t have the content pipeline to handle these things. While I could figure out how to bend Unity’s asset system into working that way, I decided to just go with a plain text asset. This lets me read in the file at runtime and read the data I need but now means the source size and format is what ends up on the user’s machine. Unity does support binary files for text assets (which makes it a bit of a confusing name), however since I may need to tweak the settings I’d rather have it be in a text format.

First thing I did was use some regular expressions in my editor (Sublime Text 2) to convert the XML into an INI-style format. Once I had removed some of the top level XML nodes, all it took was a simple find and replace to fix up all the data nodes:

Find: <(.+)>(.+)</.+>
Replace: \1 = \2

This cut out nearly half of the text in the file (the angle brackets and the closing tag text) and got me down to 7KB. Great start. It also means not needing to use any of the XML namespace in code which is good for performance. But I realized I could do better.

Since this is private data format, why not limit the fields to having to be in the same order as the data structure? If I take away out-of-order data in the file I can get rid of all the field names entirely. I just read each line and parse it into the correct data type. This again massively reduces the size of the file and simplifies the reading process.

The end result is that I went from a 15KB XML file to a 2KB text file that will also load vastly faster due to its much more simplified format. I feel this is similar to how I optimized the geometry in my XNA GS 4.0 app week game; just thinking a bit more about the problem and coming up with a simple solution.

Why I bought unlimited turns in Triple Town

Last night I made a comment on Twitter about the price of unlimited turns in Triple Town:

Then I went to bed. While trying to fall asleep I was thinking about that $7 price tag and wondering why it felt so much. I’ve come up with a few ideas:

  1. The iOS game store generally trends towards prices < $3, even for upgrades on “freemium” titles.
  2. Triple Town appears so simple that mentally it’s easy to look at and say “that’s not enough for $7″.
  3. I hate the mechanic of giving a game for free but then limiting users and either forcing them to stop playing or pay up to play more.

While I still really dislike #3, I realized that while it was more than most games I spend almost that much on my morning coffee and muffin at the cafe (which, thanks to Snowpocalypse, I haven’t had in the last two days). Surely a game that I played for a couple hours total time yesterday is worth a coffee and a muffin? And since it was a one-time fee it meant it wasn’t like many other games that only allow you to buy in consumable quantities.

What I found also funny was that I got responses from both Spry Fox and Daniel Cook (the designer) about it saying much of what I thought in my head last night. I’m sure they just watch for mentions of Triple Town in search, but I’m going to believe that I’m just that important that they felt inclined to respond. It’s better for the ego. ;)

In any case I came around on my own last night so this morning I ponied up for the unlimited turns. $7 well spent. Now if you’ll excuse me I have a town to build.

EagleScript

Over the last few days I’ve started working on my own scripting language I’m calling EagleScript. The goal is to have a very simple syntax which can be compiled into platform-neutral byte code and executed in a simple stack-based interpreter. I’ve got a lot working so far and wanted to share it with all of you, primarily those of you interested in making your own language.

Please note that I have no expertise or experience with making languages or virtual machines, so I’m not going to say EagleScript is a good example of how to do it, just that it currently works and will hopefully get better as I keep playing with it. If you want to contribute, please feel free to fork the repository, make changes, and send a pull request if you’ve got some bug fix or feature.

Anyway the source code is up on Bitbucket along with a nice readme that outlines what the language does. Additionally there is a console based test app and some XNA Game Studio 4.0 projects showing how you can utilize the scripting language. Hopefully some day I’ll have lots more documentation, but for now this is what you have if you want to dig in. Most of the source is commented so if you want to see how it works hopefully it’s not too hard.

https://bitbucket.org/nickgravelyn/eaglescript

New direction for TiledLib

As some of you may know, I maintain a small library for working with the Tiled map editor‘s TMX files with XNA Game Studio. This library was made up of a content pipeline piece as well as a runtime piece to make it easy to load these maps into a game and draw them. This was convenient, however caused a lot of headaches for me.

  1. People all need subtly different ways of working with the data including rendering. This meant extra architecture work to support variations on the same thing.
  2. People need new features of Tiled to be supported. This not only meant an upgrade to the importer, but also then the runtime to expose the data. This made #1 even harder.
  3. Lots of work was still being done at runtime. Games would be parsing out strings for object types or property values which is all work that could be done at build time with a custom pipeline piece.

After talking with a coworker who based their map loading on TiledLib, I came to a new design decision. Since this change is fairly large and I didn’t want to put quite all of it into the library’s readme file, I decided to do a bit of a write up here.

TiledLib is now going to be solely the importer pieces along with some helpers for writing your own content processor. The core idea is that you can do a lot of game specific work in your processor to clean up the data or remove data you don’t need before loading it into the game. As one example, you no longer have to parse strings at runtime to get numbers as you can do that at build time.

If you don’t need any of the new features coming in Tiled, you can always grab the older versions from the repository and use them if that’s all you need. But if you do want the improvements, it does mean some extra work  to write your own processor and runtime code. I do plan to continue making demo games in the repository to demonstrate how to work with this new Tiled (there’s already one very basic demo) so hopefully that will help developers get running. You could also start with the old runtime and processor system if you wanted (though you’d have to add the new features like tile objects and polygons).

So apologies to those who wanted the turn key solution to loading TMX files for their games, however in the long run this will yield the best results for games and the library.