rewind actions in Unity

that games guy Icon.

by that games guy

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 🙂