
혼자 공부할 때 가장 시간을 많이 잡아먹는 것은 오류입니다. 같은 오류를 몇 시간씩 붙잡고 있다가 포기하는 경우가 많습니다. 수업에서는 오류 메시지를 읽는 법…
일반 프로그램과 게임이 다르게 움직이는 이유
문서 편집기나 계산기 같은 일반 프로그램은 사용자가 무언가를 할 때까지 기다립니다. 키를 누르면 반응하고, 아무것도 안 하면 화면도 그대로입니다. 이런 방식을 이벤트를 기다린다고 표현합니다.
게임은 다릅니다. 플레이어가 가만히 있어도 적은 다가오고, 구름은 흘러가고, 타이머는 줄어듭니다. 세상이 계속 살아 움직여야 하므로, 게임은 기다리지 않고 스스로 계속 돌아가는 반복문을 가지고 있습니다. 이 반복문이 게임 루프입니다.
애니메이션 영화가 정지된 그림을 빠르게 넘겨 움직임을 만드는 것처럼, 게임도 매 순간 세상을 조금씩 바꾼 그림을 연속으로 보여 줍니다. 다른 점은 영화는 미리 그린 그림을 틀지만, 게임은 플레이어의 입력에 따라 매번 새로 그린다는 것입니다.
루프 한 바퀴 안에서 일어나는 일
게임 루프의 기본 형태는 세 단계로 설명됩니다. 실제 엔진은 훨씬 많은 단계를 가지고 있지만, 큰 줄기는 같습니다.
- 입력 처리: 키보드, 마우스, 패드, 터치 상태를 읽습니다.
- 업데이트: 입력과 규칙에 따라 캐릭터 이동, 충돌, 점수, 적의 판단 같은 게임 상태를 바꿉니다.
- 렌더링: 바뀐 상태를 바탕으로 화면을 새로 그려 모니터에 보냅니다.
이 세 단계가 끝나면 다시 처음으로 돌아갑니다. 플레이어가 점프 키를 누르면, 다음 바퀴의 입력 단계에서 그 사실을 읽고, 업데이트 단계에서 캐릭터를 띄우고, 렌더링 단계에서 공중에 뜬 모습을 그립니다. 그래서 아무리 빨라도 입력과 화면 사이에는 최소 한 바퀴만큼의 시간이 걸립니다.
업데이트 안에서도 순서가 중요합니다. 캐릭터를 먼저 움직이고 충돌을 검사할지, 충돌을 먼저 검사할지에 따라 벽을 뚫는 버그가 생기기도 하고 사라지기도 합니다. 게임 프로그래머가 기능을 넣을 때 '어느 시점에 실행되는가'를 늘 따지는 이유입니다.
프레임과 FPS가 체감에 주는 영향
FPS가 높을수록 화면이 부드럽고 입력 반응도 빨라집니다. 한 바퀴가 짧게 끝날수록 같은 시간에 더 자주 입력을 읽고 화면을 고치기 때문입니다. 액션 게임이나 대전 게임에서 프레임이 중요하게 다뤄지는 이유입니다.
FPS는 루프 한 바퀴에 걸리는 시간과 반대 관계입니다. 1초에 60번 그리려면 한 바퀴를 약 16.7밀리초 안에 끝내야 합니다. 이 시간 안에 입력, 물리, 인공지능, 사운드 처리, 화면 그리기를 모두 마쳐야 합니다. 어느 하나라도 오래 걸리면 그 프레임이 늦게 나오고, 플레이어는 화면이 툭 끊기는 느낌을 받습니다.
평균 FPS보다 들쭉날쭉함이 더 거슬린다는 점도 기억해 두세요. 대부분 빠르다가 가끔 한 프레임이 길게 걸리면, 평균 숫자는 높아도 플레이어는 버벅임을 느낍니다. 그래서 실무에서는 프레임 시간이 일정하게 유지되는지를 함께 봅니다.
게임 엔진 안에서 게임 루프를 만나는 곳
Unity나 Unreal Engine을 쓰면 게임 루프를 직접 짜지 않습니다. 엔진이 이미 루프를 돌리고 있고, 개발자는 그 루프의 정해진 지점에 내 코드를 끼워 넣습니다.
Unity에서는 스크립트에 Update라는 함수를 만들면 매 프레임 한 번씩 호출됩니다. 물리 계산용인 FixedUpdate는 프레임과 별개로 일정한 간격으로 호출됩니다. Unreal Engine에서는 액터의 Tick 함수가 매 프레임 호출되는 역할을 합니다. 이름은 달라도 '루프가 한 바퀴 돌 때마다 이 코드를 실행해 달라'는 같은 개념입니다.
처음 엔진을 배우는 사람은 모든 코드를 Update에 넣는 경향이 있습니다. 매 프레임 확인할 필요가 없는 일까지 넣으면 루프가 무거워집니다. 한 번만 하면 되는 준비 작업은 시작 시점에, 가끔 일어나는 일은 이벤트로 처리하는 습관이 필요합니다.
처음 배울 때 헷갈리는 점
가장 흔한 문제는 프레임마다 일정한 거리를 움직이게 코딩하는 것입니다. 매 프레임 1씩 이동하게 하면, 빠른 컴퓨터에서는 캐릭터가 빨리 달리고 느린 컴퓨터에서는 천천히 움직입니다. 이 문제는 지난 프레임에서 흐른 시간을 곱해 주는 델타 타임으로 해결합니다.
또 하나는 화면 그리기와 게임 로직이 늘 같은 속도라고 생각하는 것입니다. 많은 엔진은 물리 계산을 고정 간격으로 따로 돌리기 때문에, 한 프레임 안에 물리가 여러 번 계산되거나 한 번도 계산되지 않을 수도 있습니다. 입력은 Update에서 읽고 물리 힘은 FixedUpdate에서 주는 식으로 역할을 나누는 이유입니다.
마지막으로, 루프 안에서 오래 걸리는 작업을 하면 게임 전체가 멈춘다는 점을 놓치기 쉽습니다. 큰 파일을 읽거나 복잡한 계산을 한 프레임 안에서 끝내려 하면 그동안 화면이 얼어붙습니다. 여러 프레임에 나눠 처리하거나 로딩 화면을 두는 방식이 필요한 이유입니다.
직접 만들어 보면 가장 빨리 이해됩니다
엔진 없이 콘솔 창에서 숫자가 1초마다 바뀌는 작은 루프를 짜 보거나, 화면에 사각형 하나를 띄우고 키로 움직이는 프로그램을 만들어 보세요. 입력, 업데이트, 그리기 세 단계를 손으로 나눠 써 보면 엔진의 Update와 Tick이 무엇을 대신해 주는지 분명해집니다.
엔진 화면만 따라 하다 보면 코드가 언제 왜 실행되는지 놓치기 쉽습니다. 기초 원리부터 차근차근 짚고 싶다면, SBS아카데미게임학원 게임프로그래밍 과정 상담에서 C언어·C++ 기초와 엔진 실습이 어떤 순서로 이어지는지 확인해 보시면 공부 계획을 세우기 쉬워집니다.
자주 묻는 질문
FPS를 60으로 고정하는 이유는 뭔가요?
많은 모니터의 화면 갱신 주기에 맞추기 쉽고, 부드러움과 성능 부담 사이의 균형이 좋기 때문입니다. 고주사율 모니터를 지원하는 게임은 더 높게 설정하기도 합니다.
프레임이 떨어지면 항상 그래픽 문제인가요?
아닙니다. 복잡한 계산, 많은 드로우콜, 메모리 정리 같은 CPU 쪽 작업이 원인일 때도 많습니다. 프로파일러로 어느 단계에서 시간이 걸리는지 먼저 확인해야 합니다.
가까운 캠퍼스에서 게임프로그래밍 알아보기
사는 곳과 가까운 캠퍼스를 고르면 수업 내용, 오시는 길, 상담 신청을 한 번에 볼 수 있습니다.