Unity WebGL开发笔记

Posted Alvin Chao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity WebGL开发笔记相关的知识,希望对你有一定的参考价值。

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

WebGL开发笔记


前言

`PC项目转WebGL遇到一些坑,还是记录一下好了。


一、先搭环境

1.浏览器选择火狐,理由是方便本地直接运行看效果,相关设置:

火狐浏览器webgl相关设置:
地址栏输入:about:config
搜索:webgl.force-enabled设置为true
搜索:security.fileuri.strict_origin_policy设置为false

2.本地布署IIS

配置简单可以使用其他浏览器看最终效果
控制面板-程序-启用或关闭windows功能-勾Internet Information Services-全勾应用程序开发功能

控制面板-管理工具-Internet Information Services (IIS)管理器-添加网站-设好网站名称,物理路径,端口号

添加MIME类型:
.unity3d application/octet-stream
.unityweb application/binary

3.防火墙规则设置

开启BranchCache内容检索(HTTP-In)规则
防火墙入站规则加TCP端口,端口要与IIS设置的端口一致

二、Unity设置

1.切平台

2.Player Settings设置


勾选后台行动,至于WebGL Template下面说

自动精简代码,挺好用的

平时Enable Exceptions选Explicitly Thrown Exceptions Only查异常用,正式版无问题时用None

3.打版看效果

火狐浏览器直接双击本地浏览或者火狐/谷歌/EDGE随意一款浏览器输入本机IP:端口

4.自定义网页登陆样式


在项目中创建WebGLTemplates文件夹,自定义样式为其子文件夹中index.html,最精简代码如下:

<!DOCTYPE html>
<html lang="en-us">

 <head>
   <meta charset="utf-8">
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   <title>Unity WebGL Player | %UNITY_WEB_NAME%</title>
   <script src="%UNITY_WEBGL_LOADER_URL%"></script>
   <script>
   var gameInstance = UnityLoader.instantiate("gameContainer", "%UNITY_WEBGL_BUILD_URL%");
   </script>
 </head>
 
 <body>
   <div id="gameContainer" style="width: %UNITY_WIDTH%px; height: %UNITY_HEIGHT%px; margin: auto"></div>
 </body>
 
</html>

新功能写到script块里(比如进度条),宽高居中之类写在body里,跟做网站写HTML一样的感觉(大概吧。。本人10几年没摸网站了)。

三、编程注意事项

1.会报错的东西

DllImport类加载C++库的写法调用DLL一定报错
webgl不支持多线程
webgl不支持System.IO
webgl自带字体不会显示汉字要自行导入TTF格式字体
JSON等外部文件读取要用WWW方式获取,比如放在了StreamingAssets文件夹就不能直接获取,实例代码如下:

using UnityEngine.Networking;
public class JsonManager:MonoBehaviour

	JsonData _data;
	string path = Application.streamingAssetsPath + "/test.json";
	void ReadJson() 
        StartCoroutine(WWWLoadJsonFile());
    

    IEnumerator WWWLoadJsonFile() 

        UnityWebRequest www = UnityWebRequest.Get(path);
        yield return www.SendWebRequest();

        if (www.isNetworkError || www.isHttpError)
        
            Debug.LogError(www.error);
        
        else 
            //Debug.Log(www.downloadHandler.text);
            byte[] result = www.downloadHandler.data;
            //有中文的JSON,格式要存utf8+bom
            string jsondata = System.Text.Encoding.UTF8.GetString(result, 3, result.Length-3);
            _data = JsonMapper.ToObject<JsonData>(jsondata);
        

    


Resources文件夹倒是可以正常加载。

2.数据库

原项目用的是方便又小巧的SQLite,可惜sqlite3.dll不给面子,WebGL会报错,于是改用更加小巧的(作者连使用手册都不写)iboxDB,亲测支持WebGL,但有个小问题,我是做的本地存储,保存的数据库会因浏览器不同分别存到浏览器各自的UserData里,脆了点,还能用吧。

//创建数据库
DB.Root(Application.persistentDataPath);
DB db = new DB (6);

//保存数据库,如果使用火狐浏览器会自动保存不需要这条
Application.ExternalEval("FS.syncfs(false, function (err) );");

//删除数据库,这个6对应创建时的iddress
//如果是PC端则自动生成db6.box,不过现在讲webgl就呵呵呵,火狐能用谷歌这条没效果
BoxSystem.DBDebug.DeleteDBFiles(6);

总结

只要知道了规则,以后开发就会很容易了。
PS:没湖底泥好玩

深入理解Unity WebGL内存


Unity支持WebGL以来,我们开发团队就一直致力于优化WebGL的内存消耗。在Unity文件上已有对于WebGL内存管理的详尽分析,在Unite Europe 2015Unite Boston 2015两届大会上,也有专题针对WebGL进行深入的讲解。然而开发者仍然对这方面的内容依旧讨论热烈,所以我们意识到应该分享更多关于这方面的内容。

Unity WebGL和其它发布平台有何不同?

如果开发者从一些本来就要控制内存的平台像是PC或是WebPlayer转过来的话或许已经有个基础,应该都不会造成问题。

如果目标是游戏机(Console)的话,内存管理相对比其他平台容易,因为可以准确知道内存是如何运用的。你可以很好的管理内存确保游戏完美运作。手机平台内存管理就有些复杂,因为设备种类繁多,但至少可以选择最低标准的设备,并根据市场情况略过那些标准更低的设备。

网页就没那么轻松了,理想状况下所有玩家(Client)都有64位浏览器和海量存储器,但事实总是残酷的。首先你无法知道它们计算机的硬件规格。然后除了他们的操作系统和浏览器之外,并无法取得其它信息。最后玩家可能像执行其它网页一样的方法开启你的WebGL内容。因此这是一个非常复杂的问题。


总览

这张图描述的是当Unity WebGL在浏览器执行的内存配置
深入理解Unity WebGL内存

这张图显示在Unity Heap区域上,Unity WebGL的内容会需要分配额外的内存,这里需要理解清楚才能进一步优化来降低玩家流失率。

从图里能看到有几个内存分配,DOM, Unity Heap, Asset Data以及程序代码,程序代码一旦加载网页就会永远存在内存里,其他像是Asset Bundles, WebAudio, Memory File System将会依据内容不同来分配(Asset Bundle下载大小不同,音乐的播放不同等等)

在载入的时候,asm.js解析和编译期间也有几个浏览器内存临时分配,这里有时候会导致32位浏览器出现内存不足。

Unity Heap

一般来说,Unity Heap是包含所有Unity特定的对象(Game Objects),组件(Components),材质(Textures)和着色器(Shaders)等等。 

WebGLUnity Heap的大小需要先算好让浏览器分配内存空间,一旦分配好之后Buffer就不能重设大小了。

负责分配Unity Heap的程序如下:

buffer = new ArrayBuffer(TOTAL_MEMORY);

这段程序可以在产生的build.js里找到,并会交由浏览器的JS VM执行。

TOTAL_MEMORY可以从Player Settings里的WebGL Memory Size来指定,预设情况是256MB,实际上一个空项目只有16MB

然而,真的游戏项目可能需要更多,在大多数情况下会需要用到256MB386MB,记住,设定越多内存表示越多玩家能正常执行。

原代码/编译码 的内存

程序可以执行之前需要:

1被下载到Client

2复制到一个Text blob区块

3编译

顾虑到上述每一个步骤都需要一块内存

1.下载暂存区是暂时的,但源代码和编译码在内存里会留着直到关闭页面。

2.下载暂存区和源代码区分配的内存大小都是由Unity所产生的无压缩js空间。估计会需要多少空间

1

制作一个可发布版本


2

将jsgz及datagz改名为*.gz,然后用解压缩的工具解开它们。


3

解开后的大小会是需要的浏览器内存大小



3.编译码的大小取决于不同浏览器

一个简单的优化方法是启用引擎剥离功能(Strip Engine Code),那样的话发布的包就不会包含不需要的原生的引擎码,(例如:不需要2d物理模块将会被剥除)

请记住,例外支持(Exceptions support)和第三方的套件会影响你的程序大小,话虽如此,我们知道开发者想要在发布时做空值检查(null checks)和数组检查(Array bounds checks)时内存不要销超出了例外支持的范围,为此,你可以送-emit-null-checks-enable-array-bounds-checkil2cpp,像这样:

PlayerSettings.SetPropertyString("additionalIl2CppArgs", "--emit-null-checks --enable-array-bounds-check");

最后请记住,发布development build会产生更多程序代码的包因为它不是,这应该对你来说不是问题,因为最后你会发布给玩家的是最终版本(release build),对吧? ;-)

资源(Asset Data)

在其他平台上,应用程序可以永久的存取硬盘上的内容,但在网页是没有实际的文件系统所以是不可能的,因此,一旦Unity WebGL的数据(.data档案)被下载完成后,它就会存在内存里。缺点是和其他平台相比它需要额外的内存(5.3开始.data的档案会压缩成lz4放在内存)。例如这个Profiler说明,256mbUnity heap会产生约40mb.data档案。

深入理解Unity WebGL内存

甚么是.data档案?他是Unity产生的文件组合:data.unity3d(全部的场景,有依赖关系的资源和Resources目录底下的所有东西)unity_default_resources和引擎所需要的一些小档案。

要了解资源的确切大小,可以在WebGL打包完后看一下 Temp\StagingArea\Data里的data.unity3d(记住,当你关闭Unity时,temp文件夹会被删除)。你也可查看传给UnityLoader.jsDataRequest的偏移植。

new DataRequest(0, 39065934, 0, 0).open('GET', '/data.unity3d');
(这段程序代码可能会依照不同的Unity版本而不同 - 这段从Unity5.4节录)

内存文件系统(Memory File System)

上面提到Unity WebGL虽然没有真正的文件系统,但还是可以存取数据,和其他平台相比主要的差异是在所有的I/O行为都在内存里面完成。重要的是这个文件系统并不在Unity heap里面,因此会需要额外的内存开销,比如当我们写一个数组到档案时:

var buffer = new byte [10*1014*1024];

File.WriteAllBytes(Application.temporaryCachePath + "/buffer.bytes", buffer);

这个档案会写到内存里,你也可以在浏览器的profiler找到:

深入理解Unity WebGL内存

这段Unity heap的内存是256mb

同理,由于Unity的快取系统(caching system)依赖着文件系统,整个快取也是放在这样的文件系统,这代表像和快取的Asset Bundle会同样永远放在内存里直到关闭,而且是在Unity heap区的规画之外。

Asset Bundles

降低WebGL内存消耗最好的方法之一就是采用Asset Bundle(如果你对这个不熟可以参考)。然而,不同的Asset Bundle使用方式可能会对内存(Unity heap内或外)产生重大的影响,有可能会造成无法再32位浏览器无法正常执行。

现在你需要用到Asset Bundle,然后你该怎么做?将所有的资源打包成一包Asset Bundle?

千万不要!就算这样能降低网页的加载时间,你还是需要下载(可能超大)这个Asset Bundle,导致内存飙高。来看看下载AB之前的内存用量:
深入理解Unity WebGL内存

如你看到的,256mbUnity heap定义了。这是下载了AB之后还没有放入暂存。


深入理解Unity WebGL内存

你现在看到的是一个额外的缓冲区,大约是同等于硬盘上的65mb,由XHR分配。这只是一个临时的缓冲区但是他可能造成内存几祯的尖峰,直到GC(garbagecollect)回收它。

那么如何做可以减低这些内存尖峰?需要帮每个资源建立一个AssetBundle?很有趣的想法但不太实用。

打包Asset Bundle是没有一个规则,需要根据你项目的需求来让包装下载有意义。(例如:单机游戏可选男女主角,且游戏周期只会用到一种,那选完角色之后可以只加载该性别的包)

最后,记住完成之后要用AssetBundle.Unload来卸除。

Asset Bundle Caching

WebGLAsset Bundle外取和在其他平台一样用WWW.LoadFromCacheOrDownload,虽然有一个明显的不同是这是放在内存的。在Unity WebGLAB的快取依赖IndexedDB,被放在内存文件系统里的emscripten编译程序支持。

来看看用LoadFromCacheOrDownload下载AssetBundle之前的内存抓图:
深入理解Unity WebGL内存

如你所见,512mbUnity heap用掉了,4mb左右其他分配。包被载入之后的图:
深入理解Unity WebGL内存

额外的内存需求上升至167mb左右,这是这个AssetBundle额外需要的内存(原本是64mb压缩包)。然后下图是js vm做完GC之后的图:
深入理解Unity WebGL内存

结果好多了,但仍需要85mb左右,大多数的内存用来存放AssetBundle,这些内存就算你用unload去释放到结束你都无法取回内存。还有,当玩家第二次用浏览器打开你的内容时,不管之前是否有分配过区块,这些内存又会再次被分配。

这是来自Chrome的内存快照参考:
深入理解Unity WebGL内存

Unity heap之外还有一个Asset bundle系统所需要快取相关的临时分配。坏消息是最近我们发现它比预期的大很多,我们预期会在Unity 5.5 beta 4, 5.3.6p65.4.1p2修复这个问题。

如果你用更旧的Unity版本不想升级或是你的WebGL内容已经接近发布了,可以透过编辑器脚本来设定一些属性:

PlayerSettings.SetPropertyString("emscriptenArgs", " -s MEMFS_APPEND_TO_TYPED_ARRAYS=1",BuildTargetGroup.WebGL);

长远来看要最小化Asset Bundle高速缓存用量最好的解决方案是用而不是用或是没有hash/version参数的,如果你使用新的 API的话。

然后在XMLHttpRequest等级使用备用的快取机制,将下载的档案直接存到indexedDB里避开内存文件系统。这是我们最近开发放在,需要的话可随意取用修改。

AssetBundle 压缩

在Unity 5.3和5.4,都是支持LZMA和LZ4压缩的,虽然使用LZMA(默认值)结果会比未压缩的LZ4来的小,但用在WebGL上有几个缺点:有明显的效能问题、需要更多内存分配。所以我们比较推荐用(实际上,LZMA压缩法在Unity5.5会被移除),为了弥补这个缺口,你可能会希望能用gzip/brotli来压缩你的资源。

可以查看更多关于的相关资料

WebAudio

UnityWebGL的音效是有别于其他平台的,这和内存会有什么关联?

Unity将在JavaScript上支持建立特定的
对象,用来更方便播放网页音效。
由于WebAudio缓冲区存在Unity heap外面,因此无法用Unity profiler追踪,你需要用特定的浏览器工具来检查内存查看有多少用在音效上。这个例子用Firefox来查询音效内存用量:
深入理解Unity WebGL内存

考虑到这些音效缓冲存放未压缩的数据,不太适合放超大型的声音文件(例如:很长的背景音乐)。所以现在你可能需要写些自己的js套件来使用<audio>标签,这样声音文件压缩用较少的内存。

FAQ

减少内存使用的最佳方法是什么?简单概括如下:
1、减少Unity Heap的大小

尽可能保持“WebGL Memory Size”够小

2、减少包里程序代码量

启用Strip Engine Code

关闭异常检测(Disable Exceptions)

避免使用第三方插件

3、减少数据大小

使用Asset Bundle

压缩材质


是否有能够决定最小WebGL Memory Size的方法?

有,最佳方案是使用内存分析器(memory profiler),分析内容实际所需的内存大小,然后依据结果改变WebGLMemory Size。

以空项目为例,内存分析器告诉我们总使用量仅为16MB(这个值可能在不同Unity版本上有所不同),这代表只须要设定WebGL Memory Size大于16MB即可。当然,内存的总使用量将会依据内容而有所不同。

然而,如果因为某些原因无法使用分析器,可以简单地通过不断减少WebGL Memory Size 值,直到发现你的内容真正所需要的最小内存使用量为止。

另外值得注意的是任何不是16的倍数的值都将被自动四舍五入(执行期间)为下一个16的倍数,这是Emscripten编译程序所要求的。

WebGL Memory Size(mb)
设定将决定产生htmlTOTAL_MEMORY(bytes)的值。
深入理解Unity WebGL内存

在不重新打包项目的前提下要测试内存堆栈的值,建议使用编辑html的方式。一旦找到适合的值,只需在Unity项目设定中更改WebGL Memory Size即可。

最后,记住Unity分析器将占用一些来自Unity heap的内存,所以在使用分析器时可能需要加一些WebGL内存大小。


执行时发生内存溢位,如何修复?

这要看是Unity还是浏览器的内存溢位。错误信息会指出问题所在和解决办法,如果你是开发者,可以透过在WebGL设定中为项目分配更多(或更少)的内存来解决。可以依据此来调整WebGL内存大小。然而还有很多可以解决内存溢位的方法。如果出现以下错误信息:
深入理解Unity WebGL内存

除了讯息所提之外,你还可以尝试减少程序和数据的大小。因为当浏览器加载网页时,它会尝试为一些内容寻找空余的内存,其中最重要的是:代码,数据,Unity heap和被编译的asm.js。它们可能相当大,尤其是数据和Unity heap内存,这对32位浏览器来说可能是问题。

在一些例子中,尽管有足够的内存,浏览器仍加载失败,因为内存是碎片化的。这就是为什么有时候你的内容可能在重新启动浏览器之后,可以成功加载的原因。

另一种情况是,当Unity 内存溢位时提示以下讯息:
深入理解Unity WebGL内存

这种情况下就需要优化你的Unity项目。

如何衡量内存消耗?

为了分析内容所使用的浏览器内存,可以使用Firefox浏览器的Chrome。但它们无法显示WebAudio内存使用情况,因此还可以透过里的方法在Firefox里拍张快照然后搜寻“webaudio”找到。如果需要透过JavaScript分析内存,请尝试使用(只支援Chrome)

使用Unity Profiler测量Unity heap内存使用。需注意您可能需要增加WebGL的内存大小,以便能够使用Profiler

此外,我们一直致力于开发新的工具,以便分析发布版本:使用时先包成WebGL版本,访问就能使用该工具。虽然这个工具在Unity5.4中可用,但请注意这还是开发中的功能,可能随时会更改或删除。但至少现在可以使用它达到测试的目的。


WebGLMemory Size的最小值与最大值是多少?

16MB是最小的,最大是2032MB,然而我们通常建议保持在512MB以下。

是否可能因为开发目的需要分配超过2032MB的内存?

这是一个技术上的限制:2048MB(或更多)将会超出TypeArray所用的32位整数型态的最大值,而TypeArray被用于在JavaScript中实现Unity heap

为何Unity Heap大小不可改变?

我们一直在考虑使用Emscripten编译程序标志ALLOW_MEMORY_GROWTH,来允许调整Heap的大小,但目前是没这么设定,因为它会禁用一些Chrome中的优化。我们还未对这个影响做一些真正的基准检验。预计使用这个flag会导致内存问题更严重。如果您遇到Unity heap太小,以至于无法满足所需内存的情况,这时就需要更多内存,那么浏览器就必须分配一个更大的Heap,从旧的里面中复制一切,然后再释放旧的Heap。这么做需要同时维持新Heap和旧Heap两份内存(直到完成复制),这样需要更多的总内存。因此反而会比使用预定固定内存的方式占用更大。

为什么32位浏览器在64位操作系统上会内存溢位?

32位浏览器执行时的内存限制是一样的,无论操作系统是6432位。

结论


最后建议使用浏览器专用的分析工具,来分析你的Unity WebGL内容,因为Unity profiler无法追踪超出Unity heap之外的内存分配。

希望这些信息对你有用。如有任何疑问请到讨论。

点击一下
立即阅读相关好文章







......


近期热文




以上是关于Unity WebGL开发笔记的主要内容,如果未能解决你的问题,请参考以下文章

蛮牛译馆首款用WEBGL平台开发的UNITY游戏

Unity是啥?

Unity是啥?

Unity WebGL开发笔记

unity 3d 是哪个国家开发的

Unity3D 2021软件安装包下载及安装教程