Menu
CoddyTech

Singleton tổng quát

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 43 trên 64.

Singleton ở phần 3 gồm năm dòng mà mọi manager đều lặp lại: một Instance tĩnh, một Awake để gán giá trị cho nó và phần kiểm tra đối tượng trùng lặp. Lớp cơ sở generic viết các dòng này một lần. Mỗi manager kế thừa từ lớp đó và khai báo tên kiểu của chính nó:

public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance { get; private set; }

    protected virtual void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this as T;
    }
}

public class ScoreManager : Singleton<ScoreManager>
{
    public int score;
    public void Add(int points) { score += points; }
}

public class SoundManager : Singleton<SoundManager>
{
    public void Play(string clip) { Debug.Log("Playing " + clip); }
}
// trong bất kỳ script nào
ScoreManager.Instance.Add(10);
SoundManager.Instance.Play("coin");

Một thành viên tĩnh của lớp generic thuộc về từng kiểu được tạo từ lớp đó: Singleton<ScoreManager> và Singleton<SoundManager> là hai kiểu, mỗi kiểu có một Instance riêng, vì vậy các manager không bao giờ dùng chung một Instance. ScoreManager.Instance đã có kiểu ScoreManager, đó là mục đích của T. Bên trong lớp cơ sở, this chỉ được biết đến với kiểu Singleton<T>, vì vậy this as T chuyển nó thành kiểu manager thực sự của nó.

Lớp cơ sở là abstract: Unity chỉ thêm các manager vào đối tượng. Manager nào cần có Awake riêng thì không được viết void Awake() thông thường. Cách viết đó che khuất phương thức của lớp cơ sở (trình biên dịch sẽ cảnh báo), Unity chỉ gọi phương thức của manager và Instance sẽ không bao giờ được gán. Hãy override phương thức đó và gọi phương thức của lớp cơ sở trước:

protected override void Awake()
{
    base.Awake();
    score = 0;
}
challenge icon

Thử thách

Trung bình

Tạo lớp cơ sở cho cả hai trình quản lý để chúng tồn tại khi tải cảnh.

  1. Trong PersistentSingleton, viết protected virtual void Awake(): đối tượng đầu tiên trở thành Instance và tồn tại (DontDestroyOnLoad); đối tượng xuất hiện sau sẽ tự hủy.
  2. Wallet và MusicPlayer kế thừa lớp này; xóa Instance của chúng và hàm Awake của Wallet.
  3. MusicPlayer ghi đè Awake: gọi base.Awake(), sau đó, nếu đây là Instance, ghi nhật ký Now playing: Adventure.

Nhấn Chơi, đến khu rừng rồi quay lại: 1 đồng xu. Sau đó nhấn Kiểm tra.

Tự làm thử

using UnityEngine;

public abstract class PersistentSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance { get; private set; }

    // Awake: giữ instance đầu tiên khi tải các cảnh, hủy mọi instance xuất hiện sau
}

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

Tự luyện tập: Playground Unity