명령 대기열(command Queue)은 큐 기반 구조(FIFO)를 사용해 매 프레임마다 명령을 수집하고 일관되게 처리하는 디자인 패턴입니다. 이 방식은 게임 시스템과 오브젝트의 동작을 분리하며, 게임 로직을 큐에 쌓인 명령 중심으로 구성해 동작을 깔끔하고 유연하게 제어할 수 있도록 돕습니다. (적용 사례: D3D12 기반 비행 데모)
⚠️ 참고: 이 패턴은 구조화와 모듈화를 개선하지만, 지연 실행으로 인한 오버헤드가 발생할 수 있고, 명령이 잘못 전달되거나 예상치 않게 지연되면 디버깅이 어려워질 수 있습니다.
CCommandQueue는 FCommand 객체를 FIFO 방식으로 관리하고 실행을 처리합니다. 명령 생성과 실행 시점을 분리하여, 게임 로직을 더 깔끔하고 모듈화된 형태로 구성할 수 있습니다.
코드 분석
사용 예제
#define FakeDeltaTime 0.016fint main(){ CScene* scene = new CScene; CPlayer* player = new CPlayer; CMonster* monster = new CMonster; scene->AddObject(player); scene->AddObject(monster); // This simulates 5 frames, pushing and executing one command per frame. for (int frame = 1; frame <= 5; frame++) { std::cout << ">> [Frame " << frame << "] <<\n"; FCommand cmd; cmd.action = [player, monster, frame](float dt) { if (frame % 2 == 0) player->PlayerAction(dt); else monster->MonsterAction(dt); }; scene->PushCommand(cmd); scene->Update(FakeDeltaTime); std::cout << "\n"; } delete scene; return 0;}
이 예제는 player, monster, 그리고 frame을 캡처하는 람다를 사용해 각 프레임마다 명령을 생성하는 5프레임 처리 과정을 수행합니다. 생성된 각 명령은 PushCommand()를 통해 scene에 추가하고, Scene::Update()에서 실행됩니다.
CScene::Update()
void CScene::Update(float deltaTime){ // Process all pending commands while (!mCommandQueue.IsEmpty()) { FCommand cmd = mCommandQueue.Dequeue(); if (cmd.action) cmd.action(deltaTime); }}
이 함수는 mCommandQueue에서 대기 중인 모든 명령을 순서대로 제거하며 실행합니다.
맺는 말
명령 대기열은 로직과 실행을 명확히 분리하여 프레임 기반 시스템을 더욱 모듈화하고 유지 보수성을 높이는 동시에 구조화와 타이밍 제어를 개선, 일괄 처리 최적화 같은 장점을 가질 수도 있습니다.
반면, 지연 실행으로 인한 오버헤드가 발생하고 명령의 순서 관리나 디버깅이 복잡해질 수 있으며 명령 대기열에 명령을 계속 보관하는 경우 메모리 사용량이 증가할 수도 있는 단점이 있습니다.