게임에서의 시간

주상돈·2025년 4월 2일

TIL

목록 보기
46/53

이번 포스트에서는 게임 개발에서 중요한 시간 관리에 대해 알아보려고 한다. 우리가 일상에서 느끼는 연속적인 시간과 달리, 게임 내 시간은 프레임 단위로 끊어져 있어 여러 가지 특별한 문제와 기법들이 등장하는데요. FPS, Delta Time, 시간 스케일, 네트워크 동기화, 그리고 멀티스레드 시간 관리에 대해 하나씩 살펴보자.


1. 게임에서의 시간

일상에서는 시간이 부드럽게 흐르지만, 게임은 프레임 단위로 시간이 끊어져 있다. 이 때문에 다음과 같은 문제가 발생할 수 있다:

  • 캐릭터 애니메이션 문제: 움직임이 너무 빠르거나 느리게 보인다.
  • 물리 충돌 이상: 충돌 타이밍이 어긋나거나 빠른 물체가 벽을 뚫고 지나간다.
  • 프레임 종속 현상: FPS에 따라 게임 속도가 달라진다.

게임의 시간 개념을 제대로 이해하면 안정적이고 예측 가능한 플레이를 구현할 수 있다.


2. FPS(Frame Per Second)의 이해

FPS는 1초 동안 화면에 몇 개의 프레임이 표시되는지를 의미한다.
예를 들어 60FPS면 1초에 60장의 정지된 화면이 순차적으로 출력되므로, 한 프레임의 시간은 약 0.016초(1/60)이다.

  • FPS가 높으면: 화면이 부드럽고, 입력 반응이 즉각적이다.
  • FPS가 낮으면: 화면이 끊기거나 느려지며, 조작감과 게임 난이도에도 영향을 준다.

실시간 액션, 슈팅, 대전 게임 등에서는 FPS가 핵심 요소이다.


3. V-Sync와 FPS 제한

Unreal Engine 같은 엔진은 화면 렌더링 문제를 해결하기 위해 V-Sync(수직 동기화)FPS 제한 기능을 제공한다.

V-Sync(수직 동기화)

  • 개념: 그래픽 카드가 모니터의 주사율과 동기화해서 프레임을 출력하도록 한다.
  • 장점: 화면 찢어짐(스크린 티어링)을 줄여 깔끔한 화면을 유지할 수 있다.
  • 단점: 입력 지연이 발생할 수 있다.

FPS 제한

  • 개념: 불필요하게 높은 FPS로 시스템 자원이 낭비되는 것을 막기 위해 특정 FPS(예: 60FPS, 120FPS)로 제한한다.
  • 효과: 일정한 게임 속 시간 계산을 보장할 수 있다.

이러한 기능들은 Delta Time 계산에도 큰 영향을 미쳐, 안정적인 FPS 덕분에 캐릭터 움직임이나 물리 연산이 예측 가능해진다.


4. Delta Time(델타 타임)이란?

Delta Time은 이전 프레임과 현재 프레임 사이에 실제로 경과한 시간이다.
FPS가 변하면 매 프레임마다 Delta Time도 달라지므로, 이를 바탕으로 이동 거리나 속도를 계산하게 된다.

예시를 보자:

void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    FVector NewLocation = GetActorLocation() + (MoveDirection * Speed * DeltaTime);
    SetActorLocation(NewLocation);
}

또한, Delta Time을 활용하면 프레임 간 보간(Lerp)을 통해 자연스러운 애니메이션 전환을 구현할 수 있다.

// Delta Time에 따라 선형 보간(Lerp) 적용
FVector NewLocation = FMath::Lerp(CurrentLocation, TargetLocation, DeltaTime * InterpolationSpeed);
SetActorLocation(NewLocation);

이렇게 FPS 변화에 상관없이 초당 일정한 이동 거리를 구현할 수 있다.

5. Delta Time의 변동과 안정화 기법

게임은 프레임 단위로 시간이 끊어지기 때문에 FPS가 불안정하면 Delta Time 값이 크게 변동할 수 있다. 이를 보완하기 위한 기법들은 다음과 같다.

Variable Timestep (가변 시간 간격)

  • 설명: 매 프레임 계산된 실제 Delta Time 값을 그대로 사용한다.
  • 문제점: FPS 급락 시 한 프레임에 과도하게 이동할 수 있다.

