javascript調用unity

WebGL:與瀏覽器腳本交互

構建適用于 Web 的內容時,可能需要與網頁上的其他元素進行通信?;蛘撸赡芟M褂?Unity 當前在默認情況下未公開的 Web API 來實現功能。在這兩種情況下,都需要直接與瀏覽器的 JavaScript 引擎連接。Unity WebGL 提供了不同的方法來執(zhí)行此操作。

從 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 腳本發(fā)送一些數據或通知。建議的做法是調用內容中的游戲對象上的方法。如果要從嵌入在項目中的 JavaScript 插件執(zhí)行調用,可使用以下代碼:

unityInstance.SendMessage(objectName, methodName, value);

其中,__objectName__ 是場景中的對象名稱;__methodName__ 是當前附加到該對象的腳本中的方法名稱;__value__ 可以是字符串、數字,也可為空。例如:

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

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

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

如果希望從嵌入頁面的全局作用域內執(zhí)行調用,請參閱下面的代碼可見性部分。

從 Unity 腳本調用 C 函數

由于 Unity 使用 emscripten 將源代碼從 C/C++ 代碼編譯為 JavaScript,因此您也可以使用 C/C++ 代碼編寫插件,并從 C# 調用這些函數。因此,您可以不使用上面示例中的 jslib 文件,而是在項目中使用 C/C++ 文件;它將自動使用您的腳本實現編譯,并且您可以從中調用函數,就像上面的 JavaScript 示例一樣。

如果使用 C++ (.cpp) 來實現該插件,則必須確保使用 C 鏈接來聲明函數以免發(fā)生名稱錯用問題:

# 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 開始,所有構建代碼都在自己的范圍內執(zhí)行。這種方法可以將內容嵌入任意頁面,而不會與嵌入頁面代碼發(fā)生沖突,并且可以在同一頁面上嵌入多個構建版本。

如果項目中包含?.jslib?插件形式的所有 JavaScript 代碼,則此 JavaScript 代碼的運行作用域將與編譯的構建相同,并且代碼應該與 Unity 先前版本中的工作方式非常相似(例如,以下對象和函數應該在 JavaScript 插件代碼中直接可見:Module、SendMessage、HEAP8、ccall 等)。

但是,如果計劃從嵌入頁面的全局作用域調用內部 JavaScript 函數,則必須在 WebGL 模板 index.html 中使用?unityInstance?變量。在 Unity 引擎實例化成功后執(zhí)行此操作,例如:

? var myGameInstance = null;

? ? script.onload = () => {

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

? ? ? ? myGameInstance = unityInstance;

? ? ? ? …

然后可以使用?myGameInstance.SendMessage()?向構建發(fā)送消息,或使用?myGameInstance.Module?訪問構建 Module 對象。


方案如下:

1.在Unity場景中有一個GameObject,我們命名為A,

A上有C#腳本,里面有個方法

public void Func(string str)

{

//處理邏輯

}

2.在發(fā)布出的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創(chuàng)建一個unity的容器實例,再通過容器實例給游戲對象A發(fā)送一個調用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文件夾中,創(chuàng)建后綴為.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底層怎么處理的,我在單例的構造里寫初始化會被多次執(zhí)行,如果在這里調用js方法后果就是反復橫跳導致棧內存爆炸;

解決辦法是在Unity生命周期函數中調用(Awake或者Start)js函數,目的是通知加載完成,然后這個js函數中給Unity傳登錄信息(這里的登錄信息可以是js從瀏覽器cookies獲取的,unity做不到);

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容