You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中JSON数据PC端正常读取,安卓端失效的原因求助

为什么安卓端无法读写StreamingAssets里的JSON文件?

这个问题我太熟了——Unity的StreamingAssets目录在不同平台的行为差异很大,这就是你遇到的核心问题:

  • PC端:StreamingAssets是普通的文件系统目录,完全支持读写操作,所以你的代码能正常工作。
  • 安卓端:StreamingAssets里的文件是打包进APK压缩包的,属于只读资源,既不能直接写入,甚至连File.ReadAllText这种同步读取方式都无法正常使用。

解决方案:切换到persistentDataPath存储

正确的做法是:把StreamingAssets作为初始默认数据的存放地,第一次运行游戏时将默认的lastlevel.json复制到有读写权限的Application.persistentDataPath目录,之后所有的读写操作都针对这个目录下的文件。

修改后的完整代码

这里我帮你重写了ProgressSavior脚本,处理了跨平台的读写逻辑,包括安卓端的异步读取:

using System.Collections;
using System.IO;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

public class ProgressSavior : MonoBehaviour
{
    // 最终的读写路径:persistentDataPath(全平台可读写)
    private string _savePath;
    // 初始默认文件的路径(StreamingAssets)
    private string _defaultFilePath;
    
    private readonly Regex _levelRegex = new Regex(@"^level_[1-9][0-9]*$");
    private Level _savedLevel;

    private void Awake()
    {
        // 初始化路径
        _defaultFilePath = Path.Combine(Application.streamingAssetsPath, "lastlevel.json");
        _savePath = Path.Combine(Application.persistentDataPath, "lastlevel.json");
        
        // 启动协程加载初始数据
        StartCoroutine(InitializeSaveData());
    }

    private IEnumerator InitializeSaveData()
    {
        // 检查persistentDataPath里是否已有保存文件
        if (!File.Exists(_savePath))
        {
            // 如果没有,从StreamingAssets复制默认文件
            yield return CopyDefaultFileToPersistentPath();
        }
        
        // 读取保存的数据
        string jsonData = File.ReadAllText(_savePath);
        _savedLevel = JsonUtility.FromJson<Level>(jsonData);
        
        // 检查当前场景是否是关卡,是的话尝试保存
        string currentSceneName = SceneManager.GetActiveScene().name;
        if (_levelRegex.IsMatch(currentSceneName))
        {
            TrySaveCurrentLevel(currentSceneName);
        }
    }

    private IEnumerator CopyDefaultFileToPersistentPath()
    {
        // 安卓端需要用UnityWebRequest读取StreamingAssets
        if (Application.platform == RuntimePlatform.Android)
        {
            using UnityWebRequest webRequest = UnityWebRequest.Get(_defaultFilePath);
            yield return webRequest.SendWebRequest();
            
            if (webRequest.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError($"Failed to load default save file: {webRequest.error}");
                // 初始化一个默认的Level对象
                _savedLevel = new Level { name = "level_1" };
                string defaultJson = JsonUtility.ToJson(_savedLevel);
                File.WriteAllText(_savePath, defaultJson);
                yield break;
            }
            
            // 将读取到的内容写入persistentDataPath
            File.WriteAllText(_savePath, webRequest.downloadHandler.text);
        }
        else
        {
            // PC/其他平台直接复制文件
            File.Copy(_defaultFilePath, _savePath);
        }
    }

    private bool ShouldSaveLevel(string currentLevelName)
    {
        if (!_levelRegex.IsMatch(currentLevelName) || !_levelRegex.IsMatch(_savedLevel.name))
        {
            return false;
        }
        
        if (int.TryParse(currentLevelName.Split('_')[1], out int currentLevelNum) && 
            int.TryParse(_savedLevel.name.Split('_')[1], out int savedLevelNum))
        {
            return currentLevelNum > savedLevelNum;
        }
        
        return false;
    }

    private void TrySaveCurrentLevel(string currentLevelName)
    {
        if (ShouldSaveLevel(currentLevelName))
        {
            _savedLevel.name = currentLevelName;
            string jsonData = JsonUtility.ToJson(_savedLevel);
            File.WriteAllText(_savePath, jsonData);
            Debug.Log($"Saved progress: {currentLevelName}");
        }
        else
        {
            Debug.LogWarning("No need to save - current level is not newer than saved progress");
        }
    }

    public string LoadLastLevel()
    {
        return _savedLevel?.name ?? "level_1";
    }
}

[System.Serializable]
public class Level
{
    public string name;
}

关键修改点说明

  1. 路径切换:用Application.persistentDataPath替代StreamingAssets作为读写目录,这个路径在安卓、iOS、PC等所有平台都有完整的读写权限。
  2. 初始数据复制:第一次运行时,将StreamingAssets里的默认lastlevel.json复制到persistentDataPath,避免每次都读取只读的APK资源。
  3. 安卓端读取适配:安卓端用UnityWebRequest异步读取StreamingAssets里的资源,这是Unity官方推荐的方式。
  4. 错误处理:添加了读取失败时的默认值初始化,避免空引用错误。
  5. 逻辑优化:将启动时的逻辑移到协程中,处理异步读取的问题,避免主线程阻塞。

额外注意事项

  • 确保lastlevel.json已经放在Assets/StreamingAssets目录下,并且内容是有效的JSON(比如{"name":"level_1"})。
  • 安卓打包时,Unity会自动将StreamingAssets里的文件打包进APK,不需要额外设置。
  • 测试安卓端时,可以通过adb shell查看persistentDataPath的位置(通常是/storage/emulated/0/Android/data/[你的包名]/files/),确认文件是否正确生成。

内容的提问来源于stack exchange,提问作者lolozen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 10:48:12