U
10 / 22 · 10 phút
Hỏi đáp

Chuyển scene: LoadScene, màn hình chờ, và thứ gì sống sót

câu trả lời ngắn

Làm sao chuyển scene trong Unity?

Thêm mọi scene vào Scene List trong File > Build Profiles, rồi gọi SceneManager.LoadScene kèm tên scene. Việc đổi diễn ra ở khung hình kế tiếp, mọi thứ trong scene cũ bị huỷ, và chỉ object gốc có DontDestroyOnLoad sống sót — cùng với biến static và Time.timeScale, hai thứ không ai reset hộ bạn.

Nạp một scene khác chỉ tốn một dòng. Mọi rắc rối đều nằm quanh dòng đó: scene chưa từng được thêm vào bản build, đoạn code phía sau vẫn chạy tiếp, hoặc thứ bạn đang cần đã bị vứt đi cùng scene cũ.

Bước 1 — đưa scene vào danh sách

  1. 1

    Mở File▸Build Profiles (trước Unity 6 gọi là File▸Build Settings) rồi tìm mục Scene List.

  2. 2

    Mở từng scene bạn muốn có trong game rồi bấm Add Open Scenes, hoặc kéo thẳng các file .unity từ bảng Project vào.

  3. 3

    Scene nằm ở index 0 là scene mà bản build khởi động vào. Hãy để menu, hoặc màn intro, đứng đầu.

Bước 2 — nạp nó

Doorway.cs — the whole of scene switching
using UnityEngine;
using UnityEngine.SceneManagement;   // thiếu dòng này thì không tìm thấy SceneManager

public class Doorway : MonoBehaviour
{
    public void Play()
    {
        SceneManager.LoadScene("Arena");        // theo tên, đúng như ghi trong Scene List
    }

    public void Restart()
    {
        // Scene đang chạy, tên gì cũng được.
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }
}

Thứ gì sống sót qua lần nạp, thứ gì không

  • Mọi GameObject trong scene cũ đều bị huỷ. Tham chiếu tới chúng thành null, và coroutine đang chạy trên chúng dừng ngay tại chỗ.

  • Object đã gọi DontDestroyOnLoad thì đi tiếp — nhưng chỉ khi nó là object gốc, không có cha. Nhạc nền, dữ liệu save đang giữ trong bộ nhớ, và một manager là những thứ hay được giữ lại.

  • Biến static không thuộc scene nào, nên nó giữ nguyên giá trị. Tiện khi cần mang số màn chơi sang scene mới, và là cái bẫy khi giá trị thừa từ lần chơi trước vẫn còn nằm đó.

  • Time.timeScale là biến toàn cục. Tạm dừng game bằng timeScale = 0, rồi nạp menu mà quên trả về 1, thì menu mở ra trong trạng thái đứng hình — mà chẳng có lỗi nào giải thích vì sao.

Màn hình chờ, cho scene nạp hơi lâu

Loader.cs — a progress bar that actually means something
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class Loader : MonoBehaviour
{
    public Slider bar;

    public void Load(string scene) => StartCoroutine(LoadRoutine(scene));

    IEnumerator LoadRoutine(string scene)
    {
        AsyncOperation op = SceneManager.LoadSceneAsync(scene);

        while (!op.isDone)
        {
            // progress dừng ở 0.9 trong lúc scene chờ tới lượt hiện ra,
            // nên phải chia lại, không thì thanh tiến trình chẳng bao giờ đầy.
            bar.value = Mathf.Clamp01(op.progress / 0.9f);
            yield return null;
        }
    }
}

Hai scene cùng lúc

LoadSceneMode.Additive đặt một scene chồng lên scene đang chạy thay vì thay thế nó: một thanh HUD không phải nạp lại, hay một màn chơi được nạp thêm bên cạnh khu trung tâm. Gỡ ra bằng UnloadSceneAsync, và nhớ rằng object mới sinh ra sẽ nằm trong scene nào đang là scene active.

bài học làm ra thứ này

Screen, panel và GameManager

Bài này là một công thức đứng riêng. Trong khoá học, cùng thứ đó được dựng như một phần của dự án xuyên suốt năm tab — Căn bản, bài 10.