Fixed Timestep (고정 시간 간격)

  • 설명: 일정한 시간 간격(예: 1/60초)마다 업데이트를 수행한다.
  • 장점: 물리 계산과 충돌 판정이 일정하게 진행된다.
  • 단점: 렌더링과 업데이트 사이 보간(interpolation) 처리가 필요하다.

Sub-Stepping (서브 스텝)

  • 설명: 한 프레임의 Delta Time을 여러 작은 단위로 나눠서 업데이트한다.
  • 장점: 고속 이동이나 복잡한 충돌 상황에서 정확도를 향상시킨다.
  • 단점: CPU/GPU 자원 소모가 증가할 수 있다.

Delta Time 클램핑

  • 설명: Delta Time 값이 너무 커지지 않도록 상한선을 정해 제한한다.
  • 예시 코드:
    float DeltaTime = GetDeltaTime(); // 실제 계산된 Delta Time
    const float MaxDeltaTime = 0.05f;   // 예: 50ms
    if (DeltaTime > MaxDeltaTime)
        DeltaTime = MaxDeltaTime;

6. 시간 스케일 (Time Scale)

시간 스케일은 게임 내 전체 시간의 흐름 속도를 조절하는 메커니즘이다.
이를 통해 게임 내 시간을 느리게, 빠르게 또는 일시 정지 상태로 만들 수 있다.

  • 예시 1: 액션 게임이나 슈팅 게임에서는 슬로우 모션 효과로 총알 궤적이나 폭발 효과를 극대화할 수 있다.
  • 예시 2: 시뮬레이션이나 퍼즐 게임에서는 빠른 진행으로 전체 게임 속도를 높인다.
  • 예시 3: 시간 스케일을 0으로 설정하면 게임 로직은 멈추지만, UI 애니메이션 등은 계속 작동할 수 있다.
void AMyPhysicsActor::Tick(float DeltaTime)
{
    // GlobalTimeScale은 전역 또는 액터 단위로 관리되는 시간 스케일 값이다.
    float AdjustedDeltaTime = DeltaTime * GlobalTimeScale;
    UpdatePhysics(AdjustedDeltaTime);
}

이처럼 시간 스케일을 활용하면 게임 내 다양한 효과를 손쉽게 구현할 수 있다.

7. 네트워크 게임에서의 시간 동기화

네트워크 게임에서는 클라이언트 간 지연이나 패킷 손실로 인해 각 클라이언트의 로컬 시간이 조금씩 달라질 수 있다.
이를 방지하기 위해 서버와 클라이언트 간의 시간 동기화가 필수적이다.

  • 서버 주도 방식: 서버가 주기적으로 타임스탬프를 보내고, 클라이언트는 이를 바탕으로 자신의 시간을 보정한다.
  • 입력 예측과 보정: 클라이언트가 로컬에서 예측한 후 서버 업데이트와 비교하여 차이를 보정한다.

이러한 동기화 기법은 반응 속도가 중요한 게임에서 매우 중요하다.


8. 멀티스레드 시간 관리

현대 게임은 물리 연산, AI, 렌더링 등 여러 작업을 멀티스레드로 처리한다.
이때 모든 스레드가 동일한 글로벌 타이밍 시스템을 공유해야 전체 게임 로직이 일관되게 업데이트된다.

  • 글로벌 타이밍 시스템: 모든 스레드가 동일한 시간 값을 참조하여 업데이트 간 시간 차이를 최소화한다.
  • 정밀한 시간 측정: FPlatformTime::Seconds() 등의 함수를 이용해 정확한 시간 측정 후 Delta Time을 계산한다.
  • 동기화 메커니즘: 락이나 큐 같은 방법으로 스레드 간 데이터 접근을 제어한다.

예시 코드:

// FRunnable 기반 스레드 내에서의 시간 관리 예시
double GlobalTime = FPlatformTime::Seconds();
float DeltaTime = GlobalTime - LastUpdateTime;
LastUpdateTime = GlobalTime;

// 이 Delta Time을 이용해 물리 연산을 업데이트한다.
UpdatePhysics(DeltaTime);

마무리

게임 개발에서 시간 관리는 안정적이고 예측 가능한 게임 플레이를 위해 매우 중요한 요소이다.
FPS 안정성, Delta Time의 일관성, 시간 스케일 조절, 네트워크 동기화, 그리고 멀티스레드 시간 관리 등 다양한 기법들을 적절히 활용하면, 플레이어에게 일관된 경험을 제공할 수 있다.

0개의 댓글