Menu
CoddyTech

Eventos de C# en Unity

Parte de la sección Arquitectura de juegos en Unity del Journey de Unity de Coddy. Lección 12 de 64.

Cuando cambia la puntuación, hay varias cosas que se interesan por ello: el texto de la puntuación, un sonido, un logro. Si ScoreKeeper llama a cada una, necesita un campo para cada una, y cada nueva característica implica editarlo. Un evento le da la vuelta a eso: ScoreKeeper anuncia el cambio y quien esté interesado se suscribe.

Dos palabras de C# hacen que esto funcione. Action<int> es el tipo de un método que recibe un int y no devuelve nada. Un event de ese tipo es una lista de esos métodos: otro código añade su método con +=, y el propietario los llama a todos con Invoke. En C# sencillo:

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);
    }
}

// en otra parte
Alarm alarm = new Alarm();
Sleeper sleeper = new Sleeper();
alarm.Rang += sleeper.WakeUp;
alarm.Ring(7);   // Levantado a las 7

En Unity, el publicador y sus oyentes son componentes, normalmente en distintos GameObjects:

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, en el texto del 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 oyente es un método que recibe un int, aquí la nueva puntuación. Invoke llama a todos los métodos suscritos, en el orden en que se suscribieron. Si no hay ningún oyente, el evento es null, así que una llamada directa a OnScoreChanged(score) provocaría una NullReferenceException, mientras que ?. omite la llamada. La palabra clave event limita los demás scripts a += y -=: en ScoreLabel, keeper.OnScoreChanged(5) o keeper.OnScoreChanged = null no compila.

ScoreKeeper no tiene ningún campo para la etiqueta y no sabe que existe. Un contador de combos o un reproductor de sonido se suscribe de la misma manera, y ScoreKeeper se queda tal cual; además, una escena de prueba sin HUD se ejecuta sin errores. Un oyente encuentra al publicador mediante una referencia del Inspector, como aquí, o de cualquier otra forma que conozcas, como GetComponent o un singleton. Quitar un oyente con -= es importante cuando se destruyen objetos, y ese es el tema de la próxima lección.

challenge icon

Desafío

Fácil

Haz que el luchador anuncie su combo.

  1. En Fighter.cs, añade using System; y public event Action<int> OnComboChanged;.
  2. Reemplaza las líneas que usan comboText y flame por OnComboChanged?.Invoke(combo); y elimina ambos campos.
  3. En el Start() de ComboText y ComboFlame, escribe fighter.OnComboChanged += Show; y += Grow;.

Pulsa «Jugar» y presiona Espacio unas cuantas veces: el combo aumenta y después se borra. Luego, pulsa «Comprobar».

Pruébalo tú mismo

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 las lecciones de Arquitectura de juegos en Unity

Practica por tu cuenta: Playground de Unity