Menu
CoddyTech

Sự kiện C# trong Unity

Thuộc phần Kiến trúc trò chơi trong Unity trong hành trình Unity của Coddy. Bài học 12 trên 64.

Khi điểm số thay đổi, có một số thành phần cần biết: dòng chữ điểm số, âm thanh, thành tích. Nếu ScoreKeeper gọi từng thành phần, nó cần một trường cho mỗi thành phần, và mỗi tính năng mới lại buộc bạn phải chỉnh sửa nó. Sự kiện đảo ngược cách làm đó: ScoreKeeper thông báo về thay đổi, còn những thành phần cần biết sẽ đăng ký nhận thông báo.

Có hai từ trong C# giúp làm việc này. Action<int> là kiểu của một phương thức nhận một int và không trả về gì. Một event có kiểu đó là danh sách các phương thức như vậy: code khác thêm phương thức của mình bằng +=, còn đối tượng sở hữu sự kiện gọi tất cả các phương thức đó bằng Invoke. Trong C# thông thường:

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

// Ở chỗ khác
Alarm alarm = new Alarm();
Sleeper sleeper = new Sleeper();
alarm.Rang += sleeper.WakeUp;
alarm.Ring(7);   // Dậy lúc 7 giờ

Trong Unity, bên phát sự kiện và các bên lắng nghe là những component, thường nằm trên các GameObject khác nhau:

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, trên phần chữ của HUD
public ScoreKeeper keeper;
public TMP_Text label;

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

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

Action<int> nghĩa là mỗi bên lắng nghe là một phương thức nhận một int, trong trường hợp này là điểm số mới. Invoke gọi từng phương thức đã đăng ký, theo thứ tự đăng ký. Khi không có bên lắng nghe nào, sự kiện có giá trị null, vì vậy lời gọi thông thường như OnScoreChanged(score) sẽ gây ra NullReferenceException, còn ?. sẽ bỏ qua lời gọi đó. Từ khóa event chỉ cho phép các script khác dùng += và -=: trong ScoreLabel, keeper.OnScoreChanged(5) hoặc keeper.OnScoreChanged = null sẽ không biên dịch được.

ScoreKeeper không có trường dành cho dòng chữ điểm số và cũng không biết dòng chữ đó tồn tại. Bộ đếm combo hoặc trình phát âm thanh có thể đăng ký theo cách tương tự, còn ScoreKeeper vẫn giữ nguyên, và cảnh kiểm thử không có HUD vẫn chạy mà không gặp lỗi. Bên lắng nghe tìm bên phát sự kiện thông qua tham chiếu trong Inspector như ở đây, hoặc bằng bất kỳ cách nào bạn biết, chẳng hạn như GetComponent hoặc singleton. Việc xóa một bên lắng nghe bằng -= rất quan trọng khi các đối tượng bị hủy, nội dung này sẽ có trong bài học tiếp theo.

challenge icon

Thử thách

Dễ

Cho đấu sĩ thông báo chuỗi combo của nó.

  1. Trong Fighter.cs, thêm using System; và public event Action<int> OnComboChanged;.
  2. Thay các dòng dùng comboText và flame bằng OnComboChanged?.Invoke(combo);, rồi xóa cả hai trường.
  3. Trong Start() của ComboText và ComboFlame, viết fighter.OnComboChanged += Show; và += Grow;.

Nhấn Chơi rồi nhấn Space vài lần: combo tăng lên, sau đó được xóa. Tiếp theo, nhấn Kiểm tra.

Tự làm thử

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

Tất cả bài học trong Kiến trúc trò chơi trong Unity

Tự luyện tập: Playground Unity