U
tab 01Căn bản11 bài — chạm để mở
10 / 11 · 16 phút
Căn bản

Screen, panel và GameManager

dự án của bạn tới lúc này

Assets/

Scenes/

Arena.unity

GameManagerKnightMain CameraCanvasHudPanelPausePanel
Intro.unity

Scripts/

CharacterSwitcher.csIntroScreen.csGameManager.cs

Audio/

arena music (.ogg)click (.wav)

đã có sẵnbài này thêm

Game nào cũng có một object không thuộc về thế giới trong game: nó quyết định bạn đang nhìn cái gì và loa đang phát cái gì. Nhưng trước khi viết nó, có hai từ phải thôi dùng lẫn lộn — vì trong Unity chúng là hai thứ hoàn toàn khác nhau.

Screen và panel không phải là một

✓ Screen

SceneManager.LoadScene("Arena")

Là nguyên một scene của Unity, nằm trong file .unity riêng. Nạp một screen là huỷ sạch mọi thứ trong scene cũ rồi dựng scene mới từ ổ cứng. Dùng khi bạn đúng là muốn làm lại từ trang giấy trắng.

✓ Panel

pausePanel.SetActive(true)

Là một GameObject nằm trong chính scene bạn đang ở. Bật tắt panel không đổi gì khác — sàn, khối và Knight vẫn y nguyên chỗ cũ. Dùng cho mọi thứ nằm đè lên trên game.
  • Khác chỗ, khác thế giới → screen. Màn intro không phải sân đấu, nên nó được một file scene riêng.

  • Cùng chỗ, chỉ thêm thứ đè lên → panel. Tạm dừng diễn ra ngay trong sân đấu, nên nó là GameObject trong sân đấu.

  • Nếu thấy mình phải dùng DontDestroyOnLoad để giữ cái gì đó sống qua một lần chuyển, gần như chắc chắn là bạn đang cần một panel.

sơ đồ động

Hai screen, mỗi cái là một file scene. Hai panel, cả hai nằm bên trong screen thứ hai. Sân đấu bên dưới không hề nhúc nhích.

Bước 1 — màn intro, một scene riêng

Đây là chỗ duy nhất trong cả khoá mà một scene thứ hai thật sự đáng tạo. Màn intro không có sàn, không nhân vật, không vật lý: nó nạp trong chớp mắt, và lúc bạn rời nó đi thì chẳng có gì đáng giữ lại.

  1. 1

    File▸New Scene → Basic (URP), rồi File▸Save As → Assets/Scenes/Intro.unity.

  2. 2

    Thêm GameObject▸UI▸Canvas, một Text làm tên game, và một UI▸Button ghi PLAY.

  3. 3

    Tạo object rỗng tên Intro, gắn IntroScreen.cs vào, rồi trỏ On Click () của nút tới LoadArena.

IntroScreen.cs — the entire intro screen
using UnityEngine;
using UnityEngine.SceneManagement;

public class IntroScreen : MonoBehaviour
{
    public void LoadArena()
    {
        SceneManager.LoadScene("Arena");
    }
}

Bước 2 — một manager mà cả sân đấu gọi được

Quay lại Arena.unity. Một cái nút cần bảo manager tạm dừng. Script trên Knight cần phát tiếng đánh trúng. Cả hai đều không sở hữu manager, mà mỗi lần cần lại đi tìm bằng FindObjectOfType thì vừa chậm vừa dễ sai. Một trường static là giải quyết xong.

GameManager.cs — attach to the GameManager object you made last lesson
using UnityEngine;
using UnityEngine.SceneManagement;

// Panel chứ không phải screen: cả hai đều nằm trong Arena.unity.
public enum GamePanel { Hud, Pause }

public class GameManager : MonoBehaviour
{
    // Cả sân đấu truy cập manager qua đúng trường này.
    public static GameManager I;

    void Awake()
    {
        I = this;
    }
}

Bước 3 — hai panel bên trong sân đấu

  1. 1

    GameObject▸UI▸Canvas. Unity tự thêm một EventSystem bên cạnh — để yên nó, đó là thứ làm cho nút bấm được.

  2. 2

    Trong Canvas tạo hai object rỗng: HudPanel và PausePanel. Mỗi cái chỉ là một cái hộp chứa.

  3. 3

    HudPanel chứa thứ bạn muốn thấy lúc đang chơi — một Text ghi 0 là đủ. PausePanel chứa một Image tối phủ kín màn hình, chữ PAUSED, một nút RESUME và một nút MENU.

