# Event Queue ## Pattern Scope **Event Queues** are a powerful tool that help make executions **synchronized** and ensure the user **perceives visual feedback in order**.. The main idea is to create a **linear execution** of elements **based on their order of entrance**. Patterns like: - [[00-bytecode]] - [[02-finite_state_machine]] - [[00-double_buffer]] - [[00-data_locality]] - [[00-command]] share similar concepts or **fit very well** with the _event queue_ approach. A practical example of applying this pattern would be an **in-game shop**. A player's **speed of interaction** (e.g., buying multiple items) is usually _faster_ than the **visual feedbacks** attached to each action. So, if the player buys _five items in less than three seconds_, and each feedback takes _one second_ to display, we can use an **event queue** to: - Avoid triggering multiple feedbacks simultaneously (if one is already playing). - **Enqueue** all feedbacks and **execute them linearly**. When working with **visual feedback**, it’s important to **maintain consistency** with the approach used throughout the game, this preserves **visual coherence**.. Another example is **server requests**, an event queue helps to prevent sending _100 new requests before the first one finishes_. ### <img src="00-assets/01-images/02-icons/code-icon-36x36.png" align="left" width="24px" height ="24px" style="margin:2px" /> Pattern Application Let’s use the **shop item** example. We can: - Make a **direct call** from the _shop controller_ to the _UI_ (using an **OOP**[^1] and **controller-view**[^2] approach). - Use **event notifications** to communicate between systems (**EOP**[^1]), letting the UI know when something is bought. - Use a **looping system** to check data changes in each item and decide when to update the UI and logic controllers (a **DOP**[^1] and **ECS**[^3] approach). These are just examples, _choose what best fits your architecture._ ```csharp public void BuyItem(ShopItem item) { //Code //Notify element to be bought this.notifyBoughtItemEvent?.Invoke(this, item); } ``` > [!CAUTION] > This feedback is **conceptual and oversimplified**. ```csharp public class UIFeedbackCommand : ISimpleCommand, ICommandData, ICommandEvents<object> { private readonly Sprite sprite; private readonly GameObject targetFeedbackGO; private Coroutine feedbackCoroutine; #region ICommandData_Values public bool IsCommandRunning => this.feedbackCoroutine != null; #endregion #region ICommandEvents_Values public event Action<Coroutine> CommandStarted; public event Action<Coroutine> CommandEnded; #endregion public UIFeedbackCommand(Sprite sprite, GameObject targetFeedbackGO) { this.sprite = sprite; this.targetFeedbackGO = targetFeedbackGO; } //A punch animation on the sprite can be a good feedback private IEnumerator PunchFeedback() { CommandStarted?.Invoke(this, this.feedbackCoroutine); //Execute & wait the visuals var currentCoroutine = this.feedbackCoroutine; this.feedbackCoroutine = null; CommandEnded?.Invoke(this, currentCoroutine); } #region ISimpleCommand_Methods public void Execute() { if(IsCommandRunning) return; this.feedbackCoroutine = targetFeedbackGO.StartCoroutine(PunchFeedback()); } #endregion } ``` ```csharp private readonly Queue<UIFeedbackCommand> feedbackQueue = new(); public void OnItemBoughResponse(object sender, ShopItem item) { var feedbackCommand = new UIFeedbackCommand(item.sprite, item.gameObject); this.feedbackQueue.Enqueue(feedbackCommand); if(this.feedbackQueue.Count == 1) this.feedbackQueue[0].Execute(); } private void OnFeedbackCommandEndsResponse(object sender, Coroutine args) { //Run next command and unsubcribe of the command (UIFeedbackCommand)sender.CommandEnded -= OnFeedbackCommandEndsResponse; this.feedbackQueue.Dequeue(); if(this.feedbackQueue.Count > 0) this.feedbackQueue[0].Execute(); } ``` As you can see, the **feedbacks are stacked**, and they’re **executed linearly** until the queue is empty. Even though these examples are conceptual, the **core idea** is clearly represented. ### <img src="100-assets/01-images/02-icons/info-icon-36x36.png" align="left" width="24px" height ="24px" style="margin:2px" /> Considerations > [!WARNING] > If you are using **events** to trigger the next element, be **careful to unsubscribe** from all of them if the flow is **interrupted**. # References - [Game Programming Patterns by Robert Nystrom](https://gameprogrammingpatterns.com/event-queue.html) [^1]: [[01-programming_paradigms]] [^2]: [[04-controller_view]] [^3]: [[03-ecs_architecture]]