Course contentsShow
← Unity Game Development
Lesson 477 of 8287. Advanced Game ArchitectureFree lesson

Building Reusable Input History Buffers

Create a command history buffer to store and replay player actions for advanced gameplay features like replays or move-set recording.

In this lesson, you will learn how to architect a circular buffer to capture, store, and replay a sequence of player inputs, enabling features like frame-perfect replays or combo-recording systems.

The Core Concept: The History Buffer

At its simplest, an input history buffer is a collection that stores a series of ICommand objects over time. By using a "circular" or "fixed-size" buffer, we ensure the game doesn't crash from memory bloat during long play sessions; when the buffer reaches its limit, the oldest inputs are simply overwritten by the newest ones.

To make this reusable, we define a structure that captures the command itself and the exact timestamp or frame count it occurred.

Implementing the Buffer

Here is a simplified look at how to store these commands in a Queue structure:

public class InputHistoryBuffer
{
    private Queue<CommandEntry> _history = new Queue<CommandEntry>();
    private int _maxCapacity = 1000;

    public void Record(ICommand command)
    {
        if (_history.Count >= _maxCapacity)
            _history.Dequeue(); // Remove oldest to make room

        _history.Enqueue(new CommandEntry(command, Time.frameCount));
    }

    public void ReplayAll()
    {
        foreach (var entry in _history)
        {
            entry.Command.Execute();
        }
    }
}

Decoupling for Replayability

By passing an ICommand into the buffer, we decouple the act of performing an action from the state of the game. Because your commands implement a standard Execute() method (as learned in our previous Command Pattern lesson), the buffer doesn’t need to know if the action was a jump, a sword swing, or a spell cast. It simply stores the interface.

To replay the sequence, the system iterates through the queue and triggers Execute() on each command. If you want to simulate real-time playback, you can use a Coroutine to wait for the difference in Time.frameCount between entries, recreating the player's exact timing. This approach allows you to implement "undo" features or ghost-recording systems without modifying your core player controller logic.

Key Takeaway: By storing serialized Command objects in a fixed-size queue, you create a memory-efficient and modular history system that can perfectly reconstruct gameplay sequences without tight coupling to game objects.