Rewind Time

https://github.com/GandhiGames/rewind_controller

What is it?

This project provides a method of implementing a rewind effect in a game, popularised by games such as Braid.

Braid makes extensive use of a Rewind ability as a core game mechanic.

The rewind ability in this project is made possible due to the Command Pattern, which makes implementing a rewind ability surprisingly easy.

All actions in the scene (including player/enemy movement, deaths, projectile release/movement) are encapsulated as actionable command objects. The base Command class is shown below.

/// <summary>
/// Base abstract command. All commands (e.g. movement, attack) should inherit form this class.
/// </summary>
public abstract class Command
{
protected Transform character;
protected Vector2 dir;

public Command (Transform character = null, Vector3 dir = default(Vector3))
{
this.character = character;
this.dir = dir;
}

/// <summary>
/// Perform the action.
/// </summary>
public abstract void Execute ();

/// <summary>
/// Perform the reverse action.
/// </summary>
public abstract void Undo ();
}

Each command has an execute and undo method. For example, implementation of a simple movement command.

public class MovementCommand : Command
{
private Vector2? undoDir;

public MovementCommand (Transform character, Vector2 dir) : base (character, dir) { }

public override void Execute () 
{
undoDir = -dir;
character.Translate (dir, Space.World);
}

public override void Undo ()
{
if (undoDir.HasValue) character.Translate (undoDir.Value, Space.World);
}
}

When a command is generated it is added to a global group containing all commands executed.

rewind.AddCommand (new MovementWithPhysicsCommand (transform, amountToMove * Time.deltaTime, physics), true);
rewind.AddCommand (new ClimbLadderCommand (this, true), true);

This group simply encapsulates a list and provides access to a Reverse method (important in undoing the performed actions).

public class CommandGroup
{
private List<Command> commands = new List<Command> ();

public void Add (Command command)
{
commands.Add (command);
}

public void Remove (int index)
{
commands.RemoveAt (index);
}

public Command Get (int index)
{
return commands [index];
}

public int Length ()
{
return commands.Count;
}

public List<Command> Reverse ()
{
commands.Reverse ();
return commands;
}
}

To reverse the scene (and begin the rewind effect), you simply iterate through the command group in reverse order and execute the commands undo methods.

/// <summary>
/// Checks if it is ok to undo and calls the most recent commands undo method.
/// The command list is traversed from most recent to first.
/// When current command is less than 0 the complete flag is set to true.
/// This will stop the rewind process and enable the character to run new commands.
/// </summary>
protected override void Execute ()
{
if (CancelRequested ())
{
Reset ();
return;
}

foreach (var command in commands[currentGroup].Reverse ())
{
command.Undo ();
}

commands.RemoveAt (currentGroup);

currentGroup--;
if (currentGroup < 0)
{
Reset ();
}
}

Thank you for reading 🙂