Menu
CoddyTech

Zdarzenia C# w Unity

Część sekcji Architektura gier w Unity ścieżki Unity w Coddy. Lekcja 12 z 64.

Gdy wynik się zmienia, interesuje to kilka elementów: tekst wyniku, dźwięk, osiągnięcie. Jeśli ScoreKeeper wywołuje każdy z nich, potrzebuje pola dla każdego, a każda nowa funkcja oznacza konieczność edycji tej klasy. Zdarzenie odwraca tę sytuację: ScoreKeeper ogłasza zmianę, a zainteresowani subskrybują zdarzenie.

Dwa słowa C# sprawiają, że to działa. Action<int> to typ metody, która przyjmuje jeden int i niczego nie zwraca. event tego typu to lista takich metod: inny kod dodaje swoją metodę za pomocą +=, a właściciel wywołuje je wszystkie przez Invoke. W zwykłym C#:

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

// w innym miejscu
Alarm alarm = new Alarm();
Sleeper sleeper = new Sleeper();
alarm.Rang += sleeper.WakeUp;
alarm.Ring(7);   // Pobudka o 7

W Unity nadawca i jego odbiorcy to komponenty, zwykle umieszczone na różnych obiektach GameObject:

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, na tekście HUD
public ScoreKeeper keeper;
public TMP_Text label;

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

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

Action<int> oznacza, że każdy odbiorca to metoda przyjmująca jeden int — tutaj nowy wynik. Invoke wywołuje każdą subskrybowaną metodę w kolejności, w jakiej się zasubskrybowały. Gdy nie ma żadnego odbiorcy, zdarzenie ma wartość null, więc zwykłe OnScoreChanged(score) spowodowałoby wyjątek NullReferenceException, a ?. zamiast tego pomija wywołanie. Słowo kluczowe event ogranicza inne skrypty do używania += i -=: w ScoreLabel zapis keeper.OnScoreChanged(5) lub keeper.OnScoreChanged = null nie zostanie skompilowany.

ScoreKeeper nie ma pola dla etykiety i nie wie o jej istnieniu. Licznik kombinacji lub odtwarzacz dźwięku subskrybuje zdarzenie w ten sam sposób, a ScoreKeeper pozostaje bez zmian. Scena testowa bez HUD działa bez błędów. Odbiorca odnajduje nadawcę za pomocą referencji z Inspektora, jak w tym przykładzie, albo w dowolny znany Ci sposób, na przykład przez GetComponent lub singleton. Usunięcie odbiorcy za pomocą -= ma znaczenie, gdy obiekty są niszczone — to temat następnej lekcji.

challenge icon

Wyzwanie

Łatwy

Spraw, by wojownik ogłaszał swoją serię.

  1. W pliku Fighter.cs dodaj using System; i public event Action<int> OnComboChanged;.
  2. Zastąp linie używające comboText i flame kodem OnComboChanged?.Invoke(combo); i usuń oba pola.
  3. W metodzie Start() w klasach ComboText i ComboFlame wpisz fighter.OnComboChanged += Show; i += Grow;.

Naciśnij „Graj” i naciśnij kilka razy spację: seria rośnie, a potem się zeruje. Następnie naciśnij „Sprawdź”.

Spróbuj swoich sił

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

Wszystkie lekcje w sekcji Architektura gier w Unity

Poćwicz samodzielnie: Edytor online Unity