Metlivi 블로그

가상 펫 게임 루프에서 대기 상태와 상호작용 상태를 분리하는 방법

가상 펫 게임에서는 자율적인 행동과 플레이어의 동작을 서로 다른 업데이트 경로로 유지한 뒤, 명시적인 전환을 통해 두 동작이 하나의 공유된 펫 상태를 수정하도록 하세요. 대기(idle) 행동은 경과 시간에 따라 진행되어야 하며, 상호작용(interactive) 행동은 입력 이벤트나 현재 입력 상태에 반응해야 합니다. 이렇게 하면 일시 정지와 테스트가 더 쉬워지고, 펫의 애니메이션이나 욕구 타이머가 플레이어의 명령과 충돌하는 것을 방지할 수 있습니다.

2026년 10월 15일6분 분량작성: Metlivi Editorial Team
섹션 1

‘대기’와 ‘상호작용’의 의미 정의하기

이 둘을 반드시 서로 다른 씬(scene)이나 별개의 루프로 볼 필요는 없으며, 행동 모드로 취급해야 합니다. 대기(idle) 상태에서 펫은 일상적인 루틴을 실행합니다. 소소한 주변 행동을 선택하거나, 타이머를 진행하거나, 필수적이지 않은 애니메이션을 재생하는 식입니다. 상호작용(interactive) 상태에서 게임은 탭, 드래그 또는 선택된 명령과 같은 의도적인 동작을 처리합니다. 상호작용 중에도 펫의 애니메이션은 계속 재생될 수 있습니다. 중요한 차이는 어느 시스템이 결정을 내리고 게임플레이 상태를 변경할 권한을 갖는가에 있습니다.

먼저 플레이어가 수행할 수 있는 동작과 펫이 수행할 수 있는 자율적인 행동의 간단한 목록을 작성해 보세요. 각각이 어떤 상태 변수를 변경할 수 있는지 표시합니다. 예를 들어, 무심코 눈을 깜빡이는 동작은 겉모습(렌더링)에만 영향을 줄 수 있지만, 선택된 놀이 동작은 펫의 현재 활동을 변경하고 타이머를 시작할 수 있습니다. 이러한 간단한 소유권 매핑은 코드가 비대해지기 전에 충돌을 발견하는 데 도움이 됩니다.

섹션 2

외부 루프를 펫의 행동과 독립적으로 유지하기

엔진의 프레임 콜백은 펫의 모든 규칙을 담기보다는 작업을 조율하는 역할을 해야 합니다. 유용한 순서는 다음과 같습니다. 입력을 수집하고, 유효한 명령을 적용하고, 시뮬레이션을 진행한 다음, 최신 상태를 렌더링합니다. 입력 수집과 시뮬레이션을 분리하여 탭 입력이 실수로 타이머 틱으로 처리되지 않도록 하고, 렌더링에서는 게임플레이 변경이 일어나지 않도록 유지하세요. Game Programming Patterns에서는 이러한 분리를 입력 처리, 게임 업데이트, 렌더링이 반복되는 루프로 설명합니다. 고정 단계(fixed-step) 업데이트는 시뮬레이션에 안정적인 주기가 필요할 때 유용합니다. Game Loop, Game Programming Patterns

웹 게임의 경우, requestAnimationFrame은 다시 그리기(repaint) 전에 작업을 예약하고 경과 시간을 측정하는 데 사용할 수 있는 타임스탬프를 제공합니다. 브라우저는 일반적으로 숨겨진 탭에서 이러한 콜백을 일시 정지하므로, 게임이 백그라운드에 있는 동안에도 실제 시계처럼 안정적으로 계속 작동할 것이라 가정해서는 안 됩니다. Window.requestAnimationFrame(), MDN

섹션 3

명령을 통해 플레이어 입력 라우팅하기

원시 입력을 start_play, stop_play, pet_tap과 같은 작은 명령으로 변환하세요. 명령 핸들러는 해당 동작이 현재 모드에서 유효한지 확인하고, 공유 모델을 한 번 업데이트한 뒤, 결과로 나타나는 시각적/청각적 연출 신호를 전달합니다. 이렇게 하면 동작 로직이 버튼 콜백, 애니메이션 콜백, 대기 루틴에 동시에 분산되는 것을 방지할 수 있습니다.

예를 들어, 펫이 이미 놀고 있는 중에 플레이어가 탭을 한다면 결과를 의도적으로 정의해야 합니다. 탭을 무시할지, 다른 활동을 선택할지, 아니면 활동을 멈출지 정하세요. 대기 선택기(idle chooser)와 탭 핸들러가 동일한 업데이트 주기 동안 둘 다 새로운 활동을 할당하지 않도록 해야 합니다. 이벤트 전파 기능이 있는 엔진에서는 적절한 단계에서 게임플레이 입력을 처리하고 UI의 이벤트 소모를 존중하세요. Godot의 Node 문서에 따르면 GUI 컨트롤에 우선순위가 필요할 때는 미처리 입력(unhandled input)이 게임플레이에 더 적합한 경우가 많습니다. Node class, Godot documentation

섹션 4

명확한 시계로 대기 시간 진행하기

화면에 표시되는 세션 동안 지속적으로 진행되어야 하는 행동의 경우, 경과된 시뮬레이션 시간을 누적하고 해당 주기에 도달했을 때 대기 결정을 트리거하세요. 프레임 수를 세어서는 안 됩니다. 프레임 빈도는 계속 달라지기 때문입니다. Godot에서 _process(delta)는 이전 대기 프레임 이후의 시간을 수신하는 반면, _physics_process(delta)는 물리 틱을 따릅니다. 또한 문서에서는 특정 조건에서 누적된 델타가 실제 현실의 초 단위 시간과 차이가 날 수 있음을 지적합니다. Node class, Godot documentation

