숏컷 코드
while (running)
poll input
update simulation
render frame문법과 예시
render가 빠르다고 simulation이 정확한 것은 아니다
게임 루프는 "사용자 입력을 받고, 월드를 갱신하고, 현재 상태를 화면에 그린다"는 반복 구조입니다. 엔진을 써도 이 구조는 사라지지 않고, 엔진이 루프를 대신 소유할 뿐입니다. 중요한 점은 render cycle과 simulation cycle이 같은 속도로 돌아야 할 이유가 없다는 것입니다. 렌더링이 빠를수록 화면이 부드럽게 보이지만, 물리 연산이나 게임 판정이 frame rate에 묶여 있으면 60fps 환경과 30fps 환경에서 결과가 달라지는 frame-rate dependency 문제가 생깁니다.
// frame-rate dependent 코드: 느린 PC에서 캐릭터가 느리게 움직임
position += speed;
// frame-rate independent: deltaTime으로 보정
position += speed * deltaTime;variable timestep과 fixed timestep의 트레이드오프
variable timestep은 매 frame마다 실제 경과 시간(deltaTime)을 simulation에 그대로 전달합니다. 구현이 단순하고 대부분의 이동, 애니메이션, 카메라 로직에 잘 맞습니다. 단점은 deltaTime이 매 frame 달라지기 때문에 물리 연산 결과가 미세하게 흔들리거나, 극단적인 lag spike 상황에서 오브젝트가 벽을 통과하는 현상이 생길 수 있습니다.
fixed timestep은 simulation을 고정된 간격(ex. 0.02초, 50Hz)으로만 돌립니다. 시간이 누적되면 catch-up을 위해 한 frame 안에 여러 번 update가 실행될 수 있습니다. 물리, deterministic rule, replay, 네트워크 동기화처럼 일관된 결과가 필수인 시스템에 적합합니다. 비용은 누적 시간 관리 코드가 필요하고, catch-up 부담이 생긴다는 점입니다.
accumulator += deltaTime
while (accumulator >= fixedStep)
physicsUpdate(fixedStep)
accumulator -= fixedStep
render(interpolationFactor = accumulator / fixedStep)input, simulation, render의 책임을 분리하면 구조 수명이 길어진다
처음부터 루프 안에 모든 것을 넣으면 나중에 물리, 네트워크, replay를 붙이려 할 때 구조를 다시 뜯게 됩니다. 실무에서는 "입력 수집 → 고정 간격 simulation update → 현재 상태 render"처럼 역할을 분리하는 경우가 많습니다. Unity의 Update / FixedUpdate / LateUpdate, Unreal의 Tick / 물리 tick이 이 분리의 표현입니다. 중요한 것은 어떤 코드가 어떤 타이밍에 실행되어야 하는지를 명확하게 결정하는 것입니다.
// 책임 분리 예시
pollInput() // 매 frame: 입력은 항상 최신 상태로
physicsUpdate(fixedStep) // 0.02초 간격: 물리·판정
renderUpdate(interpolation) // 가능한 한 자주: 현재 상태 보간 렌더루프와 timestep을 고를 때 핵심
| 상황 | 적합한 선택 |
|---|---|
| 이동, 카메라, 애니메이션 | variable timestep + deltaTime 보정 |
| 물리, 충돌 판정, replay | fixed timestep (FixedUpdate) |
| 네트워크 동기화, deterministic simulation | fixed timestep 필수 |
| lag spike 대응 | fixed step에 최대 catch-up 횟수 제한 |
| 렌더링 보간(부드러운 중간 상태) | 누적 시간 비율로 보간 후 렌더 |
| 입력 반응성과 물리 안정성을 둘 다 챙겨야 할 때 | 입력은 매 frame 수집하고, 판정은 fixed step으로 분리 |
특이 케이스와 주의할 점
가장 흔한 실패는 "입력도 FixedUpdate, 카메라도 FixedUpdate, 물리도 Update"처럼 책임과 시간을 뒤섞는 것입니다.
이렇게 섞이면 30fps와 120fps에서 조작감이 달라지고, replay나 네트워크 동기화도 깨집니다. 입력 수집, simulation,
render 중 무엇이 매 frame이고 무엇이 고정 간격인지 먼저 나누세요.
참고 링크
2 sources