齊諾網(wǎng)站建設(shè)成都私人做網(wǎng)站建設(shè)
參考文章:
1.Unity打包WebGL: 導(dǎo)入Vue
2.unity文檔-WebGL:與瀏覽器腳本交互
3.unity與vue交互(無第三方插件)
目錄
- 一、前期工作
- 1.新建.jslib文件
- 2.新建.cs腳本
- 3. 新建一個Text對象和button按鈕對象
- 4.添加腳本空對象UIEvent
- 5.導(dǎo)出unity為webgl
- 6.vue項目中引入unity打包的文件
- 二、從unity腳本調(diào)用js函數(shù)(vue發(fā)起,unity接收):
- 三、從js調(diào)用unity腳本函數(shù)(unity發(fā)起,vue接收):
- 四、結(jié)果展示
- 五、全部代碼
- JsTalker.cs
- communication.jslib
- 打包后的index.html
- vue頁面
一、前期工作
1.新建.jslib文件
操作步驟:
- 在unity項目的
Assets/Plugins
文件夾下新建一個txt文本文檔,保存并關(guān)掉文檔; - 將文檔命名為
communication.jslib
,保存; - 雙擊
communication.jslib
文件,用其它工具打開編輯即可(我這里用的是HBuilder),編輯內(nèi)容后面有介紹(三、從js調(diào)用unity腳本函數(shù))。
在 Unity 和 Vue 的交互中,
jslib
插件用于實現(xiàn) JavaScript 與 Unity 的橋接。它允許你在 JavaScript 中調(diào)用 Unity 的 C# 方法,或從 Unity 調(diào)用 JavaScript 函數(shù)。
以下是為什么需要 jslib
插件以及文件位置的重要性:
-
橋接功能:
jslib
插件使得 JavaScript 和 Unity 之間可以進行函數(shù)調(diào)用,這在處理 WebGL 平臺時尤其重要。Unity 的 WebGL 構(gòu)建運行在瀏覽器中,而瀏覽器環(huán)境與 Unity 的運行環(huán)境(C#)是隔離的,jslib
通過定義如何在兩者之間傳遞數(shù)據(jù)和調(diào)用方法來實現(xiàn)橋接。 -
函數(shù)調(diào)用:通過在
jslib
中定義的函數(shù),你可以從 JavaScript 直接調(diào)用 Unity 的 C# 方法,或者反向操作。這種方式簡化了兩者之間的通信,使得復(fù)雜的數(shù)據(jù)交換和操作變得可行。 -
文件位置:將
jslib
文件放置在 Unity 項目的Assets/Plugins
文件夾中的WebGL
子文件夾下是為了確保 Unity 能夠識別和正確加載這些插件文件。Unity 在構(gòu)建 WebGL 項目時,會自動將Plugins
文件夾中的jslib
文件包含在內(nèi),并按照預(yù)期執(zhí)行其中定義的 JavaScript 代碼。
這種結(jié)構(gòu)確保了 JavaScript 與 Unity 之間的高效且可靠的通信。
2.新建.cs腳本
操作步驟
- 在unity項目的
Assets
文件夾下新建一個script文件夾,在script
文件夾中新建一個C# Script腳本,命名為JsTalker.cs
;
當(dāng) Unity 項目導(dǎo)出為 WebGL 時,Unity 引擎會生成一些 JavaScript 文件,這些文件用于與瀏覽器進行交互。.cs 腳本可以與這些 JavaScript 文件集成,允許我們通過 Unity 的 C# 代碼來調(diào)用 JavaScript 函數(shù),或者從 JavaScript 函數(shù)中接收消息
3. 新建一個Text對象和button按鈕對象
操作步驟
-
在unity中的
Hierarchy
工作區(qū)中創(chuàng)建一個可回顯獲取數(shù)據(jù)的UI對象,這里以Text對象為例;新建button對象同理,并給button對象重命名為modelButton1
。
-
設(shè)置Text對象樣式為白色,目的是為了顯眼些。
4.添加腳本空對象UIEvent
操作步驟
- 在Hierarchy工作區(qū)中鼠標(biāo)右鍵 -
Create Empty
- 重命名GameObject為UIEvent
- 將JsTalker.cs
拖拽移入至UIEvent
對象里。 - 給
modelButton1
添加按鈕onClick點擊事件,為后面點擊按鈕傳參做準備;并添加JsTalker.cs
腳本。
5.導(dǎo)出unity為webgl
操作步驟
1. 打開構(gòu)建設(shè)置
- 在 Unity 編輯器中,點擊頂部菜單欄的 File。
- 選擇 Build Settings,這將打開構(gòu)建設(shè)置窗口。
2.選擇平臺
- 在構(gòu)建設(shè)置窗口中,會看到一個平臺列表。在這個列表中選擇 WebGL。
- 如果 WebGL 平臺尚未安裝,可以點擊 Add Open Scenes 或者在右下角點擊 Switch Platform 進行切換。Unity 會自動下載和安裝 WebGL 支持的必要組件。
3. 設(shè)置 Player 設(shè)置 - 在 Build Settings 窗口中,點擊 Player Settings 按鈕。這會打開 Inspector 面板中的 Player 設(shè)置。
- 在 Player Settings 面板中,我們可以配置 WebGL 特有的設(shè)置,如分辨率、質(zhì)量、圖標(biāo)等。
- Resolution and Presentation:設(shè)置 WebGL 輸出的分辨率和全屏模式。
- Other Settings:配置 WebGL 的各種參數(shù),例如內(nèi)存大小、腳本運行時等。
- Publishing Settings:設(shè)置壓縮和加密選項,以便優(yōu)化構(gòu)建的大小和性能。
4. 設(shè)置構(gòu)建目標(biāo) - 之后每次修改unity內(nèi)容都要做此打包操作
- 確保在 Build Settings 窗口中選擇了 WebGL 作為目標(biāo)平臺。
- 點擊 Build 按鈕,選擇一個保存位置,然后 Unity 會開始構(gòu)建過程。
構(gòu)建過程
- Unity 會將我們的項目打包為一個 WebGL 兼容的格式,并生成一個包含 HTML、JavaScript 和數(shù)據(jù)文件的文件夾。構(gòu)建過程可能需要一些時間,具體取決于項目的復(fù)雜性和你的計算機性能。生成的文件如下:
6.vue項目中引入unity打包的文件
操作步驟
- 在 Vue 項目根目錄的
public
文件夾 或static
文件夾下創(chuàng)建unity
文件夾。 - 將unity剛剛打包的文件復(fù)制到
public/unity
文件夾或static/unity
文件夾下。
之后每次修改unity內(nèi)容打包后都要做此復(fù)制操作
public
文件夾
用途:用于存放不會被 Webpack 處理的靜態(tài)資源,如 HTML 文件、favicon、直接引用的圖像等。
路徑:文件在 public 文件夾中的路徑將直接映射到構(gòu)建后的根目錄。例如,public/favicon.ico 在構(gòu)建后的項目中會變成 /favicon.ico。
處理:這些文件不會經(jīng)過 Webpack 處理,因此在開發(fā)和生產(chǎn)環(huán)境中都保持不變。
static
文件夾(通常在 Vue CLI 3.x 及更早版本中使用)
用途:主要用于存放靜態(tài)資源,Webpack 會將這些資源復(fù)制到構(gòu)建輸出目錄中。
路徑:static 文件夾中的資源將被 Webpack 處理并優(yōu)化。在構(gòu)建過程中,這些文件會被移動到構(gòu)建輸出目錄的 static 子目錄中,路徑會自動處理文件名的哈希值以便緩存優(yōu)化。
處理:這些文件會經(jīng)過 Webpack 的處理,比如版本控制和優(yōu)化。
二、從unity腳本調(diào)用js函數(shù)(vue發(fā)起,unity接收):
(全部代碼在后面可直接粘貼使用,這里針對主要模塊)
核心代碼
-
編輯JsTalker.cs腳本
//public TextMeshProUGUI uiText;//這個是新版的Text組件對象,用這個就要確保引用了 TextMeshPro 的命名空間using TMPro; public Text text;//這個是舊版的Text對象 public void SetToken(string token) {Debug.Log("token"+ token);text.text = token;//改變Text對象的文本內(nèi)容// 強制更新 UICanvas.ForceUpdateCanvases(); }
-
編輯打包后的index.html文件
// unity調(diào)用函數(shù)// vue發(fā)起 unity接收window.ReportReady = () => {send({id: 1,value: 2})}function send(obj) {unityInstance.SendMessage('modelButton1', 'SetToken', JSON.stringify(obj))}
SendMessage方法
SendMessage(objectName, methodName, value);
其中,objectName 是場景中的對象名稱;methodName 是當(dāng)前附加到該對象的腳本中的方法名稱;value 可以是字符串、數(shù)字,也可為空。
三、從js調(diào)用unity腳本函數(shù)(unity發(fā)起,vue接收):
(全部代碼在后面可直接粘貼使用,這里針對主要模塊)
核心代碼
-
unity中的
.jslib
定義方法GetButtonNameReady(string str)
,使用以下方法跨文檔傳遞,將參數(shù)發(fā)送到父窗口并定義type;GetButtonNameReady: function (string) {console.log("Click-buttondata:",string);// 發(fā)送消息到父窗口window.parent.postMessage({ type: 'UNITY_BUTTON_NAME', data: UTF8ToString(string) }, '*'); }
window.parent.postMessage(message, targetOrigin, [transfer]);
message: 要發(fā)送的消息,可以是字符串、對象或其他支持的數(shù)據(jù)類型。需要注意的是,發(fā)送的消息將會被序列化為 JSON 格式。
targetOrigin: 表示你希望消息發(fā)送到的目標(biāo)窗口的來源。這是一個安全機制,用于確保消息不會發(fā)送到不受信任的窗口。你可以指定特定的域(如 “https://example.com”)或者使用 “*” 作為通配符,表示允許所有來源。
transfer (可選): 一個可選的 Transferable 對象數(shù)組,用于將某些對象的所有權(quán)從當(dāng)前窗口轉(zhuǎn)移到目標(biāo)窗口。例如,MessagePort 對象可以用來在多個窗口之間傳遞消息。
- unity中的按鈕點擊事件里使用
JsTalker.cs
組件,腳本中添加點擊按鈕后傳按鈕名稱參數(shù);using System.Runtime.InteropServices; [DllImport("__Internal")] private static extern void GetButtonNameReady(string str); public void OnButtonClick(Button clickedButton) {// 獲取被點擊按鈕的名稱string buttonName = clickedButton.gameObject.name;Debug.Log("被點擊的按鈕名稱是: " + buttonName);GetButtonNameReady(buttonName);//為什么在頁面console里返回的是 48730928 ? }
- unity中選中
modelButton1
,設(shè)置其onClick添加UIEvent事件,并選擇GetButtonNameReady
方法
- vue頁面中使用監(jiān)聽message獲取按鈕名稱
'UNITY_BUTTON_NAME'
是在.jslib
文件中自定義的。export default {mounted() {window.addEventListener('message', this.handleMessage, false);},methods: {handleMessage(event) {// 檢查消息類型if (event.data.type === 'UNITY_BUTTON_NAME') {console.log('Received button name from Unity:', event.data.data);// 在此處理按鈕名稱}}},beforeDestroy() {window.removeEventListener('message', this.handleMessage, false);} }
四、結(jié)果展示
在vue頁面的控制臺中能獲得如下信息:
五、全部代碼
JsTalker.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;public class JsTalker : MonoBehaviour
{[DllImport("__Internal")]private static extern void SayHello();[DllImport("__Internal")]private static extern string ReportReady();[DllImport("__Internal")]private static extern void GetButtonNameReady(string str);//public TextMeshProUGUI uiText;public Text text;// Start is called before the first frame updatevoid Start(){ReportReady();}// Update is called once per framevoid Update(){if (Input.GetKeyUp(KeyCode.H)){SayHello();}}public void SetToken(string token){Debug.Log("token"+ token);text.text = token;// 強制更新 UICanvas.ForceUpdateCanvases();}// 這個方法會在按鈕點擊時被調(diào)用public void OnButtonClick(Button clickedButton){// 獲取被點擊按鈕的名稱string buttonName = clickedButton.gameObject.name;Debug.Log("被點擊的按鈕名稱是: " + buttonName);GetButtonNameReady(buttonName);//為什么在頁面console里返回的是 48730928 ?}
}
communication.jslib
mergeInto(LibraryManager.library, {SayHello: function () {window.alert("hello vue");},ReportReady: function() {window.ReportReady();},GetButtonNameReady: function (string) {console.log("Click-buttondata:",string);// 發(fā)送消息到父窗口window.parent.postMessage({ type: 'UNITY_BUTTON_NAME', data: UTF8ToString(string) }, '*');}
})
打包后的index.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 Player</title><link rel="shortcut icon" href="TemplateData/favicon.ico"><link rel="stylesheet" href="TemplateData/style.css">
</head><body><div id="unity-container" class="unity-desktop" style="width:100%;height: 100%;"><canvas id="unity-canvas" width=auto height=auto tabindex="-1"></canvas><div id="unity-loading-bar"><div id="unity-logo"></div><div id="unity-progress-bar-empty"><div id="unity-progress-bar-full"></div></div></div><div id="unity-warning"> </div><div id="unity-footer"><div id="unity-webgl-logo"></div><div id="unity-fullscreen-button"></div><div id="unity-build-title">HzevtSystem</div></div></div><script>// var UnityToJs = {// /// <summary>// /// JS的監(jiān)聽方法,// /// 點擊 xxxx 時,回傳默認值。// JS_OnReceiveView: function (fovMin, fovMax, angleMin, angleMax) {// // console.log(fovMin, fovMax, angleMin, angleMax);// window.top.dispatchEvent(new CustomEvent('getFovDef', { detail: { 'fovMin': fovMin, 'fovMax': fovMax, 'angleMin': angleMin, 'angleMax': angleMax } }))//自定義事件,然后獲取相應(yīng)的數(shù)據(jù)// }// }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 warningBanner = document.querySelector("#unity-warning");// Shows a temporary message banner/ribbon for a few seconds, or// a permanent error message on top of the canvas if type=='error'.// If type=='warning', a yellow highlight color is used.// Modify or remove this function to customize the visually presented// way that non-critical warnings and error messages are presented to the// user.function unityShowBanner(msg, type) {function updateBannerVisibility() {warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';}var div = document.createElement('div');div.innerHTML = msg;warningBanner.appendChild(div);if (type == 'error') div.style = 'background: red; padding: 10px;';else {if (type == 'warning') div.style = 'background: yellow; padding: 10px;';setTimeout(function () {warningBanner.removeChild(div);updateBannerVisibility();}, 5000);}updateBannerVisibility();}var buildUrl = "Build";var loaderUrl = buildUrl + "/unityweb.loader.js";var config = {dataUrl: buildUrl + "/unityweb.data",frameworkUrl: buildUrl + "/unityweb.framework.js",codeUrl: buildUrl + "/unityweb.wasm",streamingAssetsUrl: "StreamingAssets",companyName: "DefaultCompany",productName: "HzevtSystem",productVersion: "1.0",showBanner: unityShowBanner,};// 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)) {// Mobile device style: fill the whole browser client area with the game canvas:var meta = document.createElement('meta');meta.name = 'viewport';meta.content = 'width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes';document.getElementsByTagName('head')[0].appendChild(meta);container.className = "unity-mobile";canvas.className = "unity-mobile";// To lower canvas resolution on mobile devices to gain some// performance, uncomment the following line:// config.devicePixelRatio = 1;canvas.style.width = window.innerWidth + 'px';canvas.style.height = window.innerHeight + 'px';unityShowBanner('暫不支持移動端');} else {// Desktop style: Render the game canvas in a window that can be maximized to fullscreen:canvas.style.width = "100%";canvas.style.height = "100%";}loadingBar.style.display = "block";var script = document.createElement("script");script.src = loaderUrl;script.onload = () => {createUnityInstance(canvas, config, (progress) => {progressBarFull.style.width = 100 * progress + "%";}).then((unityInstance) => {console.log('unityInstance', unityInstance);console.log('window', window);// 綁定unityInstancewindow.unityInstance = unityInstance;console.log('window.unityInstance', window.unityInstance);loadingBar.style.display = "none";fullscreenButton.onclick = () => {unityInstance.SetFullscreen(1);};}).catch((message) => {alert(message);});};document.body.appendChild(script);// unity調(diào)用函數(shù)// vue發(fā)起 unity接收window.ReportReady = () => {// window.top.dispatchEvent(new CustomEvent())send({id: 1,value: 2})}function send(obj) {unityInstance.SendMessage('modelButton1', 'SetToken', JSON.stringify(obj))}</script>
</body></html>
vue頁面
<template><div style="width: 100%; height: 100%"><div @click="send">給unity發(fā)送數(shù)據(jù)</div><iframe ref="iframe" width="100%" height="100%" scrolling="no" src="/static/Unitys/web/index.html" frameborder="0"></iframe></div>
</template><script>
export default {name: "testUnityAScene",data() {return {nodeList: [{ id: 11, name: "node1" },{ id: 22, name: "node2" },{ id: 33, name: "node3" },],};},mounted() {console.log('this.$refs.iframe.contentWindow', this.$refs.iframe.contentWindow);// this.$refs.iframe.contentWindow.unityInstance.SendMessage('WebInvoker', 'Unity_InsertNaviPoint', this.nodeList.length);window.addEventListener('message', this.handleMessage, false);},methods: {send() {// 發(fā)送數(shù)據(jù)this.$refs.iframe.contentWindow.send({id: 111,value: 222})},handleMessage(event) {console.log('event11111111111:',event);// 檢查消息類型if (event.data.type === 'UNITY_BUTTON_NAME') {console.log('Received button name from Unity:', event.data.data);// 在此處理按鈕名稱}}},beforeDestroy() {window.removeEventListener('message', this.handleMessage, false);},
};
</script><style>
</style>