Menu
CoddyTech

Eventos de C# no Unity

Parte da seção Arquitetura de Jogos no Unity do Journey de Unity da Coddy. Lição 12 de 64.

Quando a pontuação muda, várias coisas se importam: o texto da pontuação, um som, uma conquista. Se o ScoreKeeper chamar cada uma delas, precisará de um campo para cada uma, e cada novo recurso significará editá-lo. Um evento inverte essa lógica: o ScoreKeeper anuncia a mudança, e quem se importa se inscreve.

Duas palavras de C# fazem isso funcionar. Action<int> é o tipo de um método que recebe um int e não retorna nada. Um event desse tipo é uma lista desses métodos: outro código adiciona seu método com +=, e o proprietário chama todos eles com Invoke. Em C# puro:

using System;

class Alarm
{
    public event Action<int> Rang;

    public void Ring(int hour)
    {
        Rang?.Invoke(hour);
    }
}

class Sleeper
{
    public void WakeUp(int hour)
    {
        Console.WriteLine("Up at " + hour);
    }
}

// em outro lugar
Alarm alarm = new Alarm();
Sleeper sleeper = new Sleeper();
alarm.Rang += sleeper.WakeUp;
alarm.Ring(7);   // De pé às 7

No Unity, o publicador e seus ouvintes são componentes, geralmente em GameObjects diferentes:

using System;
using UnityEngine;

public class ScoreKeeper : MonoBehaviour
{
    public event Action<int> OnScoreChanged;
    private int score;

    public void AddPoints(int points)
    {
        score += points;
        OnScoreChanged?.Invoke(score);
    }
}
// ScoreLabel.cs, no texto do HUD
public ScoreKeeper keeper;
public TMP_Text label;

void Start()
{
    keeper.OnScoreChanged += ShowScore;
}

void ShowScore(int score)
{
    label.text = "Score: " + score;
}

Action<int> significa que cada ouvinte é um método que recebe um int, neste caso, a nova pontuação. Invoke chama todos os métodos inscritos, na ordem em que se inscreveram. Sem ouvintes, o evento é null, então uma chamada simples como OnScoreChanged(score) lançaria uma NullReferenceException, enquanto ?. ignora a chamada. A palavra-chave event limita outros scripts a += e -=: em ScoreLabel, keeper.OnScoreChanged(5) ou keeper.OnScoreChanged = null não compila.

O ScoreKeeper não tem um campo para o rótulo e não sabe que ele existe. Um contador de combos ou um reprodutor de som se inscreve da mesma maneira, e o ScoreKeeper continua igual; uma cena de teste sem HUD é executada sem erros. Um ouvinte encontra o publicador por meio de uma referência no Inspector, como aqui, ou de qualquer outra forma que você conheça, como GetComponent ou um singleton. Remover um ouvinte com -= é importante quando os objetos são destruídos, assunto da próxima lição.

challenge icon

Desafio

Fácil

Faça o lutador anunciar seu combo.

  1. Em Fighter.cs, adicione using System; e public event Action<int> OnComboChanged;.
  2. Substitua as linhas que usam comboText e flame por OnComboChanged?.Invoke(combo); e exclua os dois campos.
  3. No Start() de ComboText e ComboFlame, escreva fighter.OnComboChanged += Show; e += Grow;.

Pressione “Jogar” e aperte Espaço algumas vezes: o combo aumenta e depois zera. Em seguida, clique em “Verificar”.

Experimente você mesmo

using UnityEngine;
using UnityEngine.InputSystem;
using TMPro;

public class Fighter : MonoBehaviour
{
    public float comboWindow = 1f;
    public TMP_Text comboText;
    public Transform flame;
    private int combo;
    private float sinceHit;

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            combo++;
            sinceHit = 0f;
            comboText.text = "Combo x" + combo;
            flame.localScale = Vector3.one * (0.5f + 0.25f * combo);
        }
        else if (combo > 0)
        {
            sinceHit += Time.deltaTime;
            if (sinceHit > comboWindow)
            {
                combo = 0;
                comboText.text = "";
                flame.localScale = Vector3.one * 0.5f;
            }
        }
    }
}

Todas as lições de Arquitetura de Jogos no Unity

Pratique por conta própria: Playground de Unity