Exhibiting at GameCity 9

A couple of weeks ago I was lucky enough to exhibit Polyology at GameCity 9. Held in the old Connexions building in the centre of Nottingham, the future home of the National Videogame Arcade.

B1GHOBnCcAA1sRI

Exhibiting was great for getting feedback, particularly from some of the intelligent and creative people knocking around at GameCity. Such as Martin Hollis and Karl Hilton; a couple of the creators behind Goldeneye on the N64. Alan Hazelden creator of Sokobond and the upcoming A Good Snowman Is Hard To Build and even the Guardian’s Keith Stuart.

Along with industry professionals there are children of all ages, happy to pick up the controller and give you candid feedback. It was great seeing families helping each other getting through the tougher puzzles. It’s the variation of people at GameCity which makes it such a wonderful place to get real people to playtest your game.

Although my personal favourite part of GameCity 9 wasn’t exhibiting, it was just getting to play games. Such a wonderful variety; Tap Happy Sabotage! a crazy physical multiplayer game, molecular puzzle game Tricone Lab and visual programming game Glitchspace to name a few.

So if you get a chance to exhibit something at GameCity next year, it’s definitely worth polishing up that demo and getting it in front of the public. See you there next year!

Telemetry for Indie Games: Replay System

Following on from Posting Basic Data I’m going to talk about how I implemented a system to record users inputs, upload them to a webserver and replay them as needed. This was extremely helpful for designing the puzzles for Polyology, recreating bugs and testing fixes.

Determinism

An important base line for creating a consistent replay system a deterministic game engine. So if you do exactly the same thing, you get exactly the same output. Typically the only complication comes when we use random numbers to create interesting particle effects, or spawn objects at changing locations. Fortunately computers aren’t really random at all, and the randomness is typically generated based on the millisecond you called the random function as a seed. You can either record that seed, set it to a fixed number of use an existing library such as David Bau’s Seedrandom.

//Fixed seed, so random always generated same results
Math.seedrandom('hello.');
console.log(Math.random());          // Always 0.9282578795792454
console.log(Math.random());          // Always 0.3752569768646784

//Random seed makes life more interesting but less determistic
Math.seedrandom();
console.log(Math.random());          // Reasonably unpredictable.

External Inputs

Now your engine is deterministic just need to record all external inputs so allow you to replay the game. Typically they include

  • User Input
  • Frametime
  • Random Seed
  • Which is fairly trivial to collect at runtime, bundle up and send to a webserver. However if you game is multi-threaded life is significantly more problematic as the OS can give you threads at different times but there are more extreme virtualization solutions out there.

    Wrapping this information in xml, json or whatever you prefer and upload it to a webserver.

    <Replay seed="42" level="rooftops" >
        <record type="KeyDown" frame="132" key="18" />
        <record type="KeyUp" frame="193" key="18" />
    </Replay>
    

    Personal I also included other information so allow me to search and sort the data such as

  • Users score
  • Current date and time
  • Length of replay
  • Replaying the data

    Getting the replay to work is fairly simple, just step through the file, doing what you recorded. There are a few gotchas like disable current user input, handling remapping and any state that influences the same (e.g. if the player has the key to unlock that door) but nothing that can’t be overcome quickly and easily.

    Proof of the Pudding

    This proved an invaluable resource from a design perspective, allowing me to see what users are doing from a distance. So players that don’t give you any feedback when playing your game online still give you a huge amount of valuable data. Allow you to see where 60% of users stop, and what they were doing on that level, or letting you know that after someone emailed you feedback, they continued to play the game for another 2 hours and finish off every bit of content.

    From a code side it was also useful, when users reported bugs I just watched the replays until they happened, then I could fix the code and verify it was fixed by running the new code against the replay.

    A replay system is a wonderful tool to give you a huge amount of information, and as long as you spend the time and care to mine this information you’ll undoubtedly end up with a better product in the end.

    Telemetry for Indie Games: Posting Basic Data

    If want to find out how to implement a quick telemetry system, or just want to me nosey about how I used Django to create a telemetry system for Polyology, then this post is for you.

    Django

    One of the various tag lines is “Django makes it easier to build better Web Apps more quickly and with less code.” which I can’t help but agree with. It does take some time to setup and learn but once you know Django you’ll be able to quickly create and extend your telemetry system. It has solid documentation and a lovely tutorial for getting you started on their website (disclaimer – This article was written using Django 1.4).

    Uploading data from the game

    Below is a small snippet of javascript code for uploading some bit of information, some thing like a player has loaded a level, restarted or quit your game. It ties in some situational information, the set and level, where the event occurred. Then it’s a fairly trivial matter of sprinkling your code with PostTelemetry calls in suitable places, and you can extend it with new telemetry without changing any web server code.

    function PostTelemetry(statName)
    {
        var formData = new FormData();
        formData.append("levelset", gLevelSystem.GetLevelSet())
        formData.append("level", gLevelSystem.GetLevelName())
        formData.append("stat", statName);
    
        var xmlhttp = GetXMLRequest();
    
        xmlhttp.open("POST", "/telemetry/", true);
        xmlhttp.send(formData);
    }
    

    Handling the data on the server

    Django lets you define models, layouts of how information will be defined, this is a quick model for some generic telemetry. In this case it’s just a couple of short reference strings and an integer (and a cheeky increment function for later on).

    class Telemetry(models.Model):
      levelset = models.CharField(max_length=32)
      level = models.CharField(max_length=32)
      stat = models.CharField(max_length=32)
      value = models.IntegerField()
    
      def increment(self):
        self.value = self.value + 1;
    

    Then when a post comes in simply increment the existing telemetry, if it doesn’t exist add a new one.

    def telemetry(request):
      if request.method == 'POST':
        _set = request.POST['levelset']
        _level = request.POST['level']
        _stat = request.POST['stat']
        if (_set and _level and _stat):
          try:
            query = Telemetry.objects.get(levelset=_set, level=_level, stat=_stat)
            query.increment();
            query.save();
          except ObjectDoesNotExist:
            newTelem = Telemetry(levelset=_set, level=_level, stat=_stat, value = 1)
            newTelem.save()
    ...
    

    Administrating the data

    If you add a quick Admin class, saying what is displayable and filterable

    class TelemetryAdmin(admin.ModelAdmin):
      list_display = ('stat', 'levelset', 'level', 'value')
      list_filter = ('stat', 'levelset', 'level')
    

    Django will give you a admin site, setting you add, edit, delete telemetry as well as filtering and sorting.

    Django Admin Site

    Conclusion

    So that’s it, that’s most of the code for the telemetry, and you can just add new post calls and everything is just handled. Every indie dev out there should have a simple, extensible web framework for telemetry. If you don’t have one, I’d try Django and see what you think.

    Finally I’d like to thank Mark Tully for introducing me to Django in the first place.

    Hello world!

    Today marks my first day working for myself, it’s been interesting although severely lacking in actual game development, more operations tasks like…

    • Emailing accountants
    • Registering domains
    • Setting up email
    • Registering Twitter and Facebook accounts
    • Creating this website
    • Putting together art outsourcing proposal

    All being well my next post (and website update) will involve an actual game!