행동의 성격에 맞춰 시계를 선택하세요. 시각적인 움직임은 대개 프레임 경과 시간을 사용할 수 있습니다. 충돌과 관련된 움직임은 엔진의 물리 업데이트에 속합니다. 펫의 '몇 초마다 주변 행동 선택' 타이머는 시뮬레이션 타이머를 사용할 수 있지만, 프레임 지연이 길어지거나 일시 정지된 후에 어떤 일이 일어날지 결정해야 합니다. 경과 시간을 제한하거나, 결정을 한 번만 수행하거나, 일정한 범위 내에서 단계를 나누어 따라잡도록(catch up) 처리할 수 있습니다. 제한 없는 따라잡기는 게임이 재개될 때 펫이 수많은 상태를 순식간에 건너뛰게 만들 수 있습니다.

섹션 5

모드 전환을 명시적으로 만들기

상태를 더 추가하기 전에 작은 상태 전이 표를 작성해 보세요. 예를 들어 idle → playing은 플레이어가 선택한 활동을 시작하고, playing → idle은 활동이 끝나거나 플레이어가 취소할 때 발생하며, idle → resting은 자율 루틴에 의해 선택될 수 있습니다. 타이머 시작 또는 해제, 관련 애니메이션 선택과 같은 진입 및 종료 효과는 한 곳에서 정의하세요. 애니메이션 완료만을 게임플레이 모드가 변경되었다는 증거로 사용해서는 안 됩니다. 시각적 재생은 도중에 중단되거나 건너뛰어질 수 있기 때문입니다.

전환 함수는 현재 모드의 유효성을 검사하고, 모델을 업데이트하며, 렌더러가 표시할 수 있는 결과를 반환해야 합니다. 이를 통해 '펫이 무엇을 하고 있는지'와 '그 행동이 어떻게 그려지는지'를 분리할 수 있습니다. 또한 테스트 시에도 명확한 기준이 생깁니다. 명령을 전달하고, 결과 모드를 검사한 뒤, 대기 루틴이 이를 덮어쓰지 않는지 확인하면 됩니다.

섹션 6

백그라운드 시간 동작 방식 결정하기

흔히 쓰이는 두 가지 정책이 있으며, 알맞은 선택은 게임 디자인에 따라 달라집니다. 활성 세션 중에만 작동하는 펫은 일시 중단되는 동안 시뮬레이션을 멈추고 이전 상태에서 재개할 수 있습니다. 게임이 닫혀 있는 동안에도 타이머가 진행되도록 설계된 게임은 시간 기준점을 기록하고 복귀 시 경과 시간을 조정해야 합니다. 백그라운드에서 프레임 루프를 실행하려고 하지 말고 이를 별도의 재개 계산으로 처리하세요. 브라우저의 애니메이션 콜백은 숨겨진 탭에서 일시 정지될 수 있습니다. Window.requestAnimationFrame(), MDN

단순한 펫의 경우 실제로 진행되어야 하는 수치(예: 활동 타이머 하나)만 조정하고, 따라잡기 처리에 합리적인 상한선을 두세요. 그런 다음 짧은 간격과 긴 간격으로 게임을 닫았다가 다시 열어보는 테스트를 진행합니다. 시계 선택, 일시 중단 동작, 따라잡기 상한선은 설계상의 결정 사항이며, 엔진의 프레임 콜백이 이를 대신 결정해 주지 않습니다.

섹션 7

경합하는 업데이트 및 누락된 입력 점검하기

재현 가능한 몇 가지 시나리오로 루프를 테스트하세요. 몇 차례의 주기 동안 게임을 대기 상태로 두기, 대기 결정 직전에 명령 내리기, 입력을 길게 누르거나 반복하기, 일시 정지 후 재개하기, 애니메이션이 아직 재생 중일 때 새로운 동작 트리거하기 등이 있습니다. 각 명령이 모델을 한 번만 변경하는지, 대기 로직이 플레이어가 선택한 상태를 덮어쓰지 않는지, 경과 시간 동작이 선택한 일시 정지 정책과 일치하는지 확인하세요.

시뮬레이션에 고정 업데이트(fixed update)를 사용하는 경우, 렌더링된 프레임 사이에 이것이 0회, 1회 또는 여러 번 실행될 수 있음을 유의하세요. Unity는 FixedUpdate에 대해 이러한 동작을 문서화하고 물리 계산에 사용할 것을 권장합니다. 입력과 시각적 업데이트는 타이밍 고려 사항이 다릅니다. FixedUpdate(), Unity documentation Unity의 Input System 또한 장치 이벤트를 큐에 넣고 구성된 업데이트 모드에 따라 처리하므로, 입력 처리를 해당 명령을 사용하는 루프와 일치시키세요. Input event queue, Unity Input System

섹션 8

유지해야 할 실용적인 분리 구조

아키텍처를 단순하게 유지하세요. 공유된 펫 모델 하나, 입력-명령 경로 하나, 대기 행동 업데이트 하나, 명시적 전환 함수 하나, 그리고 모델을 읽는 렌더러 하나로 구성합니다. 타이밍에 민감한 동작에 필요한 경우에만 고정 시뮬레이션 단계를 추가하고, 모든 애니메이션이나 주변 환경 선택이 물리 틱에 의존하게 만들지 마세요. 버그가 발생하면 누가 마지막으로 모드를 변경했는지, 어떤 시계가 이를 진행시켰는지 살펴보세요. 이렇게 하면 각 업데이트에 명확한 역할이 부여되어 펫의 행동이 늘어나더라도 대기 및 상호작용 동작을 예측 가능하게 유지할 수 있습니다.

섹션 9

출처

관련 글

이 주제 더 살펴보기