Como implementar o padrão Message Queue no Unity

Como implementar o padrão Message Queue no Unity

O padrão Message Queue é uma maneira simples e eficiente de desacoplar o código no seu projeto de jogo.

Uma das formas mais fáceis e eficazes de desacoplar o código em um jogo é implementando o padrão Message Queue, também conhecido como Event Queue.

Esse padrão consiste em enviar mensagens quando você deseja atualizar algo na interface do jogo, por exemplo, sem precisar manter uma referência direta ao objeto da interface ou utilizar um padrão Singleton para expor acesso a ele. Em vez disso, a interface escutará o tipo de mensagem necessário para atualizar a interface, e essa mensagem será enviada apenas quando um sistema do jogo precisar atualizar a interface.

Vamos dividir o padrão Message Queue em três partes: o gerenciador da fila, responsável pela fila em si; a mensagem, que contém o que queremos enviar; e os ouvintes, que ficam esperando a mensagem para executar alguma ação assim que a receberem.

O gerenciador Message Queue

O núcleo do padrão Message Queue é uma classe responsável por armazenar uma lista de ouvintes, além de adicionar e remover ouvintes e enviar mensagens para cada ouvinte que estiver escutando um tipo específico de mensagem.

No código abaixo, criamos nossa classe MessageQueue e definimos um Dictionary para armazenar nossos ouvintes, organizando-os por tipo. Cada Type possui uma List de Delegate, que são callbacks executados quando um tipo específico de mensagem é recebido.

using System;
using System.Collections.Generic;

public class MessageQueue
{
    private readonly Dictionary<Type, List<Delegate>> _listeners;

    private MessageQueue()
    {
        _listeners = new Dictionary<Type, List<Delegate>>();
    }
}

Em seguida, adicionamos métodos à nossa classe MessageQueue. O primeiro é o AddListener, que tem o tipo genérico T para o parâmetro listener.

public void AddListener<T>(Action<T> listener)
{
    if (_listeners.TryGetValue(typeof(T), out List<Delegate> listeners))
    {
        listeners.Add(listener);
    }
    else
    {
        listeners = new List<Delegate> { listener };
        _listeners.Add(typeof(T), listeners);
    }
}

O próximo método é o RemoveListener, que remove um ouvinte da lista e, caso a lista fique vazia, a remove do Dictionary.

public void RemoveListener<T>(Action<T> listener)
{
    if (_listeners.TryGetValue(typeof(T), out List<Delegate> listeners))
    {
        listeners.Remove(listener);
        if (listeners.Count == 0)
        {
            _listeners.Remove(typeof(T));
        }
    }
}

Por fim, o método SendMessage encontra uma lista de ouvintes aguardando um tipo de mensagem e chama cada um como callback, passando a mensagem como parâmetro.

public void SendMessage(object message)
{
    if (_listeners.TryGetValue(message.GetType(), out List<Delegate> listeners))
    {
        for (int i = 0; i < listeners.Count; i++)
        {
            listeners[i].DynamicInvoke(message);
        }
    }
}

O objeto Message

O objeto mensagem pode ser qualquer classe. No exemplo abaixo, criamos uma classe que representa uma mensagem contendo uma propriedade NewColor.

using UnityEngine;

public class UpdateColorMessage
{
    public Color NewColor;
}

O uso dos ouvintes

A classe MessageQueueExample é um MonoBehaviour que pode ser adicionado a qualquer GameObject no Unity. Ela usa o MessageQueue para adicionar e remover um callback.

using UnityEngine;

public class MessageQueueExample : MonoBehaviour
{
    private readonly MessageQueue _messageQueue = new MessageQueue();

    private void OnEnable()
    {
        _messageQueue.AddListener<UpdateColorMessage>(UpdateColor);
    }

    private void OnDisable()
    {
        _messageQueue.RemoveListener<UpdateColorMessage>(UpdateColor);
    }

    private void UpdateColor(UpdateColorMessage message)
    {
        // Use o message.NewColor para atualizar a cor
    }
}

Os métodos OnEnable e OnDisable são executados uma vez pelo Unity, garantindo que os ouvintes sejam sempre adicionados e removidos corretamente.

Enviando mensagens

Abaixo estão três métodos que enviam mensagens para mudar a cor para vermelho, verde e azul.

public void ChangeColorToRed()
{
    var message = new UpdateColorMessage { NewColor = Color.red };
    _messageQueue.SendMessage(message);
}

public void ChangeColorToGreen()
{
    var message = new UpdateColorMessage { NewColor = Color.green };
    _messageQueue.SendMessage(message);
}

public void ChangeColorToBlue()
{
    var message = new UpdateColorMessage { NewColor = Color.blue };
    _messageQueue.SendMessage(message);
}

Cada método cria uma instância de UpdateColorMessage e define a propriedade NewColor antes de enviar a mensagem aos ouvintes.

Conclusão

O padrão Message Queue é muito poderoso para desacoplar o código e facilitar a comunicação entre sistemas usando mensagens. Espero que este artigo tenha ajudado você a entender como implementá-lo usando C# no Unity.

O código deste artigo, assim como um exemplo completo implementado no Unity, pode ser encontrado no meu repositório do GitHub.

Você já utilizou esse padrão em algum projeto? Compartilhe sua experiência nos comentários e me avise se tiver alguma dúvida!

comments powered by Disqus