如何在Meta Quest上访问Unity应用的MIDI数据文件?
Meta Quest上Unity读取StreamingAssets中MIDI文件失败的解决方案
你的问题核心是路径处理错误和Android平台下StreamingAssets的访问逻辑偏差,权限问题并非关键原因(StreamingAssets属于APK内部资源,不需要外部存储权限)。以下是具体问题分析和修复方案:
核心问题拆解
- 路径拼接错误:Android平台下
Application.streamingAssetsPath的格式为jar:file:///data/app/.../base.apk!/assets,你用Path.Combine拼接路径时会生成带Windows风格反斜杠的URL,导致UnityWebRequest无法识别。 - 无法枚举StreamingAssets文件夹:Android打包后,StreamingAssets是APK压缩包内的资源,直接用UnityWebRequest请求文件夹URL不会返回文件列表,这和Windows/编辑器的文件系统逻辑完全不同。
- 冗余权限请求:你请求的
ExternalStorageRead权限完全没必要,反而会干扰问题排查方向。
修复方案
方案1:预生成文件列表(推荐)
在编辑器中生成包含所有MIDI文件路径的JSON文件,打包时放入StreamingAssets,运行时读取这个JSON获取文件列表,避免在Android下枚举文件夹。
第一步:编辑器脚本生成文件列表
在Editor文件夹下创建脚本:
using System.IO; using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Text.Json; public class MidiListGenerator : EditorWindow { [MenuItem("Tools/Generate MIDI File List")] public static void GenerateMidiList() { string midiRoot = Path.Combine(Application.streamingAssetsPath, "MidiFiles"); List<string> midiPaths = new List<string>(); // 遍历Piano和Drums文件夹 string pianoFolder = Path.Combine(midiRoot, "Piano"); if (Directory.Exists(pianoFolder)) { foreach (string file in Directory.GetFiles(pianoFolder, "*.mid")) { midiPaths.Add($"Piano/{Path.GetFileName(file)}"); } } string drumsFolder = Path.Combine(midiRoot, "Drums"); if (Directory.Exists(drumsFolder)) { foreach (string file in Directory.GetFiles(drumsFolder, "*.mid")) { midiPaths.Add($"Drums/{Path.GetFileName(file)}"); } } // 写入JSON文件 string jsonPath = Path.Combine(midiRoot, "MidiFileList.json"); string json = JsonSerializer.Serialize(midiPaths); File.WriteAllText(jsonPath, json); AssetDatabase.Refresh(); Debug.Log("MIDI文件列表已生成:" + jsonPath); } }
点击顶部菜单Tools/Generate MIDI File List生成JSON文件。
第二步:修改MidiFileLoader的加载逻辑
替换原有的LoadMidiFiles和LoadMidiFilesFromFolder方法:
// 替换原LoadMidiFiles方法 private IEnumerator LoadMidiFiles() { string listPath = Path.Combine(Application.streamingAssetsPath, "MidiFiles/MidiFileList.json"); string jsonContent = ""; if (Application.platform == RuntimePlatform.Android) { UnityWebRequest request = UnityWebRequest.Get(listPath); yield return request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) { Debug.LogError("读取文件列表失败:" + request.error); yield break; } jsonContent = request.downloadHandler.text; } else { jsonContent = File.ReadAllText(listPath); } List<string> allMidiPaths = JsonSerializer.Deserialize<List<string>>(jsonContent); // 根据isDrums筛选路径 string targetFolder = MidiReader.instance.isDrums ? "Drums/" : "Piano/"; midiFilePaths = allMidiPaths.Where(p => p.StartsWith(targetFolder)).ToList(); PopulateMenu(); } // 移除原LoadMidiFilesFromFolder方法
方案2:修正MIDI文件读取逻辑
确保MidiReader.StartReading方法在Android下用UnityWebRequest读取文件内容,而非File类:
// MidiReader中的StartReading方法示例 public void StartReading(string relativePath) { string fullPath = Path.Combine(Application.streamingAssetsPath, relativePath); StartCoroutine(ReadMidiFile(fullPath)); } private IEnumerator ReadMidiFile(string path) { byte[] midiData = null; if (Application.platform == RuntimePlatform.Android) { UnityWebRequest request = UnityWebRequest.Get(path); yield return request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) { Debug.LogError("读取MIDI文件失败:" + request.error); yield break; } midiData = request.downloadHandler.data; } else { midiData = File.ReadAllBytes(path); } // 解析midiData生成关卡... }
最后:移除冗余权限请求
删除Start方法中的权限请求代码以及RequestStoragePermissions方法,因为完全不需要。
内容的提问来源于stack exchange,提问作者Omar Ali
相关产品推荐
相关产品推荐

