当前位置: 首页 > article >正文

2025-01-07 Unity 使用 Tip3 —— 游戏保存数据到 Application.persistentDataPath 不生效解决方案更新

文章目录

  • 1 问题描述
  • 2 老版解决方案(测试可行)
    • 2.1 创建 js 脚本
    • 2.2 添加 js 引用
  • 3 新版解决方案(测试不可行)
  • 4 实际问题

​ WebGL 平台限制了文件访问系统,在 Unity 以前版本中,开发者想要在 WebGL 上保存游戏到本地很不方便。

​ 目前,Unity 新版中给出了一项解决方案,但经测试,该方案目前不可取(截止 2025-01-07)。

  • Unity 版本:6000.0.26f1c1

1 问题描述

​ Unity WebGL 平台中,游戏保存数据到 Application.persistentDataPath 不生效(官方链接:https://docs.unity3d.com/ScriptReference/Application-persistentDataPath.html)。

image-20250107001802329

​ 老版本 Unity 2019.2 官方给出了解释:Unity WebGL 将会话之间必须保留的所有文件(例如 PlayerPrefs 或保存在 persistentDataPath 中的文件)存储到浏览器 IndexedDB。这是一个异步 API,因此不确定何时完成。

image-20250107010458838

2 老版解决方案(测试可行)

​ 借助 js 脚本,在保存后显式写入数据到/idbfs中。

2.1 创建 js 脚本

​ 在项目 /Assets/Plugins/ 目录下创建名为 Save 的 txt 文件,写入如下内容,更改后缀名为 .jslib:

mergeInto(LibraryManager.library, {
    // 刷新数据到 IndexedDB
    SyncDB: function () {
        FS.syncfs(false, function (err) {
           if (err) console.log("syncfs error: " + err);
        });
    }
});

2.2 添加 js 引用

  1. 在需要使用 js 脚本的 C# 代码块中添加命名空间 System.Runtime.InteropServices; 引用。
  2. 在需要使用 js 脚本的 C# 代码块中加入外部函数声明。
  3. 在负责保存的 C# 代码后面加入外部函数调用。

示例:

// 1. 引用命名空间
#if UNITY_WEBGL && !UNITY_EDITOR
using System.Runtime.InteropServices;
#endif

public class TxtHelper
{
    // 2. 加入外部函数声明
#if UNITY_WEBGL && !UNITY_EDITOR
    [DllImport("__Internal")]
    private static extern void SyncDB();
#endif
    
    public static void Save(string filePath, string content)
    {
        ... // Your code here
            
        // 3. 调用外部函数
#if UNITY_WEBGL && !UNITY_EDITOR
    	SyncDB();
#endif
    }

    public static string Load(string filePath)
    {
        ...
    }
}

​ 参考链接:

  • https://blog.csdn.net/mkr67n/article/details/127348730。
  • https://blog.csdn.net/xiaotaiyang_gege/article/details/135862196。

3 新版解决方案(测试不可行)

​ 对于较新的 Unity 版本(2021.3.44f1、2022.3.44f1、6000.0.11f1 及以上),有此问题的更新:

  • 修复了 Application.persistentDataPath 不会自动持久化的问题,通过添加新的 JS 配置选项“autoSyncPersistentDataPath: true”来启用 Application.persistentDataPath 到 IndexedDB 的自动同步。
  • 该选项位于 Builds/Index.html 文件中,取消注释并刷新正在运行的构建页面,问题将得到解决。

​ 即,不需要任何代码操作,只需要打包完成后,将 Builds/Index.html 文件中的第 78 行取消注释即可。

image-20250107012115743

​ 当然,前提是你选择了 WebGL 平台的 Default 模板,如下图所示(其他模板自行探索)。

image-20250107012433437

​ 如果不想每次打包后都修改 Builds/Index.html 文件,也可以直接修改模板中的 Index.html ,一劳永逸。其位置在 S:\Unity Editor\6000.0.26f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\WebGLTemplates\Default 目录中。

image-20250107012617172

​ 参考链接:

  • https://issuetracker.unity3d.com/issues/webgl-streamwriter-not-triggering-syncfs-when-writing-a-file-to-slash-idbfs。
  • https://gamedev.stackexchange.com/questions/184369/file-saved-to-indexeddb-lost-unless-we-change-scenes。

4 实际问题

​ 在 Unity 6000.0.26f1c1 版本中,按照步骤将上述第 78 行代码注释后,打包运行时出现如下报错:

image-20250107032152086 image-20250107032223093

报错信息:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘length’)
at Object.hashName (WebGL.framework.js:9:66347)
at Object.lookupNode (WebGL.framework.js:9:66977)
at Object.rmdir (WebGL.framework.js:9:54898)
at Object.rmdir (WebGL.framework.js:9:2685)
at Object.rmdir (WebGL.framework.js:9:75286)
at ___syscall_rmdir (WebGL.framework.js:9:177933)
at WebGL.wasm:0x14b27f
at WebGL.wasm:0x2610db6
at WebGL.wasm:0x26167f1
at WebGL.wasm:0x30ac93e
at WebGL.wasm:0x30ad59b
at WebGL.wasm:0x26101ae
at WebGL.wasm:0x29c020e
at WebGL.wasm:0x29bca03
at WebGL.wasm:0x29c119d
at WebGL.wasm:0x29c11b3
at WebGL.wasm:0x29b9849
at WebGL.wasm:0x29b6b88
at WebGL.wasm:0x327a445
at WebGL.framework.js:9:142371
at HandleError (WebGL.framework.js:9:142414)
at WebGL.framework.js:9:144075

