Unity WebGL的记录

前言:

最近在学习Unity导出webGL

 

 

 


导出配置:

什么是webGL:

WebGL工程选项允许Unity以JavaScript程序形式发布使用HTMl5技术和WebGL渲染API、在网页浏览器中运行的Unity内容。为了构建和测试WebGL内容,在“Build Player”窗口中选择WebGL构建目标,并且点击“Build & Run ”

为了在WebGL中运行,所有代码需要是JavaScript。我们使用 emscripten 编译器工具链来交叉编译Unity运行时代码(用C和C++编写)到asm.js的JavaScript代码。asm.js是一个非常理想的JavaScript子集,允许JavaScript引擎 预编译asm.js代码为极具性能的原生代码。
为了转换.NET 游戏代码(C#和UnityScript脚本)至JavaScript,我们使用了一个称为IL2CPP的技术。IL2CPP获取.NET字节代码并将它转换为对应的C++源文件,然后通过使用emscripten,这些源文件被编译,最终将用户的脚本转换为JavaScript。

平台支持:

Unity WebGL内容在当前大部分桌面主流浏览器都支持,然而不同的浏览器,提供的支持程度仍有所差别。移动设备并不被Unity WebGL支持。
不是所有Unity特性在WebGL工程中都可用,大部分是因为平台限制的关系,例如:

  • 多线程不被支持,因为JavaScript没有线程支持技术。这对Unity使用多线程来提升执行速度,以及脚本代码和管理dll,都产生了影响。实际上,所有在System.Threading命名空间中的都不被支持。
  • WebGL工程无法在MonoDevelop或Visal Studio中进行调试。详细请看“Debugging and trouble shooting WebGL builds”。
  • 浏览器不允许联网进行IP套接字的直接存取,出于安全方面得考虑。详细请看“WebGL Networking”。
  • WebGl图形API等同于OpenGL ES 2.0,有一些限制,详细请看:“ WebGL Graphics”。
  • WebGL工程使用一个自定义、基于Web Audio API的后端,用于音效。这仅支持基本的音频功能。详细请看“Using Audio in WebGL”。
  • WebGL是一个预编译平台,所以它不允许使用System.Reflection.Emit的代码动态生成。这在所有其它IL2CPP平台,iOS,以及大部分控制台都一样。

导出:

  • 首先就是先说一个大坑——项目的位置目录,绝对不能有中文!
  • 我们下载相应的导出WebGL配置之后,可以看到这样的选项,当你选中Development Build 勾选框,Unity生成一个开发工程,有事件探查器支持喝一个开发控制台来查看错误。另外,开发工程不压缩内容(即,内容不是最小化的);维持在在可以人工阅读的JavaScript形式,保留了函数名,这样用户得到有用的错误追踪栈。注意,这意味着开发工程会非常大,太大而无法发布,所以,一般不要选中Development Build

  • 发布时尽量不要勾选Strip Engine Code(这个选项默认被选中,来允许代码为WebGL所精简。随着这个选项被选中,Unity不包括任何你不使用的类的代码。代码精简可能会造成一些问题,所以尽量不要勾选这个)

更多坑就参考这个文档这个文档

反正坑是真不少

 


本地运行:

导出之后生成了两个文件夹和一个html文件,那个html文件就是入口文件,在IE浏览器上可以看到完美运行(但是在谷歌却不行,查了一下,原来是这样因为谷歌为了安全考虑,不支持打开本地WebGL),下面是我用IE打开的效果

 


结合nodejs搭服务器运行:

为了方便在谷歌浏览,以及以后让公网可以浏览,我们来利用nodejs做后端,搭一个服务器程序:

代码写好了:

#server.js
//导入系统核心库
var http=require("http");
var fs=require("fs");
var url=require("url");
var path=require("path");

var server=http.createServer(function(request,response){
    console.log("有人连过来了~");
    //解析url路径,并保存路径名
    var pathname=url.parse(request.url).pathname;
    //空路径为入口界面(Unity打包出来也就这一个页面而已)
    if(pathname=="/"){
        pathname="index.html"
    }
    //指定文件路径(这里放在运行脚本同级的名叫static的目录中)
    //normalize:使格式规范化
    //这里的pathname并不一定是index.html搞清楚
    var fileURL="./"+path.normalize("./static/"+pathname);
    //得到扩展名
    //以.结尾返回.,以.html结尾返回.html,无.返回空值
    var extname=path.extname(pathname);

    //读文件
    fs.readFile(fileURL,function(err,data){
        if(err){
            //文件不存在
            response.writeHead(404,{
                "Content-Type":"text/plain;charset=UTF8"
            })
            response.end("404了~");

        }else{
            //返回对应的数据
            getMime(extname,function(mime){
                response.writeHead(200,{
                    "Content-Type":mime
                })
                response.end(data);
                console.log("extname"+extname);
            });
        }
    });
});

//这里写服务器ip,默认端口
server.listen("3001");

function getMime(extname,callback){
    fs.readFile("./mime.json",function(err,data){
        if(err){
            throw Error("找不到mime.json"+extname);
        }
        //转成json对象
        var mimeJSON=JSON.parse(data);
        var mime=mimeJSON[extname] || "text/html" ;
        //执行回调函数
        callback(mime);
    })
}

需要一个mime.json文件存储需要的类型

#mime.json
{
    ".memgz":"application/octet-stream",
    ".datagz":"application/octet-stream",
    ".unity3dgz":"application/octet-stream",
    ".jsgz":"application/x-javascript; charset=UTF-8",
     ".*":"application/octet-stream"
}

还有一个404文件应对访问路径不正确的情况

#404.html
<!DOCTYPE html>
<html lang="en"> 
    <head>
        <meta charset="UTF-8">
        <title>test</title>
    </head>
    <body>
            404
    </body>
</html>

然后我们将Unity打包出来的文件都放到static文件夹下,

最后在终端 node server,然后在浏览器里访问:127.0.0.1:3001 即可

 


 

WebGL与nodeJS交互:

全屏适应:

在聊代码交互之前先说一种让WebGL场景在浏览器中全屏自适应的方法,只要在Unity导出的webGL文件里做手脚就好了

原来的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端测试</title>
    <script src="Build/UnityLoader.js"></script>
    <script>
      UnityLoader.instantiate("unityContainer", "Build/webGLTest.json");
    </script>
  </head>
  <body>
    <div id="unityContainer" style="width: 800px; height: 560px; margin: auto"></div>
  </body>
</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端测试</title>
    <script src="Build/UnityLoader.js"></script>
    <script>
      UnityLoader.instantiate("unityContainer", "Build/webGLTest.json");
      function Reset()
      {
        var canvas = document.getElementById("#canvas");//获取#canvas
        canvas.height=  document.documentElement.clientHeight;//获取body可见区域高度
        canvas.width =  document.documentElement.clientWidth;//获取body可见区域高度
      } 
    </script>
  </head>
  <body onresize="Reset()",scroll=no,style="overflow:hidden">
    <div id="unityContainer" style="width: 100vw; height: 100wh; margin: auto"></div>
  </body>
</html>

交互:

js传给WebGL:

在WebGL导出的html中加上这样一句话:

gameInstance.SendMessage("ObjectName","LoadAssetBundle","value");

gameInstance是 上面UnityLoader.instantiate的接收返回值的变量

  • ObjectName就是Unity场景中模型的名称,是最上级物体
  • LoadAssetBundlesh是Unity里的方法名称
  • value是这个方法的参数(若无参则不需加这个参数)
  • 若有多个参数就用逗号隔开加载value后面

即在C#中有这样的一个方法:

public class ObjectName : MonoBehaviour
{   
	public void LoadAssetBundle(string value){
	
        }
}

 

WebGL传给js:

在C#代码中加入:

Application.ExternalCall("Show", input.text);
  • Show是js中定义的方法
  • input.text也是传给js的参数

js中应有这样的函数:

 function Show(string){
                     
        }

更新一下,Application.ExternalCall方法已经过时,大家可以参考一下官方文档

解决方法:

  • 先需要写一个JS的脚本,主要是调用mergeInto();方法,第一个参数不用变,第二个参数就是JS的方法集合。写完之后将这个文件的后缀改为.jslib,放到Plugins文件夹中
mergeInto(LibraryManager.library, {

  Hello: function () {
    window.alert("Hello, world!");
  },

  HelloString: function (str) {
    window.alert(Pointer_stringify(str));
  },

  PrintFloatArray: function (array, size) {
    for(var i = 0; i < size; i++)
    console.log(HEAPF32[(array >> 2) + i]);
  },

  AddNumbers: function (x, y) {
    return x + y;
  },

  StringReturnValueFunction: function () {
    var returnStr = "bla";
    var bufferSize = lengthBytesUTF8(returnStr) + 1;
    var buffer = _malloc(bufferSize);
    stringToUTF8(returnStr, buffer, bufferSize);
    return buffer;
  },

  BindWebGLTexture: function (texture) {
    GLctx.bindTexture(GLctx.TEXTURE_2D, GL.textures[texture]);
  },

});

 

  • 之后在C#中调用
using UnityEngine;
using System.Runtime.InteropServices;

public class NewBehaviourScript : MonoBehaviour {

    [DllImport("__Internal")]
    private static extern void Hello();

    [DllImport("__Internal")]
    private static extern void HelloString(string str);

    [DllImport("__Internal")]
    private static extern void PrintFloatArray(float[] array, int size);

    [DllImport("__Internal")]
    private static extern int AddNumbers(int x, int y);

    [DllImport("__Internal")]
    private static extern string StringReturnValueFunction();

    [DllImport("__Internal")]
    private static extern void BindWebGLTexture(int texture);

    void Start() {
        Hello();
        
        HelloString("This is a string.");
        
        float[] myArray = new float[10];
        PrintFloatArray(myArray, myArray.Length);
        
        int result = AddNumbers(5, 7);
        Debug.Log(result);
        
        Debug.Log(StringReturnValueFunction());
        
        var texture = new Texture2D(0, 0, TextureFormat.ARGB32, false);
        BindWebGLTexture(texture.GetNativeTextureID());
    }
}

 

 


WebJS踩得坑:

  • 导出路径杜绝中文
  • 默认字体不显示,建议Win + R 开启运行,输入 fonts 直接打开系统字体库
    找到你想要的字体, 后缀必须是ttf,把这个倒进去

 

 

 

 

 

 

 

 

 

商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢

 

发表评论