我试着存储我正在播放的当前场景中的当前音乐,然后在下一个场景中将其更改为另一个。
这是我的剧本。
音频管理器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioManager : MonoBehaviour {
public static AudioManager Instance;
private MusicList musicList;
[SerializeField] private AudioSource _effectsSource, currentMusic;
private void Awake() {
if (Instance == null) {
Instance = this;
DontDestroyOnLoad(this);
}
else Destroy(this);
Instance = this;
musicList = GetComponentInChildren<MusicList>();
}
public void PlayMusic(MusicId id) {
AudioSource musicToPlay = musicList.GetMusicSource(id);
musicToPlay.Play();
currentMusic = musicToPlay;
}
public void ChangeMusic(MusicId newMusicId) {
currentMusic.Stop();
AudioSource musicToPlay = musicList.GetMusicSource(newMusicId);
musicToPlay.Play();
currentMusic = musicToPlay;
}
public void PlaySound(AudioClip clip) {
_effectsSource.PlayOneShot(clip, 0.1f);
}
}
MusicList是音频管理器的子级
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MusicList : MonoBehaviour
{
public Music[] musicList;
private void Awake()
{
foreach (Music music in musicList) {
music.source = gameObject.AddComponent<AudioSource>();
music.source.clip = music.clip;
music.source.volume = music.volume;
music.source.loop = true;
}
}
public AudioSource GetMusicSource(MusicId id) {
foreach (Music music in musicList) {
if (music.id == id) return music.source;
}
return null;
}
}
音乐
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Music
{
public MusicId id;
public AudioClip clip;
[HideInInspector]
public AudioSource source;
public float volume;
}
public enum MusicId {
Menu,
Gameplay
}
当我在第一个场景中调试时,当前曲目播放被存储,但当我更改场景并尝试访问currentMusic值时,它为空。
谢谢你的帮助!
1条答案
按热度按时间p8h8hvxi1#
这里至少有两种方法。一种是使用专有场景数据(不是一个技术术语),这意味着使用当前场景中的任何信息,而另一种方法是使用数据持久性(这是一个技术术语)在场景更改后将数据"保存"在(通常) transient 对象中。还有第三种方法可以持久化数据,但不推荐用于您的用例。
它们都有各自的用例,每个用例都有不同的目标,让我们来攻击它们:
1.场景信息
这是直截了当的:你有一个菜单,一个教程,2个关卡,一个boss战关卡,然后是"结束游戏+学分"。这意味着6首不同的歌曲(假设每个场景,包括结束学分,有一个不同的主题)。一旦你从菜单切换到关卡,从关卡切换到关卡,然后切换到"结束",一首新歌会弹出在音频管理器上。
这里最简单的方法是在每个场景中有一个GameObject(最好有相同的名称和相同的组件,只有歌曲不同)。让我们称之为"场景信息"。在这个GO上你可以附加任何必要的信息到你的水平,像一些描述的水平,目标,难度或歌曲。您可以使用
Find()
函数来定位此GO,然后访问您需要的组件(就像一个保存了Music
的组件,然后只需将其弹出到AudioSource
和Play()
上即可)。小提示:场景GO必须NOT设置为
DontDestroyOnLoad()
。因为你不能在不同的场景中"携带"它,因为每个场景都有自己的东西。当然,这应该不同于你的AudioManager
GO。1.数据持久性
这通常是用来"携带"玩家相关的东西,如球员图标,球员姓名,健康,库存等从一个较早的场景到这一个。你也可以"携带"的音乐,我猜,假设它是一个自定义播放列表或如果你想继续这首歌。否则,我不建议音乐,如果歌曲总是不同的水平。
这基本上就是您对
AudioManager
所做的。1.存储(不推荐用于您需要的用途)
还有第三个选项与存储有关,使用PlayerPrefs,但我认为这对于一首歌来说有点过头了。但是如果你觉得更容易的话,可以随意使用它。正如我所说,这完全取决于你的用例。
这个选项主要是指游戏的喜好,如音量,图形设置等,它基本上是"记忆"的游戏设置。
小警告:
我会将
currentMusic;
设置为public
。你的
Awake()
函数有一个奇怪的流。else Destroy(this);
缺少一个返回值。把Instance
设置为this
是不明智的,因为你正在销毁对象。试试这个:
你说:MusicList是AudioManager的子类。我想你是指 * component * 而不是 * children *,如果它所连接的gameObject叫做"AudioManager"的话,对吗?否则就不太有意义了。子类是别的东西。
PlayMusic()
中的代码与ChangeMusic()
相同(除了停止前一首歌曲以更新它),这意味着如果你想修改PlayMusic()
或ChangeMusic()
的代码,你总是需要做两次(在每个函数中),这样做更好:......可以进一步简化为:
而且现在没有临时变量了,这毫无理由地使代码复杂化了。
(of当然,假设id总是被保证在列表中,当然,它应该在列表中),这样你只有一个地方来修改你的代码或者修复潜在的bug。
虽然您可以"强制" C#对象序列化,但最好使用Unity对象(UnityEngine.object中的任何对象,但主要依赖于ScriptableObjects、Components和Preabs)。
音乐初始化的工作流程也很奇怪。不是在每个音乐 Package 器上初始化音频源,而是在列表中循环进行。这有点"倒退",但不一定是坏事。我只是不会这么做。