C#-Events in Unity
Teil des Abschnitts Spielarchitektur in Unity der Unity-Journey von Coddy. Lektion 10 von 50.
Wenn sich der Punktestand ändert, betrifft das mehrere Dinge: den Punktestandstext, einen Sound, einen Erfolg. Wenn der ScoreKeeper jedes davon aufruft, braucht er für jedes ein Feld, und jede neue Funktion bedeutet, dass er bearbeitet werden muss. Ein Ereignis kehrt das um: Der ScoreKeeper kündigt die Änderung an, und alle, die daran interessiert sind, abonnieren es. Du kennst event und Action aus C#. In Unity sind der Herausgeber und seine Listener Komponenten, normalerweise auf verschiedenen 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, am Text des HUDs
public ScoreKeeper keeper;
public TMP_Text label;
void Start()
{
keeper.OnScoreChanged += ShowScore;
}
void ShowScore(int score)
{
label.text = "Score: " + score;
}Action<int> bedeutet, dass jeder Listener eine Methode ist, die einen int entgegennimmt, hier den neuen Punktestand. Invoke ruft jede abonnierte Methode auf, und zwar in der Reihenfolge, in der sie abonniert haben. Gibt es keinen Listener, ist das Ereignis null, sodass ein einfaches OnScoreChanged(score) eine NullReferenceException auslösen würde; ?. überspringt den Aufruf stattdessen. Das Schlüsselwort event beschränkt andere Skripte auf += und -=: In ScoreLabel lässt sich keeper.OnScoreChanged(5) oder keeper.OnScoreChanged = null nicht kompilieren.
Der ScoreKeeper hat kein Feld für das Label und weiß nicht, dass es existiert. Ein Combo-Zähler oder ein Sound-Player abonniert es auf dieselbe Weise, und der ScoreKeeper bleibt unverändert; eine Testszene ohne HUD läuft ohne Fehler. Ein Listener findet den Herausgeber über eine Inspector-Referenz wie hier oder auf eine andere bekannte Weise, etwa mit GetComponent oder einem Singleton. Einen Listener mit -= zu entfernen, ist wichtig, sobald Objekte zerstört werden; darum geht es in der nächsten Lektion.
Aufgabe
EinfachDie Combo des Kämpfers wächst mit jedem Treffer mit der Leertaste und fällt nach comboWindow (1) Sekunde ohne Treffer wieder auf 0 zurück. Momentan setzt Fighter.cs selbst den Combo-Text und die Größe der Flamme, daher braucht es für beides ein Feld. Ändere das mit einem Ereignis:
- Deklariere in
Fighter.csein öffentliches Ereignis namensOnComboChanged, das die Combo (eine int) übergibt. Löse es jedes Mal mit der neuen Combo aus, wenn sich die Combo ändert, auch beim Zurücksetzen auf 0, und stelle sicher, dass ein Treffer keinen Fehler verursacht, wenn niemand darauf hört. - Lösche die Felder
comboTextundflamedes Fighters sowie die Zeilen, die sie verwenden: Der Fighter weiß nichts über das HUD. - Abonniere in
ComboText.csundComboFlame.csdie MethodenShowundGrowinStart()für das Ereignis des Fighters. Beide haben einfighter-Feld, das in der Szene verknüpft ist.
Drücke Play, triff den Sandsack ein paar Mal mit der Leertaste, warte eine Sekunde und klicke dann auf Prüfen.
Probier es selbst
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;
}
}
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Spielarchitektur in Unity
1Komponenten statt Hierarchien
Kleine KomponentenEin Basisskript für GegnerAbstrakte KomponentenKomponenten im ZusammenspielRückblick: Modularer Geschützturm4Serialisierte Daten
Serialisierbare KlassenListen im InspectorEnums im InspectorRückblick: Loot-Tabelle7Zustandsautomaten
Zustände als KlassenEin ZustandsautomatÜbergängeGegner-KIWiederholung: Türzustände10Projekt: Arena-Überlebenskampf
Der HeldGegnertypen2Interfaces in Unity
IDamageableInterfaces und TryGetComponentGetComponentsRückblick: Interaktionstaste5ScriptableObjects
Daten-AssetsWaffenwerteGemeinsame EinstellungenWellen als DatenRückblick: Item-Datenbank8Wiederverwendbarer Code
ErweiterungsmethodenEin generisches SingletonGenerische HilfsmethodenRückblick: FindNearest3Ereignisse
C#-Events in UnityIn OnEnable abonnierenUnityEvent-FelderEvents steuern die UIRückblick: Achievement-Popup