본문으로 건너뛰기

시간 되감기 시스템

IRewindable 인터페이스를 구현한 모든 오브젝트의 상태를 스냅샷으로 기록하고 N초 전 상태로 복원합니다.

구조

TimeRewindSystem (순수 C# 인스턴스)
— 등록된 IRewindable 오브젝트의 스냅샷을 주기적으로 기록
— RewindTo(float secondsAgo)로 특정 시점 복원

IRewindable (interface)
— CaptureState() : 현재 상태를 object로 반환
— RestoreState() : 캡처된 상태로 복원

RewindableObject (MonoBehaviour)
— 기본 구현: Transform(position, rotation, scale)을 기록

빠른 시작

1. TimeRewindSystem 인스턴스 만들기

TimeRewindSystem은 순수 C# 클래스입니다. 소유 컴포넌트에서 생성하고 Update에서 Tick을 호출합니다.

생성자 인자기본값설명
maxHistorySeconds5최대 기록 시간 (초)
snapshotsPerSecond10초당 스냅샷 수

2. RewindableObject 부착

복원할 오브젝트에 RewindableObject 컴포넌트를 추가합니다. Transform이 자동으로 기록됩니다.

// 플레이어, 적, 발사체 등 되감기할 오브젝트에 부착
gameObject.AddComponent<RewindableObject>();

3. 되감기 실행

using AchUtils.TimeRewind;

[SerializeField] RewindableObject[] rewindables;

private TimeRewindSystem timeRewindSystem;

void Awake()
{
timeRewindSystem = new TimeRewindSystem(maxHistorySeconds: 5f, snapshotsPerSecond: 10);
foreach (var rewindable in rewindables)
rewindable.Bind(timeRewindSystem);
}

void Update()
{
timeRewindSystem.Tick(Time.deltaTime);
}

// 되감기 시작 (Update에서 계속 호출 가능)
timeRewindSystem.StartRewind();

// 특정 시점으로 복원
timeRewindSystem.RewindTo(secondsAgo: 3f);

// 되감기 종료 (이후 다시 기록 시작)
timeRewindSystem.StopRewind();

4. 스킬로 사용하는 예시

public class TimeRewindSkill : MonoBehaviour
{
[SerializeField] float rewindSeconds = 3f;
private readonly TimeRewindSystem timeRewindSystem = new();

IEnumerator UseSkill()
{
var system = timeRewindSystem;
system.StartRewind();

float t = 0f;
while (t < rewindSeconds)
{
t += Time.deltaTime;
system.RewindTo(rewindSeconds - t); // 점진적 되감기
yield return null;
}

system.StopRewind();
}
}

API

TimeRewindSystem

TimeRewindSystem(float maxHistorySeconds = 5f, int snapshotsPerSecond = 10)

void Register(IRewindable target)
void Unregister(IRewindable target)

void Tick(float deltaTime)

void StartRewind()
void StopRewind()

// N초 전 상태로 모든 등록 오브젝트를 복원
void RewindTo(float secondsAgo)

// 단계별 되감기 (delta만큼 과거로)
void StepRewind(float deltaSeconds)

bool IsRewinding
float HistoryDuration // 현재 기록된 히스토리 길이 (초)

event Action OnRewindStart
event Action OnRewindStop

IRewindable 직접 구현

RewindableObject 대신 커스텀 상태를 캡처하려면 인터페이스를 직접 구현합니다.

public class EnemyAI : MonoBehaviour, IRewindable
{
private TimeRewindSystem timeRewindSystem;

private float hp;
private Vector3 position;
private AIState state;

private void OnEnable() => timeRewindSystem?.Register(this);
private void OnDisable() => timeRewindSystem?.Unregister(this);

public object CaptureState()
{
return (hp, transform.position, state);
}

public void RestoreState(object snapshot)
{
(hp, position, state) = ((float, Vector3, AIState))snapshot;
transform.position = position;
}
}

리소스 최적화

스냅샷 개수 = MaxHistorySeconds × SnapshotsPerSecond × 등록 오브젝트 수

예: 5초 × 10fps × 20오브젝트 = 1000 스냅샷

오브젝트가 많거나 히스토리가 길면 SnapshotsPerSecond를 낮추거나 MaxHistorySeconds를 줄이세요.

활용 사례

용도설명
퍼즐 되돌리기잘못 놓은 블록을 이전 상태로 복원
시간 역행 스킬Braid, Titanfall 스타일 시간 되감기
디버깅버그 발생 직전 상태로 되돌아가 재현
리플레이 미리보기죽기 전 몇 초를 슬로우로 다시 보기

::: warning 물리 엔진 Rigidbody를 가진 오브젝트를 되감으면 물리 상태(velocity, angular velocity)도 함께 캡처해야 올바르게 복원됩니다. RewindableObject를 상속해 CaptureState를 오버라이드하세요.

public override object CaptureState()
{
var rb = GetComponent<Rigidbody>();
return (base.CaptureState(), rb.velocity, rb.angularVelocity);
}

:::