A Command Queue is a design pattern that uses a queue-based structure (FIFO) to collect commands each frame and process them consistently, separating game systems from object behavior. It organizes game logic around queued commands and helps control actions in a clean and flexible way. (e.g., used in D3D12 FlightDemo)
⚠️ Note: This pattern improves structure and modularity, but may introduce overhead due to deferred execution, and can be harder to debug if commands are misrouted or delayed unexpectedly.
#pragma once#include <functional>struct FCommand{ using Action = std::function<void(float)>; Action action;};
Wraps a callable action (usually a lambda or function object) that takes float deltaTime as its parameter. This allows the command to handle time-dependent logic.
The CCommandQueue manages a FIFO list of FCommand and handles their execution. It decouples command creation from execution timing, allowing cleaner and more modular game logic.
Code Breakdown
Usage Example
#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;}
This example simulates a 5-frame loop where each frame generates a command using a lambda that captures player, monster, and frame. Each command is pushed to the scene via PushCommand() and executed during 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); }}
This method dequeues from mCommandQueue, then executes all pending commands in order.
Conclusion
The command queue pattern can clearly separate logic from execution, making frame-based system more modular and maintainable, while also improving structure, timing control, and batch processing optimization.
On the other hand, it may introduce overhead due to deferred execution, complicate command ordering and debugging, and increase memory usage if commands are continuously retained in the queue.
Therefore, it’s important to consider these carefully and decide whether to adopt it based on the specific requirements of the project.