界面、面板与 GameManager
Assets/
Scenes/
Arena.unity
Scripts/
Audio/
你已经有的这一课新增
每个游戏里都有一个不属于世界本身的对象:它决定你在看什么,以及扬声器里出来什么。不过在写它之前,有两个词不能再用混了 —— 因为在 Unity 里它们是完全不同的两回事。
Screen 和 panel 不是同一个词
✓ Screen
SceneManager.LoadScene("Arena")
.unity 文件里。加载一个 screen 会把你所在场景里的一切都销毁,再从硬盘上把新场景搭起来。当你就是要一张白纸时,用的就是它。✓ Panel
pausePanel.SetActive(true)
地方不同、世界也不同 → screen。intro 不是竞技场,所以它有自己单独的 scene 文件。
同一个地方、只是多盖一层东西 → panel。暂停就发生在竞技场里,所以它就是竞技场里的一个 GameObject。
如果你发现要靠着 DontDestroyOnLoad 才能让某个东西在一次切换里活下来,那你其实想要的八成是 panel。
两个 screen,各自是一个 scene 文件。两个 panel,都住在第二个 screen 里面。底下的竞技场纹丝不动。
第 1 步 —— intro 这个 screen,一个独立的 scene
整门课里只有这里值得再建第二个 scene。intro 没有地面、没有角色、没有物理:眨眼就加载完了,而你一离开它,就没有什么值得留下的。
- 1
→ 选 Basic (URP),接着 → Assets/Scenes/Intro.unity。
- 2
添加 ,放一个写着游戏名的 Text,再来一个 ,标成 PLAY。
- 3
建一个空对象叫 Intro,挂上 IntroScreen.cs,然后把按钮的 On Click () 指到 LoadArena。
using UnityEngine;
using UnityEngine.SceneManagement;
public class IntroScreen : MonoBehaviour
{
public void LoadArena()
{
SceneManager.LoadScene("Arena");
}
}第 2 步 —— 一个整个竞技场都够得着的 manager
回到 Arena.unity。按钮要叫 manager 暂停,Knight 上的脚本要播放击中的音效。它们两个都不拥有这个 manager,而每次都用 FindObjectOfType 去找它,既慢又容易出错。一个 static 字段就搞定了。
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
建一个 。Unity 会在它旁边顺手加一个 EventSystem —— 别动它,按钮能被点全靠它。
- 2
在 Canvas 里面建两个空对象:HudPanel 和 PausePanel。它们各自只是一个容器。
- 3
HudPanel 放你游戏中想看到的东西 —— 一个写着 0 的 Text 就够了。PausePanel 放一张铺满屏幕的暗色 Image、PAUSED 这几个字、一个 RESUME 按钮和一个 MENU 按钮。
[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 负责装音乐,另一个什么都不放,需要时单发射一次音效。
[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
选中 GameManager,添加两个 component。第一个勾上 Loop,两个都取消勾选 Play On Awake。
- 2
在 Inspector 里把 HudPanel 和 PausePanel 拖进各自的槽,两个 Audio Source 拖到 Music Source 和 Sfx Source,再把音频文件拖进 Arena Music 和 Click Clip。
- 3
在 Start() 里紧跟 ShowPanel 之后加上
PlayMusic(arenaMusic);,这样一进竞技场音乐就已经在放了。 - 4
打开 Intro.unity 从那里按 Play,不要从竞技场按。玩家遇到的顺序就是这个顺序,这也是唯一能抓出 Build Settings 列表漏项的办法。
- ✓从 Intro.unity 播放,点 PLAY 就能带着音乐进入竞技场。
- ✓按 Escape 唤出 PausePanel,Knight 停下;再按一次 Escape 就继续。
- ✓切换 panel 的时候,地面、方块和三个角色一点都没挪动。
- ✓MENU 能回到 intro,回到 intro 时它没有卡死。