​ 解决方法是:将 config.autoSyncPersistentDataPath 设置为 false 或将该行注释掉。问题跟踪来源:https://issuetracker.unity3d.com/issues/webgl-typeerror-cannot-read-properties-of-undefined-reading-length-error-is-thrown-when-starting-the-player-when-config-dot-autosyncpersistentdatapath-is-set-to-true。

image-20250107033431171

​ 也就是说,新版解决方案在 Unity 6000.0.26f1c1 版本中失效。

​ 目前退回到使用老版解决方案,测试可行。

​ WebGL 可真是太好玩辣!


http://www.kler.cn/a/472360.html

相关文章:

  • Blazor用户身份验证状态详解
  • Elasticsearch分片数量是什么意思?
  • 黄仁勋演讲总结(2种显卡,1个开源大模型,1个数据采集平台)
  • Excel 做数据分析的好与不好
  • Ubuntu下的小bug
  • MITRE ATTCK 简介:初学者指南
  • 基于Spring Boot的仓库租赁管理系统
  • el-dialog 组件 在<style lang=“scss“ scoped>标签
  • 2025-01-06日SSH钓鱼日志
  • 冬季蜂巢内蜂群运动的自动化监测
  • c++开源协程库libgo介绍及使用,srs协程,boost协程 Boost::fiber
  • Redis奇幻之旅(四)4. Redis Cluster
  • 使用systemd管理MySQL服务器
  • AI 平台 GPU 节点上运行基于 PyTorch 的深度学习任务
  • Mac中配置vscode(第一期:python开发)
  • 【Linux】UOS统信服务器本地yum源搭建实践
  • 1/7 C++
  • [SeaTunnel] [MySql CDC] Generate Splits for table db.table error
  • 【LangGraph Agent架构篇—多智能体系统1】【多智能体网络】
  • go 1.23.4安装
  • 常用的数据引擎及其特点
  • 高阶知识库搭建实战五、(向量数据库Milvus安装)
  • 虚幻(UE)资源网站
  • gaussdb怎么查询一个表所在的表空间的总大小和可用大小,用GB为单位表示?
  • 【每日学点鸿蒙知识】关于热修复、图片预览、多个@State刷新性能问题等
  • 【网络安全技术与应用】(选修)实验2 用Wireshark分析典型TCP/IP体系中的协议