The option to record games can be a nice addition for many types of games. It isn’t very hard to make it possible if you understand the basics. There can be a lot of different uses for the recordings, in addition to just watching them for fun.

************************************************************
Uses for recording games
************************************************************

Let’s say you get a very high score in a game, and others think that you cheated. Just save a recording of that game and let them watch it. Of course it’s always possible to create a fake recording, but that’s extremely difficult compared to just making up a score. You could also watch a recording of an expert player to learn what kind of tactics to use. You can use recorded games as demonstrations of the game, running in the background or as independent programs. Another very important thing to consider is that it can prove very useful in finding bugs.

If you have a bug that appears very rarely, such as once per 100 games, it can be a real pain to find out the cause for it and fix it. Once you have a proper recording engine set up, you can simply save a recording of the game where the bug appeared. If the recording is a perfect replication of the game, the same bug will appear every time you watch it. Modify the events where you assume that the bug is and watch it again to see if it got fixed. If not, you can just repeat this procedure as many times as needed. Unfortunately, not all bugs can be fixed this way, because modifying the events can also cause the recording (which was made with the “old version” to not work properly any more.

************************************************************
Method 1
************************************************************

The simplest, but also the most inefficient way to accomplish the task would be to simply make the computer capture a video out of the game. For this, all you need to do is capture the screen on every loop and form an animation out of these screenshots. This is not really a good method though, for many reasons. First of all, capturing the screen could cause a big performance hit, especially with higher resolution games. The file size would be huge, and since only graphical data is saved, it won’t help a lot with bug fixing. You obviously won’t be able to use the “modify code and watch again” trick here.

************************************************************
Method 2
************************************************************

Another method that is sometimes used is the method of saving the game values instead of graphics. The idea is to save all the game’s values to a file. When playing back the game, the values are read and used to position the players, update their health etc. There also needs to be a timer which represents the loop number.

------------------------------------------------------------
First loop:
------------------------------------------------------------

Set (1,1) to Player’s X
Set (1,2) to Player’s Y
Set (1,3) to Player’s Health

------------------------------------------------------------
Next loop:
------------------------------------------------------------

Set (2,1) to Player’s X
Set (2,2) to Player’s Y
Set (2,3) to Player’s Health

Etc…

Since you aren’t saving graphical data, the file sizes will be much smaller, which is obviously good. It can be the simplest method for smaller games, and is also quite dependable. The main weakness of this method is that it can get quite complex. If you have dozens of different types of bullets flying around the playfield and you have to store each one’s x and y positions, it easily gets messy. This will also cause the file size to grow a lot. Also, it is still not a “perfect” clone of the recorded game. Let’s say you record a game and then double the player’s speed. The recorded game will not be affected. This means that it’s not very well suited for bug fixing purposes, since the “modify code and watch again” trick won’t work so well. For that, we need something else.

************************************************************
Method 3
************************************************************

In many cases, especially with more complex games, the best method to make a recorded game is to make it play out just like a normal game would, “emulate” it in a way. Think of what kind of data really needs to be saved. What makes each played game unique. Most often it is the human player’s input. That input is just about the only thing that the game can’t predict by itself, therefore it is just about the only thing that needs to be stored. So if a player moves forward, instead of saving X and Y positions on each loop, you simply store the fact that the player presses the “move forward”-key. That’s all you need, since the game ALREADY KNOWS what happens if we press that key. After that you’ll have to emulate the stored input as those key presses. To clarify a bit, here is a small example of how this method could be used (with an array):

------------------------------------------------------------

When recording:

*Always
CurrentLoop = CurrentLoop + 1

*[Whenever the player moves forward]
Set (CurrentLoop,1) to 1.

*[Whenever the player shoots]
Set (CurrentLoop,2) to 1.

------------------------------------------------------------

When playing back:

*Always
CurrentLoop = CurrentLoop + 1

*If (CurrentLoop,1) = 1
[Move the player forward]

*If (CurrentLoop,2) = 1
[Shoot]

------------------------------------------------------------

There is no need to store anything like enemy movement or different weapon modes, they are already stored in the game’s code. Think about a game where you can find different weapons and take them by touching them. As long as the movement is replicated perfectly, the player will take the weapons on exactly the same loops, won’t they? No need to store a value such as “weapon taken”. As mentioned, only the user input is of real importance. If the player himself chooses the weapons with the keyboard etc., it has to be stored, otherwise it doesn’t. There is also something else that has to be considered: the random values.

You might think that the random values can’t be predicted by the program, but this is actually not true. It is possible by making use of randomizer seeds. If you use the standard MMF randomizing, this seed can be changed with the action “randomize” located in the “special conditions”. Just insert that action and type in any value that you prefer. For an example, here we have the randomizer seed set to 7. Insert a counter and make so that when you press space bar, it gets changed to random(1000). Run the program and press space bar 3 times. Close the program, repeat it again and notice that the values are always the same.

For the example values, they turned out this way:



1=355
2=9
3=638

It’s important to take advantage of this when making recordings. It means that you don’t have to save all those random values, just simply save the random seed that you used for that game and the randomized values will be replicated perfectly in the playback. It is not a good idea to use the same seed for every game, so just set the seed to something like game time. This is best to do in the start of a game, before any other events.

This method’s weak side is that it gets out-of-sync if not replicated perfectly. With the method 2, if there is one value wrong, it may not ruin the recording as a whole. Here, even one pixel differences can mess it up completely, and things in MMF such as the event skip bug can easily affect this.

------------------------------------------------------------
An example of a recording going out-of-sync:
------------------------------------------------------------

------------------------------------------------------------

When Recording:

-Player’s angle is 90
-Player turns to 180 -> (doesn’t get saved for some reason)
-Player moves forward to 180
-Player sees a monster and shoots it to death
-Player wins the game

------------------------------------------------------------

When Playing Back:

-Player’s angle is 90
-[this didn’t get saved so nothing happens]
-Player moves forward to 90
-Player shoots in the wrong direction and the monster doesn’t die
-Since the monster didn’t get killed, it comes and kills the player

------------------------------------------------------------

So the simple fact that one value wasn’t saved properly caused the whole outcome of the game to be different. It can be daunting to make all the stuff replicate properly, but once the system is finished, it is flexible and easy to add new stuff in the game without having to modify the recording system much. This method is also very good for finding and fixing bugs. Since it is a “perfect” clone of the played game, the bugs should also get replicated.

************************************************************
Arrays and smaller file sizes
************************************************************

I find arrays the best choice for storing the kind of data that is needed here. The MMF array object works just fine. If you want the best performance, make sure that the array doesn’t have to be resized in runtime (make it big enough), since resizing it within every loop can cause a little bigger performance hit. When done properly, the recording process doesn’t require much cpu time at all, just modifying a few values every loop. The file size is a bit more problematic.

Making the files as small as possible is quite important and user-friendly. The problem with the MMF Array is that it saves all values, including 0’s. This means that if you have an array that is 20000x20, it will take a minimum of 20000*20 bytes. Unless you do it the “resize in runtime”-way, even shorter games that last for something like 1000 loops will take the same amount of space. Therefore it is important that you try to decrease the amount of dimensions that the array has as much as you can. One way of doing this would be to squeeze many values into one slot.

------------------------------------------------------------

Inefficient way:

-Player Turns Left: set(CurrentLoop,1)to 1
-Player Turns Right: set(CurrentLoop,2)to 1
-Player Moves Forward: set(CurrentLoop,3)to 1

------------------------------------------------------------

Efficient way:

-Player Turns Left: Add 1 to (CurrentLoop,1)
-Player Turns Right: Add 2 to (CurrentLoop,1)
-Player Moves Forward: Add 4 to (CurrentLoop,1)

------------------------------------------------------------

By using the more efficient way, we can make it work with only 1 slot instead of 3. The filesize can be reduced to 1/3 with this. Reading the values is a bit more complex, but not really hard, 1 means turn left, 3 means turn in both ways, 6 means move right and forward etc.

There is another way of making the files smaller. The recordings that have been saved as an MMF Array are usually about 95% filled with zero values that are useless in the game itself. If we remove them, we get an absolutely huge size drop. A simple way of doing this is to use the Zip Object. When the file is zipped, the zero parts are removed and no longer take space. I’ve had 3 megabyte arrays squeezed to under 50 kilobytes by zipping them. When the files are zipped, they obviously can’t be used, but just extract them with the same object when you want to use them.

Using all these tips together allows you to make files so small in size that it practically doesn’t matter how many of them there are. You could also attach some statistics of the game like accuracy % or survival time to be saved with the recording. Lastly, it is user-friendly and often needed to make it possible to copy/move the files, so create them in a directory where the user can easily find them.

------------------------------------------------------------
------------------------------------------------------------
------------------------------------------------------------

The information and tips shown here are based for real-time action games, though they can easily be transformed to suit other types of games. Recording doesn’t work well for all games though. Everybody gets bored if they have to watch a game that’s something like 45 mins in length, so prefer it for games that have a shorter time for each play. As some may have noticed, the methods used in this article could also be used for net games, where as little data as possible should be sent to maintain lagless gameplay. The out-of-sync possibility is even more of a problem with net play though, because transfer corruption can occur and mess up the games even if only one byte fails to reach the destination.

EDIT:

Also remember that you shouldn't use the "every"-condition at all in games that have the recording option, because it will cause things to happen differently on slower computers, messing up the playback.

An example of the method 3 is now available:

http://www.mbnet.fi/~reafer/recexample.zip