Unity发布WebGL后加载本地文件

您所在的位置:网站首页 Unity预览窗口的缓存 Unity发布WebGL后加载本地文件

Unity发布WebGL后加载本地文件

2023-07-27 07:56| 来源: 网络整理| 查看: 265

Unity发布WebGL平台的程序是不可以直接访问用户电脑的文件的。

但是在使用浏览器的时候,很多的网站都可以弹出一个窗口,选择文件并打开。

像下面这种(这里↓可以点击)

代码其实就一句:

所以,我就有个思路,想办法用Unity调用这个组件,直接从这个组件里面获取到文件流。

直接开整,先在Unity里面搞个WebGl.jslib文件。这个文件是与webgl交互用的。

详情参照Unity官网:https://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html

文件里面的内容如下:

mergeInto(LibraryManager.library, { clickSelectFileBtn:function () { console.log("Enter"); clickSelectFileBtn(); }, });

在Unity里面搞个C#交互脚本,去调用这里面的 clickSelectFileBtn() 方法

[DllImport("__Internal")] private static extern void clickSelectFileBtn(); /// /// 点击Open按钮 /// public static void ClickSelectFileBtn() { clickSelectFileBtn(); }

第一行的方法名字与.jslib文件里面的方法名一致。

这样在Unity里面调用到 ClickSelectFileBtn() 的时候就可以直接调用到.jslib的 clickSelectFileBtn() 方法了。

接下来,看下Webgl端接收代码

打完WebGL包在路径下会生成两个(没有StreamingAssets)~~~~三个文件夹(有StreamingAssets)和一个index.html文件。

 

 

 打开index.html文件:

1 DOCTYPE html> 2 3 4 5 6 Unity WebGL Player | Modeleditor 7 8 9 10 11 12 var unityInstance = UnityLoader.instantiate("unityContainer", "Build/Web.json", {onProgress: UnityProgress}); 13 14 15 16 17 18 19 20 21 Modeleditor 22 23 24 25

第12行,unity已经帮我们获取到了Unity的实例化,可以直接使用这个变量往unity发消息。

那我们刚才unity发送的消息怎么接收呢?

只需要在   之间插入一个叫clickSelectFileBtn()的方法就行。

function clickSelectFileBtn() { }

这时候就可以点击Unity里面的按钮,触发webgl里面的方法了。

在回到初衷,我们就是想用到打开文件的组件,写在哪?

只要放到 之间即可。

保存后刷新下网页,发现页面左上角多了一个选择文件组件

这样还是行,需求是点击unity按钮,触发这个组件,当前这样只能手动点击触发,而且显示在这直接当bug处理。

在这个组件里面添加一个id,方便之后用代码获取到,使用style="display:none"直接把组件隐藏,还要添加一个事件fileImport(),去处理打开的文件得到文件流,再发给unity。

 

接下来就直接上代码:

   var unityInstance = UnityLoader.instantiate("unityContainer", "Build/Web.json", {onProgress: UnityProgress}); function sendMessageToUnity(s) { //发送给unity unityInstance.SendMessage("TestObj", "GetBase64", s); } function clickSelectFileBtn() { var tempFileLayout = document.getElementById('files'); tempFileLayout.click(); } function fileImport() { //获取读取我文件的File对象 var selectedFile = document.getElementById('files').files[0]; if (selectedFile != null) { var reader = new FileReader(); reader.readAsDataURL(selectedFile); reader.onload = function (e) { var base64Str = e.currentTarget.result.substring(e.currentTarget.result.indexOf(',') + 1); sendMessageToUnity(base64Str); } } } unityInstance.SendMessage("TestObj", "GetBase64", s);这里第一个参数是物体在场景中的名字,第二个参数是挂在这个物体上的脚本中方法名,第三个参数是方法的参数。

在unity里面建个物体叫TestObj,随便写个脚本,

public void GetBase64(string base64Str) { byte[] bs = Convert.FromBase64String(base64Str); }

 

就这样是完全可以传输1Mb以内的贴图,文本等文件。

但是如果文件过大,就会报异常莫名奇怪的堆栈溢出!

Uncaught RuntimeError: memory access out of bounds

 

 

刚开始是以为unity发布web的时候内存设置得太小了,想修改webgl内存大小,发现Unity在2019版本中的PlayerSetting移除了这个选项WebGL memory size。

但是使用Editor编辑器发现还是可以获取到这个属性。

using UnityEngine; using UnityEditor; public class ChangeWebGlMemeorySize : Editor { [MenuItem("Tool/设置webgl内存为256")] public static void ChangeMemorySize() { PlayerSettings.WebGL.memorySize = 256; } [MenuItem("Tool/查看webgl内存大小")] public static void GetMemorySize() { Debug.Log(PlayerSettings.WebGL.memorySize); } }

然而,事情并没有预想的那么顺利,修改完webgl的内存后,还是一直报这个堆栈溢出的错误。

最后终于找到问题:竟然是Webgl的SendMessage给Unity的时候参数不能过大,超过一定字节的时候就会报这个溢出错误!!!

所以解决办法:

var base64Str = e.currentTarget.result.substring(e.currentTarget.result.indexOf(',') + 1); arr = []; step = 3000; for (var i = 0, l = base64Str.length; i < l; i += step) { arr.push(base64Str.slice(i, i + step)) } sendMessageToUnity("Start"); for (i = 0; i < arr.length; i++) { sendMessageToUnity(arr[i]); } sendMessageToUnity("End");

相应的在Unity接受字符串的地方改为:

StringBuilder stringBuilder = new StringBuilder(); public void GetBase64(string base64Str) { if (base64Str == "Start") { stringBuilder.Clear(); } else if (base64Str == "End") { byte[] bs = Convert.FromBase64String(stringBuilder.ToString()); } else { stringBuilder.Append(base64Str); } }

直接改为将字符串分成大小一定的段落,分批的发给Unity,注意要使用 StringBuilder  不然内存也会爆炸!

 

2021.06.10更新

发现了新方法,速度更快,内存更少

直接使用: URL.createObjectURL(file)

获取到的缓存url,不用分段发送文件了,可以直接发送给Unity使用。

function SelectFile(){ var file=document.getElementById('files').files[0]; SetFileUrlByDragEnd(file); } function SetFileUrlByDragEnd(file) { unityInstance.SendMessage("TestObj", "SendUrl", file.name+"|"+URL.createObjectURL(file)); }

在unity端获取到网址后直接用UnityWebrequest请求就行了。

 

就这样,拜拜~



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3