U
章 01基础11 课 — 点击打开
10 / 11 · 16 分钟
基础

界面、面板与 GameManager

到目前为止的项目

Assets/

Scenes/

Arena.unity

GameManagerKnightMain CameraCanvasHudPanelPausePanel
Intro.unity

Scripts/

CharacterSwitcher.csIntroScreen.csGameManager.cs

Audio/

arena music (.ogg)click (.wav)

你已经有的这一课新增

每个游戏里都有一个不属于世界本身的对象:它决定你在看什么,以及扬声器里出来什么。不过在写它之前,有两个词不能再用混了 —— 因为在 Unity 里它们是完全不同的两回事。

Screen 和 panel 不是同一个词

✓ Screen

SceneManager.LoadScene("Arena")

整整一个 Unity 场景,各自在自己的 .unity 文件里。加载一个 screen 会把你所在场景里的一切都销毁,再从硬盘上把新场景搭起来。当你就是要一张白纸时,用的就是它。

✓ Panel

pausePanel.SetActive(true)

你就在其中的那个场景里的一个 GameObject。切换 panel 不会改动别的任何东西 —— 地面、方块和 Knight 都还在原地。凡是要盖在游戏上面的东西,都用它。
  • 地方不同、世界也不同 → screen。intro 不是竞技场,所以它有自己单独的 scene 文件。

  • 同一个地方、只是多盖一层东西 → panel。暂停就发生在竞技场里,所以它就是竞技场里的一个 GameObject。

  • 如果你发现要靠着 DontDestroyOnLoad 才能让某个东西在一次切换里活下来,那你其实想要的八成是 panel。

动态图解

两个 screen,各自是一个 scene 文件。两个 panel,都住在第二个 screen 里面。底下的竞技场纹丝不动。

第 1 步 —— intro 这个 screen,一个独立的 scene

整门课里只有这里值得再建第二个 scene。intro 没有地面、没有角色、没有物理:眨眼就加载完了,而你一离开它,就没有什么值得留下的。

  1. 1

    File▸New Scene → 选 Basic (URP),接着 File▸Save As → Assets/Scenes/Intro.unity。

  2. 2

    添加 GameObject▸UI▸Canvas,放一个写着游戏名的 Text,再来一个 UI▸Button,标成 PLAY。

  3. 3

    建一个空对象叫 Intro,挂上 IntroScreen.cs,然后把按钮的 On Click () 指到 LoadArena。

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

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

第 2 步 —— 一个整个竞技场都够得着的 manager

回到 Arena.unity。按钮要叫 manager 暂停,Knight 上的脚本要播放击中的音效。它们两个都不拥有这个 manager,而每次都用 FindObjectOfType 去找它,既慢又容易出错。一个 static 字段就搞定了。

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

// Panels, not screens: both of these live inside Arena.unity.
public enum GamePanel { Hud, Pause }

public class GameManager : MonoBehaviour
{
    // The whole arena reaches the manager through this one field.
    public static GameManager I;

    void Awake()
    {
        I = this;
    }
}

第 3 步 —— 竞技场里的两个 panel

  1. 1

    建一个 GameObject▸UI▸Canvas。Unity 会在它旁边顺手加一个 EventSystem —— 别动它,按钮能被点全靠它。

  2. 2

    在 Canvas 里面建两个空对象:HudPanel 和 PausePanel。它们各自只是一个容器。

  3. 3

    HudPanel 放你游戏中想看到的东西 —— 一个写着 0 的 Text 就够了。PausePanel 放一张铺满屏幕的暗色 Image、PAUSED 这几个字、一个 RESUME 按钮和一个 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);   // the arena starts playable
}

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

    // A pause panel that does not stop time is just a picture.
    Time.timeScale = next == GamePanel.Pause ? 0f : 1f;
}

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

// Leaving the arena is a screen change, so it goes through SceneManager.
public void BackToIntro()
{
    Time.timeScale = 1f;
    SceneManager.LoadScene("Intro");
}
  • 每个 panel 一行,由比较来决定。以后加第三个 panel,你只要加一行,而不是再写一长串 if。

  • SetActive(false) 会连那个 panel 的 Update 一起停掉,所以藏起来的 panel 每帧一点都不耗。

  • 把 RESUME 按钮指向 ShowPanel,MENU 按钮指向 BackToIntro。一个是切换 panel,另一个是加载 screen —— 同一个菜单里,两种完全不同的跳转。

第 4 步 —— 音乐会循环,点击声不会

同一个 GameManager 对象上放两个 AudioSource。一个勾上 Loop 负责装音乐,另一个什么都不放,需要时单发射一次音效。

GameManager.cs — the sound half
[Header("Sound")]
public AudioSource musicSource;   // tick Loop on this one
public AudioSource sfxSource;
public AudioClip arenaMusic;
public AudioClip clickClip;

public void PlayMusic(AudioClip track)
{
    // Asking for the track that is already playing must not restart it.
    if (musicSource.clip == track && musicSource.isPlaying) return;

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

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

声音到底从哪里出来

  • AudioListener 就是耳朵,一个 scene 里应该正好只有一个。第 06 课里 Unity 已经把它放在 Main Camera 上了 —— 留着别动,另外记住 Intro.unity 也有它自己那一个。

  • 音乐活不过一次 screen 切换:加载 Intro,竞技场的 AudioSource 就跟场景里其余东西一起被销毁了。这就是 screen 的代价,也是别随手建一个的原因。

  • 音频文件放在 Assets/Audio —— 第 05 课你建的那个文件夹。音乐用 .ogg,短音效用 .wav。

  • 两个 source 都要取消勾选 Play On Awake。决定声音什么时候开始的是 manager,不是 Inspector。

第 5 步 —— 把它们连起来

  1. 1

    选中 GameManager,添加两个 Audio Source component。第一个勾上 Loop,两个都取消勾选 Play On Awake。

  2. 2

    在 Inspector 里把 HudPanel 和 PausePanel 拖进各自的槽,两个 Audio Source 拖到 Music Source 和 Sfx Source,再把音频文件拖进 Arena Music 和 Click Clip。

  3. 3

    在 Start() 里紧跟 ShowPanel 之后加上 PlayMusic(arenaMusic);,这样一进竞技场音乐就已经在放了。

  4. 4

    打开 Intro.unity 从那里按 Play,不要从竞技场按。玩家遇到的顺序就是这个顺序,这也是唯一能抓出 Build Settings 列表漏项的办法。

  • ✓从 Intro.unity 播放,点 PLAY 就能带着音乐进入竞技场。
  • ✓按 Escape 唤出 PausePanel,Knight 停下;再按一次 Escape 就继续。
  • ✓切换 panel 的时候,地面、方块和三个角色一点都没挪动。
  • ✓MENU 能回到 intro,回到 intro 时它没有卡死。