Unity3D:Resource.Load 定义的 AudioClip 最终为 Null 进入 AudioSource
Unity3D: Resource.Load defined AudioClip ends up Null going into the AudioSource
我想做的是在一个文件夹(在资源下)中包含一个音频文件,我可以将任何符合条件的音频文件放到指定的文件夹中,并让我的程序中的许多触发器从那个点读取(这就是为什么我下面的 AudioClip 是 public 静态的,所以我可以引用它)。目前,相同的音频文件在整个程序中都有效,但要更改文件需要在检查器中手动重新定义,我的最终客户将无法访问该文件,而且由于存在大量参考点而很乏味。
这是我目前的情况:
public static AudioClip BGM;
public AudioSource BGMSource;
private string formatted1;
void Start()
{
foreach(string file in System.IO.Directory.GetFiles(Application.dataPath+"/Resources/Audio/BGM"))
{
if(file.EndsWith(System.IO.Patch.GetExtension(".mp3")))
{
formatted1 = file.Replace(".mp3",string.Empty);
BGM = Resources.Load<AudioClip>(formatted1);
//BGM = (AudioClip)Resources.Load(formatted1,typeof(AudioClip)); <--same result with this
Debug.Log("found: "+formatted1);
}
}
if(BGM == null)
{
Debug.Log("Yeah, its null");
}
BGMSource.PlayOneShot(BGM, .9f);
if(BGMSource.isPlaying != true)
{
Debug.Log("I'm not playing");
}
}
照原样,这就是无法播放,没有错误消息。原来 BGM 是空的。 Debug 是这样说的,但是如果我要为 BGMSource.clip.name 添加一个 Debug 调用,它将完全出错并在该 Debug.
上出现 NullReferenceException
formatted1 字符串(文件路径和名称)的调试,它确实提供了正确的文件,名为 Test.mp3 ("C:/...Resources/Audio/BGM\Test"),格式不带“.mp3”,这是从另一个站点推荐的。我确实尝试使用 .mp3 扩展名,但似乎没关系,仍然没有播放。我还尝试使用 .wav 文件和 .ogg 文件,结果相同(注意:如果我手动附加为 public AudioClip,则所有文件都很好,因为上面写的 AudioSource 也会在这种情况下播放,但正如我带头,我们不希望这种情况)。是的,所有测试音频文件都在目录 /Resources/Audio/BGM 中。
另一个网站说了一些关于添加到文件顶部的内容 [RequireComponent(typeof(AudioClip))] 或 [RequireComponent(typeof(AudioSource))] 但那没有任何作用。
最后,该程序最终将提供给无法访问源代码的组,因此他们必须能够通过删除 Resources/Audio/BGM 中的任何 .mp3 来交换音频文件以进行自动播放。
欢迎任何帮助,谢谢!
首先要注意:永远不要使用+ "/"
作为系统文件路径!而是起诉 Path.Combine
,它会根据 运行 在
上的平台自动插入正确的路径分隔符
string file in System.IO.Directory.GetFiles(Path.Combine(Application.dataPath, "Resources", "Audio", "BGM"))
那么请阅读Resources.Load
的文档!
它要求将您的文件放在名为 Resources
的文件夹中,该文件夹已编译到应用程序构建中,因此之后无法更改。看来你是这样的。
它不采用完整系统路径,就像你在Directory.GetFiles
returns
An array of the full names (including paths) for the files in the specified directory
而是希望在所有 Resources
个文件夹中有一个路径 - 是的,您可以有多个。
比方说你把你的文件放在像
这样的结构中
Assets
|--Resources
| |--someFile.mp3
|
|--SomeOtherFolder
| |--Resources
| | |--someOtherFile.mp3
| | |--ASubFolder
| | | |--yetAnotherFile.mp3
然后您可以使用
来解决这些问题
Resources.Load<AudioClip>("someFile");
Resources.Load<AudioClip>("someOtherFile");
Resources.Load<AudioClip>("ASubfolder/yetAnotherFile");
自从构建时所有 Resources
都打包在一起。
所以你的情况应该是
Resources.Load<AudioClip>("Audio/BGM/" + formatted1);
您必须确保 formatted1
不是完整路径,而只是文件名!你可以简单地使用 Path.GetFileNameWithoutExtension
所以你甚至不需要你的 replace
var formatted1 = Path.GetFileNameWithoutExtension(file);
不好^^
在他们Best Practices for the Resources
folderUnity自己推荐
Don't use it!
然而
因为根本不推荐使用 Resources
我宁愿推荐:
如果您以后不想更改它们
您之后无法更改 Resources
,例如替换文件。因此,如果以后无论如何都无法更改文件,那么为什么不直接在以后需要它们的地方引用它们呢?
只需将您的音频文件放在 而非 Resources
的文件夹中,然后在您需要的脚本中直接引用它们:
// Simply drag&drop the clip into this field via the Inspector in Unity
[SerializeField] private AudioClip someClip;
如果您想稍后更改它们
如果您真的希望在构建之后也能够替换它们,您可以改用 UnityWebRequestMultimedia.GetAudioClip
which can also be used to load files from a system file on runtime. For this you wouldn't put your files into the Resources
or any other folder but rather either the StreamingAssets
or the Application.persistentDataPath
.
我经常去:
- 在编辑器中使用
StreamingAssets
文件夹,这样所有的东西都在项目中并通过 Application.streamingAssetsPath
访问它
- 在构建中首先检查文件是否存在于
Application.persistentDataPath
- 如果不从
Application.streamingAssetsPath
复制并存入Application.persistentDataPath
- 否则只需从
Application.persistentDataPath
加载它
已修改 API 示例
[RequireComponent(typeof(AudioSource))]
public class AudioExample : MonoBehaviour
{
[SerializeField] private AudioSource _audioSource;
public List<AudioClip> LoadedAudioClips = new List<AudioClip>;
private List<UnityWebRequest> _runningWebRequests = new List<UnityWebRequest>();
private void Awake()
{
if(!_audioSource) _audioSource = GetComponent<AudioSource>();
}
private void Start()
{
StartCoroutine(GetAudioClip());
}
private IEnumerator GetAudioClip()
{
foreach(string file in System.IO.Directory.GetFiles(Path.Combine(Application.persistentDataPath, "Audio", "BGM"))
{
if(!file.EndsWith(System.IO.Patch.GetExtension(".mp3"))) continue;
UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file:///" + file, AudioType.MPEG);
{
_runningWebRequests.Add(www);
www.Send();
}
}
while(_runningWebRequests.Count > 0)
{
foreach(var www in _runningWebRequests.Where(www => www.isDone))
{
_runningWebRequests.Remove(www);
if (www.isError)
{
Debug.LogWarning(www.error);
}
else
{
var clip = DownloadHandlerAudioClip.GetContent(www);
if(clip == null)
{
Debug.LogError("Huh?!");
}
else
{
LoadedAudioClips.Add(clip);
}
}
}
yield return null;
}
}
}
也看到你这么评论了:
StreamingAssets
也是一个特殊的文件夹,你可以把你想在运行时读入的文件存放在里面。它是本地。此外,此文件夹不是来自外部的 "visible",以后无法更改。
如果您以后需要更改内容(例如还保存文件),您将始终需要使用 Application.persistentDataPath
。
我想做的是在一个文件夹(在资源下)中包含一个音频文件,我可以将任何符合条件的音频文件放到指定的文件夹中,并让我的程序中的许多触发器从那个点读取(这就是为什么我下面的 AudioClip 是 public 静态的,所以我可以引用它)。目前,相同的音频文件在整个程序中都有效,但要更改文件需要在检查器中手动重新定义,我的最终客户将无法访问该文件,而且由于存在大量参考点而很乏味。
这是我目前的情况:
public static AudioClip BGM;
public AudioSource BGMSource;
private string formatted1;
void Start()
{
foreach(string file in System.IO.Directory.GetFiles(Application.dataPath+"/Resources/Audio/BGM"))
{
if(file.EndsWith(System.IO.Patch.GetExtension(".mp3")))
{
formatted1 = file.Replace(".mp3",string.Empty);
BGM = Resources.Load<AudioClip>(formatted1);
//BGM = (AudioClip)Resources.Load(formatted1,typeof(AudioClip)); <--same result with this
Debug.Log("found: "+formatted1);
}
}
if(BGM == null)
{
Debug.Log("Yeah, its null");
}
BGMSource.PlayOneShot(BGM, .9f);
if(BGMSource.isPlaying != true)
{
Debug.Log("I'm not playing");
}
}
照原样,这就是无法播放,没有错误消息。原来 BGM 是空的。 Debug 是这样说的,但是如果我要为 BGMSource.clip.name 添加一个 Debug 调用,它将完全出错并在该 Debug.
上出现 NullReferenceExceptionformatted1 字符串(文件路径和名称)的调试,它确实提供了正确的文件,名为 Test.mp3 ("C:/...Resources/Audio/BGM\Test"),格式不带“.mp3”,这是从另一个站点推荐的。我确实尝试使用 .mp3 扩展名,但似乎没关系,仍然没有播放。我还尝试使用 .wav 文件和 .ogg 文件,结果相同(注意:如果我手动附加为 public AudioClip,则所有文件都很好,因为上面写的 AudioSource 也会在这种情况下播放,但正如我带头,我们不希望这种情况)。是的,所有测试音频文件都在目录 /Resources/Audio/BGM 中。
另一个网站说了一些关于添加到文件顶部的内容 [RequireComponent(typeof(AudioClip))] 或 [RequireComponent(typeof(AudioSource))] 但那没有任何作用。
最后,该程序最终将提供给无法访问源代码的组,因此他们必须能够通过删除 Resources/Audio/BGM 中的任何 .mp3 来交换音频文件以进行自动播放。
欢迎任何帮助,谢谢!
首先要注意:永远不要使用+ "/"
作为系统文件路径!而是起诉 Path.Combine
,它会根据 运行 在
string file in System.IO.Directory.GetFiles(Path.Combine(Application.dataPath, "Resources", "Audio", "BGM"))
那么请阅读Resources.Load
的文档!
它要求将您的文件放在名为
Resources
的文件夹中,该文件夹已编译到应用程序构建中,因此之后无法更改。看来你是这样的。它不采用完整系统路径,就像你在
Directory.GetFiles
returnsAn array of the full names (including paths) for the files in the specified directory
而是希望在所有
Resources
个文件夹中有一个路径 - 是的,您可以有多个。比方说你把你的文件放在像
这样的结构中Assets |--Resources | |--someFile.mp3 | |--SomeOtherFolder | |--Resources | | |--someOtherFile.mp3 | | |--ASubFolder | | | |--yetAnotherFile.mp3
然后您可以使用
来解决这些问题Resources.Load<AudioClip>("someFile"); Resources.Load<AudioClip>("someOtherFile"); Resources.Load<AudioClip>("ASubfolder/yetAnotherFile");
自从构建时所有
Resources
都打包在一起。所以你的情况应该是
Resources.Load<AudioClip>("Audio/BGM/" + formatted1);
您必须确保
formatted1
不是完整路径,而只是文件名!你可以简单地使用Path.GetFileNameWithoutExtension
所以你甚至不需要你的 replacevar formatted1 = Path.GetFileNameWithoutExtension(file);
不好^^
在他们Best Practices for the
Resources
folderUnity自己推荐Don't use it!
然而
因为根本不推荐使用 Resources
我宁愿推荐:
如果您以后不想更改它们
您之后无法更改 Resources
,例如替换文件。因此,如果以后无论如何都无法更改文件,那么为什么不直接在以后需要它们的地方引用它们呢?
只需将您的音频文件放在 而非 Resources
的文件夹中,然后在您需要的脚本中直接引用它们:
// Simply drag&drop the clip into this field via the Inspector in Unity
[SerializeField] private AudioClip someClip;
如果您想稍后更改它们
如果您真的希望在构建之后也能够替换它们,您可以改用 UnityWebRequestMultimedia.GetAudioClip
which can also be used to load files from a system file on runtime. For this you wouldn't put your files into the Resources
or any other folder but rather either the StreamingAssets
or the Application.persistentDataPath
.
我经常去:
- 在编辑器中使用
StreamingAssets
文件夹,这样所有的东西都在项目中并通过Application.streamingAssetsPath
访问它
- 在构建中首先检查文件是否存在于
Application.persistentDataPath
- 如果不从
Application.streamingAssetsPath
复制并存入Application.persistentDataPath
- 否则只需从
Application.persistentDataPath
加载它
已修改 API 示例
[RequireComponent(typeof(AudioSource))]
public class AudioExample : MonoBehaviour
{
[SerializeField] private AudioSource _audioSource;
public List<AudioClip> LoadedAudioClips = new List<AudioClip>;
private List<UnityWebRequest> _runningWebRequests = new List<UnityWebRequest>();
private void Awake()
{
if(!_audioSource) _audioSource = GetComponent<AudioSource>();
}
private void Start()
{
StartCoroutine(GetAudioClip());
}
private IEnumerator GetAudioClip()
{
foreach(string file in System.IO.Directory.GetFiles(Path.Combine(Application.persistentDataPath, "Audio", "BGM"))
{
if(!file.EndsWith(System.IO.Patch.GetExtension(".mp3"))) continue;
UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file:///" + file, AudioType.MPEG);
{
_runningWebRequests.Add(www);
www.Send();
}
}
while(_runningWebRequests.Count > 0)
{
foreach(var www in _runningWebRequests.Where(www => www.isDone))
{
_runningWebRequests.Remove(www);
if (www.isError)
{
Debug.LogWarning(www.error);
}
else
{
var clip = DownloadHandlerAudioClip.GetContent(www);
if(clip == null)
{
Debug.LogError("Huh?!");
}
else
{
LoadedAudioClips.Add(clip);
}
}
}
yield return null;
}
}
}
也看到你这么评论了:
StreamingAssets
也是一个特殊的文件夹,你可以把你想在运行时读入的文件存放在里面。它是本地。此外,此文件夹不是来自外部的 "visible",以后无法更改。
如果您以后需要更改内容(例如还保存文件),您将始终需要使用 Application.persistentDataPath
。