欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity WebGL的记录

程序员文章站 2022-05-23 13:33:00
...

前言:

最近在学习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

Unity WebGL的记录

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

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

反正坑是真不少

 


本地运行:

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

Unity WebGL的记录

 


结合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 即可

Unity WebGL的记录

 


 

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,把这个倒进去

 

 

 

 

 

 

 

 

博客:is-hash.com

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