GameManager.cs — add these to the class
[Header("Panels")]
public GameObject hudPanel;
public GameObject pausePanel;

public GamePanel current { get; private set; }

void Start()
{
    ShowPanel(GamePanel.Hud);   // sân đấu mở ra là chơi được ngay
}

public void ShowPanel(GamePanel next)
{
    current = next;
    hudPanel.SetActive(next == GamePanel.Hud);
    pausePanel.SetActive(next == GamePanel.Pause);

    // Panel tạm dừng mà không dừng thời gian thì chỉ là một bức tranh.
    Time.timeScale = next == GamePanel.Pause ? 0f : 1f;
}

void Update()
{
    if (Input.GetKeyDown(KeyCode.Escape))
        ShowPanel(current == GamePanel.Pause ? GamePanel.Hud : GamePanel.Pause);
}

// Rời sân đấu là đổi screen, nên phải đi qua SceneManager.
public void BackToIntro()
{
    Time.timeScale = 1f;
    SceneManager.LoadScene("Intro");
}
  • Mỗi panel một dòng, phép so sánh tự quyết định. Sau này thêm panel thứ ba thì thêm một dòng, không phải viết thêm chuỗi if.

  • SetActive(false) dừng luôn Update của panel đó, nên panel đang ẩn không tốn gì mỗi khung hình.

  • Trỏ nút RESUME tới ShowPanel, nút MENU tới BackToIntro. Một cái là đổi panel, cái kia là nạp screen — cùng một menu, hai kiểu nhảy khác hẳn nhau.

Bước 4 — nhạc thì lặp, tiếng bấm thì không

Hai AudioSource trên cùng object GameManager. Một cái bật Loop và giữ nhạc nền. Cái còn lại không giữ gì cả, cần tiếng nào thì phát tiếng đó một lần.

GameManager.cs — the sound half
[Header("Sound")]
public AudioSource musicSource;   //để trống ô Clip
public AudioSource sfxSource;
public AudioClip arenaMusic;
public AudioClip clickClip;

public void PlayMusic(AudioClip track)
{
    // Yêu cầu phát đúng bài đang chạy thì không được bắt đầu lại từ đầu.
    if (musicSource.clip == track && musicSource.isPlaying) return;

    musicSource.clip = track;
    musicSource.Play();
}

public void PlaySfx(AudioClip clip)
{
    if (clip != null) sfxSource.PlayOneShot(clip);
}

Âm thanh thực sự phát ra từ đâu

  • AudioListener là cái tai, và mỗi scene chỉ được có đúng một cái. Unity đã đặt nó trên Main Camera từ bài 06 — cứ để yên đó, và nhớ là Intro.unity có cái của riêng nó.

  • Nhạc không sống sót qua một lần đổi screen: nạp Intro là AudioSource của sân đấu bị huỷ cùng cả scene. Đó là cái giá của screen, và cũng là lý do đừng tạo screen một cách dễ dãi.

  • File âm thanh nằm trong Assets/Audio — thư mục bạn đã tạo ở bài 05. Dùng .ogg cho nhạc, .wav cho tiếng ngắn.

  • Bỏ tick Play On Awake ở cả hai source. Manager quyết định khi nào có tiếng, không phải Inspector.

Bước 5 — kết nối mọi thứ

  1. 1

    Chọn GameManager rồi thêm hai component Audio Source. Tick Loop ở cái đầu tiên, bỏ tick Play On Awake ở cả hai.

  2. 2

    Trong Inspector kéo HudPanel và PausePanel vào ô của chúng, hai Audio Source vào Music Source và Sfx Source, rồi kéo file âm thanh vào Arena Music và Click Clip.

  3. 3

    Thêm PlayMusic(arenaMusic); vào Start(), ngay sau ShowPanel, để sân đấu hiện ra là nhạc đã chạy sẵn.

  4. 4

    Mở Intro.unity rồi bấm Play từ đó, đừng bấm từ sân đấu. Đó là thứ tự người chơi gặp chúng, và cũng là cách duy nhất phát hiện danh sách Build Settings bị thiếu.

  • ✓Bấm Play từ Intro.unity, nhấn PLAY là vào sân đấu, có nhạc.
  • ✓Bấm Escape thì PausePanel hiện ra và Knight đứng im; bấm Escape lần nữa thì chạy tiếp.
  • ✓Sàn, khối và ba nhân vật không hề nhúc nhích trong lúc đổi panel.
  • ✓Nút MENU đưa bạn về intro, và về tới nơi thì intro không bị đứng hình.