unity3d 尝试在场景之间切换音乐

hm2xizp9  于 2022-12-27  发布在  其他
关注(0)|答案(1)|浏览(378)

我试着存储我正在播放的当前场景中的当前音乐,然后在下一个场景中将其更改为另一个。
这是我的剧本。
音频管理器

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值时,它为空。
谢谢你的帮助!

p8h8hvxi

p8h8hvxi1#

这里至少有两种方法。一种是使用专有场景数据(不是一个技术术语),这意味着使用当前场景中的任何信息,而另一种方法是使用数据持久性(这是一个技术术语)在场景更改后将数据"保存"在(通常) transient 对象中。还有第三种方法可以持久化数据,但不推荐用于您的用例。
它们都有各自的用例,每个用例都有不同的目标,让我们来攻击它们:
1.场景信息
这是直截了当的:你有一个菜单,一个教程,2个关卡,一个boss战关卡,然后是"结束游戏+学分"。这意味着6首不同的歌曲(假设每个场景,包括结束学分,有一个不同的主题)。一旦你从菜单切换到关卡,从关卡切换到关卡,然后切换到"结束",一首新歌会弹出在音频管理器上。
这里最简单的方法是在每个场景中有一个GameObject(最好有相同的名称和相同的组件,只有歌曲不同)。让我们称之为"场景信息"。在这个GO上你可以附加任何必要的信息到你的水平,像一些描述的水平,目标,难度或歌曲。您可以使用Find()函数来定位此GO,然后访问您需要的组件(就像一个保存了Music的组件,然后只需将其弹出到AudioSourcePlay()上即可)。
小提示:场景GO必须NOT设置为DontDestroyOnLoad()。因为你不能在不同的场景中"携带"它,因为每个场景都有自己的东西。当然,这应该不同于你的AudioManager GO。

    • 这就是你想要的方法**

1.数据持久性
这通常是用来"携带"玩家相关的东西,如球员图标,球员姓名,健康,库存等从一个较早的场景到这一个。你也可以"携带"的音乐,我猜,假设它是一个自定义播放列表或如果你想继续这首歌。否则,我不建议音乐,如果歌曲总是不同的水平。
这基本上就是您对AudioManager所做的。
1.存储(不推荐用于您需要的用途)
还有第三个选项与存储有关,使用PlayerPrefs,但我认为这对于一首歌来说有点过头了。但是如果你觉得更容易的话,可以随意使用它。正如我所说,这完全取决于你的用例。
这个选项主要是指游戏的喜好,如音量,图形设置等,它基本上是"记忆"的游戏设置。
小警告:
我会将currentMusic;设置为public
你的Awake()函数有一个奇怪的流。else Destroy(this);缺少一个返回值。把Instance设置为this是不明智的,因为你正在销毁对象。
试试这个:

private void Awake()
{
    if (Instance == null)
    {
        DontDestroyOnLoad(this);
        Instance = this;
    }
    else if (Instance != this)
    {
        Destroy(this);
        return;
    }

    if (musicList == null)
        musicList = GetComponentInChildren<MusicList>();
}

你说:MusicList是AudioManager的子类。我想你是指 * component * 而不是 * children *,如果它所连接的gameObject叫做"AudioManager"的话,对吗?否则就不太有意义了。子类是别的东西。
PlayMusic()中的代码与ChangeMusic()相同(除了停止前一首歌曲以更新它),这意味着如果你想修改PlayMusic()ChangeMusic()的代码,你总是需要做两次(在每个函数中),这样做更好:

public void PlayMusic(MusicId id) {
    AudioSource musicToPlay = musicList.GetMusicSource(id);
    musicToPlay.Play();
    currentMusic = musicToPlay;
}
public void ChangeMusic(MusicId newMusicId) {
    currentMusic.Stop();
    PlayMusic(newMusicId);
}

......可以进一步简化为:

public void PlayMusic(MusicId id) {
    currentMusic = musicList.GetMusicSource(id);
    currentMusic.Play();
}

public void ChangeMusic(MusicId newMusicId) {
    currentMusic.Stop();
    PlayMusic(newMusicId);
}

而且现在没有临时变量了,这毫无理由地使代码复杂化了。
(of当然,假设id总是被保证在列表中,当然,它应该在列表中),这样你只有一个地方来修改你的代码或者修复潜在的bug。
虽然您可以"强制" C#对象序列化,但最好使用Unity对象(UnityEngine.object中的任何对象,但主要依赖于ScriptableObjects、Components和Preabs)。
音乐初始化的工作流程也很奇怪。不是在每个音乐 Package 器上初始化音频源,而是在列表中循环进行。这有点"倒退",但不一定是坏事。我只是不会这么做。

相关问题