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.
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.
Now your engine is deterministic just need to record all external inputs so allow you to replay the game. Typically they include
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
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.