Menu
CoddyTech

Eventos de C# no Unity

Parte da seção Arquitetura de jogos no Unity do Journey de Unity da Coddy. Lição 10 de 50.

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 significa editá-lo. Um evento inverte isso: o ScoreKeeper anuncia a mudança, e quem se importa se inscreve. Você conhece event e Action do C#. 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, e ?. ignora a chamada. A palavra-chave event limita os 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 nem sabe que ele existe. Um contador de combo ou um reprodutor de som se inscreve da mesma forma, e o ScoreKeeper continua como está; 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

O combo do lutador aumenta a cada golpe com a tecla Space e volta a 0 após comboWindow (1) segundo sem um golpe. No momento, Fighter.cs define o texto do combo e o tamanho da chama por conta própria, então precisa de um campo para cada um. Inverta essa relação com um evento:

  • Em Fighter.cs, declare um evento público chamado OnComboChanged que passe o combo (um int). Dispare o evento com o novo combo sempre que ele mudar, incluindo quando for redefinido para 0, e garanta que um golpe não cause erro quando ninguém estiver ouvindo.
  • Exclua os campos comboText e flame de Fighter e as linhas que os usam: o Fighter não sabe nada sobre o HUD.
  • Em ComboText.cs e ComboFlame.cs, inscreva os métodos Show e Grow no evento do lutador em Start(). Ambos têm um campo fighter, vinculado na cena.

Pressione Play, acerte o saco de pancadas algumas vezes com Space, espere um segundo e depois Check.

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;
            }
        }
    }
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

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