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,把这个倒进去
博客:is-hash.com
商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