javascript调用unity

WebGL:与浏览器脚本交互

构建适用于 Web 的内容时,可能需要与网页上的其他元素进行通信。或者,您可能希望使用 Unity 当前在默认情况下未公开的 Web API 来实现功能。在这两种情况下,都需要直接与浏览器的 JavaScript 引擎连接。Unity WebGL 提供了不同的方法来执行此操作。

从 Unity 脚本调用 JavaScript 函数

在项目中使用浏览器 JavaScript 的建议方法是将 JavaScript 源代码添加到项目中,然后直接从脚本代码中调用这些函数。为此,请使用 .jslib 扩展名将包含 JavaScript 代码的文件放置在 Assets 文件夹中的“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.GetNativeTexturePtr());

    }

}

简单的数字类型可在函数参数中传递给 JavaScript,无需进行任何转换。其他数据类型将作为 emscripten 堆(实际上就是 JavaScript 中的一个大型数组)中的指针进行传递。对于字符串,可使用 Pointer_stringify helper 函数转换为 JavaScript 字符串。要返回字符串值,必须调用 _malloc 来分配一些内存,并调用 stringToUTF8 helper 函数向其中写入 JavaScript 字符串。如果字符串是返回值,则 il2cpp 运行时将负责为您释放内存。对于原始类型的数组,emscripten 针对内存的不同大小的整数、无符号整数或浮点数表示形式,提供其堆的不同 ArrayBufferViews:__HEAP8、HEAPU8、HEAP16、HEAPU16、HEAP32、HEAPU32、HEAPF32、HEAPF64__。为了在 WebGL 中访问纹理,emscripten 提供了 GL.textures 数组,该数组将本机纹理 ID 从 Unity 映射到 WebGL 纹理对象。可在 emscripten 的 WebGL 上下文 GLctx 中调用 WebGL 函数。

有关如何与 JavaScript 交互的更多信息,请参阅 emscripten 文档

另外,请注意,在 Unity 安装文件夹中有几个插件可供参考(位于 PlaybackEngines/WebGLSupport/BuildTools/lib 和 PlaybackEngines/WebGLSupport/BuildTools/Emscripten/src/library* 中)。

从 JavaScript 调用 Unity 脚本函数

有时需要从浏览器的 JavaScript 向 Unity 脚本发送一些数据或通知。建议的做法是调用内容中的游戏对象上的方法。如果要从嵌入在项目中的 JavaScript 插件执行调用,可使用以下代码:

unityInstance.SendMessage(objectName, methodName, value);

其中,__objectName__ 是场景中的对象名称;__methodName__ 是当前附加到该对象的脚本中的方法名称;__value__ 可以是字符串、数字,也可为空。例如:

unityInstance.SendMessage('MyGameObject', 'MyFunction');

unityInstance.SendMessage('MyGameObject', 'MyFunction', 5);

unityInstance.SendMessage('MyGameObject', 'MyFunction', 'MyString');

如果希望从嵌入页面的全局作用域内执行调用,请参阅下面的代码可见性部分。

从 Unity 脚本调用 C 函数

由于 Unity 使用 emscripten 将源代码从 C/C++ 代码编译为 JavaScript,因此您也可以使用 C/C++ 代码编写插件,并从 C# 调用这些函数。因此,您可以不使用上面示例中的 jslib 文件,而是在项目中使用 C/C++ 文件;它将自动使用您的脚本实现编译,并且您可以从中调用函数,就像上面的 JavaScript 示例一样。

如果使用 C++ (.cpp) 来实现该插件,则必须确保使用 C 链接来声明函数以免发生名称错用问题:

# include <stdio.h>

extern "C" void Hello ()

{

    printf("Hello, world!\n");

}

extern "C" int AddNumbers (int x, int y)

{

    return x + y;

}

代码可见性

从 Unity 5.6 开始,所有构建代码都在自己的范围内执行。这种方法可以将内容嵌入任意页面,而不会与嵌入页面代码发生冲突,并且可以在同一页面上嵌入多个构建版本。

如果项目中包含 .jslib 插件形式的所有 JavaScript 代码,则此 JavaScript 代码的运行作用域将与编译的构建相同,并且代码应该与 Unity 先前版本中的工作方式非常相似(例如,以下对象和函数应该在 JavaScript 插件代码中直接可见:Module、SendMessage、HEAP8、ccall 等)。

但是,如果计划从嵌入页面的全局作用域调用内部 JavaScript 函数,则必须在 WebGL 模板 index.html 中使用 unityInstance 变量。在 Unity 引擎实例化成功后执行此操作,例如:

  var myGameInstance = null;

    script.onload = () => {

      createUnityInstance(canvas, config, (progress) => {...}).then((unityInstance) => {

        myGameInstance = unityInstance;

        …

然后可以使用 myGameInstance.SendMessage() 向构建发送消息,或使用 myGameInstance.Module 访问构建 Module 对象。


方案如下:

1.在Unity场景中有一个GameObject,我们命名为A,

A上有C#脚本,里面有个方法

public void Func(string str)

{

//处理逻辑

}

2.在发布出的WebGL项目index.html中用JS调用此方法

<script>

var gameInstance = UnityLoader.Instantiate("gameContainer", "Build/WebAndUnity.json",{onProgress:UnityProgress});

function testSend()

        {

                gameInstance.SendMessage("A", "Func", "string");

        }

</script>

需要注意的就是gameInstance,先要初始化出一个gameInstance,如上代码,再用gameInstance调用SendMessage方法。

这段代码的大概意思就是:web前端通过unityloader创建一个unity的容器实例,再通过容器实例给游戏对象A发送一个调用Func函数的消息,并且传入一个string参数。

注:可以传递的参数类型:int ,string,空。


Unity项目可以打包成WebGl,打包后的项目文件:

Build中是打包后的Js代码;

Index.html是web项目的入口,里面可以调整web的自适应,也可以拿去嵌套;

TemplateData是打包时候选的webGl模板;

web端游戏可能Unity只负责做游戏部分,而官网由另外的团队制作,之间就需要Unity和Js代码之间的相互调用;

Unity调用JavaScript

声明一下,这里说的都是Unity和外部JS代码的互相调用,项目内调用有其他方法;

老版本提供一个过时的方法

1.在WebGL项目中的Index.html中添加要调用的JS方法

functionUnity2JavaScript() {alert("UnityToWeb") }

2.Unity中调用

Application.ExternalCall("Unity2JavaScript");//可以有参数,没有返回值//Application.ExternalCall("Unity2JavaScript",a,10,"aaaa");

Unity建议使用的方法

1.在Plugins文件夹中,创建后缀为.jslib的文件,在其中写需要调用的js代码

mergeInto(LibraryManager.library, {Hello:function() {window.alert("Hello, world!");  },HelloString:function(str) {window.alert(Pointer_stringify(str));  },PrintFloatArray:function(array, size) {for(vari =0; i < size; i++)console.log(HEAPF32[(array >>2) + size]);  },AddNumbers:function(x, y) {returnx + y;  },StringReturnValueFunction:function() {varreturnStr ="bla";varbuffer =_malloc(lengthBytesUTF8(returnStr) +1);writeStringToMemory(returnStr, buffer);returnbuffer;  },BindWebGLTexture:function(texture) {GLctx.bindTexture(GLctx.TEXTURE_2D,GL.textures[texture]);  },});

2.Unity中调用——__Internal.jslib

using UnityEngine;using System.Runtime.InteropServices;publicclassNewBehaviourScript:MonoBehaviour {    [DllImport("__Internal")]    privatestaticexternvoidHello();    [DllImport("__Internal")]    privatestaticexternvoidHelloString(stringstr);    [DllImport("__Internal")]    privatestaticexternvoidPrintFloatArray(float[]array,intsize);    [DllImport("__Internal")]    privatestaticexternintAddNumbers(intx,inty);    [DllImport("__Internal")]    privatestaticexternstringStringReturnValueFunction();    [DllImport("__Internal")]    privatestaticexternvoidBindWebGLTexture(inttexture);voidStart(){        Hello();                HelloString("This is a string.");float[] myArray = newfloat[10];        PrintFloatArray(myArray, myArray.Length);intresult = AddNumbers(5,7);        Debug.Log(result);                Debug.Log(StringReturnValueFunction());                var texture = new Texture2D(0,0, TextureFormat.ARGB32,false);        BindWebGLTexture(texture.GetNativeTextureID());    }}

新方法多了可以返回值,但是每次修改必须打包才能测试;

JavaScript调用Unity

这里面有巨坑,天坑,人都坑傻了!!!

官方文档中有这几行字

恰好我用的2020版本的Unity;

主要使用这个API——

SendMessage("游戏对象名","方法名","参数"); 这个和参数和lua调用c#差不多了,但是怎么调用这个api就很玄学了;

首先如果你调用这个方法需要在Unity的资源已经加载完成才可以,这个好解决,js加个button;

WebToUnity

其次在调用这个方法前需要先实例化UnityInstance变量;

vargameInstance =null;script.onload=() =>{gameInstance =createUnityInstance(document.querySelector("#unity-canvas"), {dataUrl:"Build/Test.data",frameworkUrl:"Build/Test.framework.js",codeUrl:"Build/Test.wasm",streamingAssetsUrl:"StreamingAssets",companyName:"DefaultCompany",productName:"UnityToWeb",productVersion:"0.1",      });};//以上的参数都可以在unity的playersetting界面找到;

最后调用时要在then中用lamda表达式

functionTestSend() {gameInstance.then((unityInstance) =>{unityInstance.SendMessage("Canvas","OnLogin","dqwreqweraf");        });}

完整的index.html

              Unity WebGL Player | UnityToWeb               

WebToUnity           

       

       
         
       
     


        WebGL builds are not supported on mobile devices.     


       

       
       
UnityToWeb
     


          var buildUrl = "Build";      var loaderUrl = buildUrl + "/Test.loader.js";      var config = {        dataUrl: buildUrl + "/Test.data",        frameworkUrl: buildUrl + "/Test.framework.js",        codeUrl: buildUrl + "/Test.wasm",        streamingAssetsUrl: "StreamingAssets",        companyName: "DefaultCompany",        productName: "UnityToWeb",        productVersion: "0.1",      };      var container = document.querySelector("#unity-container");      var canvas = document.querySelector("#unity-canvas");      var loadingBar = document.querySelector("#unity-loading-bar");      var progressBarFull = document.querySelector("#unity-progress-bar-full");      var fullscreenButton = document.querySelector("#unity-fullscreen-button");      var mobileWarning = document.querySelector("#unity-mobile-warning");      // By default Unity keeps WebGL canvas render target size matched with      // the DOM size of the canvas element (scaled by window.devicePixelRatio)      // Set this to false if you want to decouple this synchronization from      // happening inside the engine, and you would instead like to size up      // the canvas DOM size and WebGL render target sizes yourself.      // config.matchWebGLToCanvasSize = false;      if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {        container.className = "unity-mobile";        // Avoid draining fillrate performance on mobile devices,        // and default/override low DPI mode on mobile browsers.        config.devicePixelRatio = 1;        mobileWarning.style.display = "block";        setTimeout(() => {          mobileWarning.style.display = "none";        }, 5000);      } else {        canvas.style.width = "960px";        canvas.style.height = "600px";      }      loadingBar.style.display = "block";      var script = document.createElement("script");      script.src = loaderUrl;  var gameInstance = null;      script.onload = () => {gameInstance = createUnityInstance(document.querySelector("#unity-canvas"), {        dataUrl: "Build/Test.data",        frameworkUrl: "Build/Test.framework.js",        codeUrl: "Build/Test.wasm",        streamingAssetsUrl: "StreamingAssets",        companyName: "DefaultCompany",        productName: "UnityToWeb",        productVersion: "0.1",      });      };    function TestSend() {            gameInstance.then((unityInstance) => {            unityInstance.SendMessage("Canvas","OnLogin","dqwreqweraf");                    });          }        document.body.appendChild(script);     

更新新坑

Unity和JavaScript代码互相调用会有很严重的时序问题,比如需要在Unity场景加载完成开始调用的登录等方法;

不知道Unity打包成WebGL底层怎么处理的,我在单例的构造里写初始化会被多次执行,如果在这里调用js方法后果就是反复横跳导致栈内存爆炸;

解决办法是在Unity生命周期函数中调用(Awake或者Start)js函数,目的是通知加载完成,然后这个js函数中给Unity传登录信息(这里的登录信息可以是js从浏览器cookies获取的,unity做不到);

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容