> 文章列表 > Unity3D打包WebGL并使用MQTT(二):使用json

Unity3D打包WebGL并使用MQTT(二):使用json

Unity3D打包WebGL并使用MQTT(二):使用json

Unity3D打包WebGL并使用MQTT(二):使用json

1. 软件环境

  • Unity: 2021.3
  • stomp.js 2.3.3:
    下载地址:https://www.jsdelivr.com/package/npm/stompjs

2. 内容介绍

这篇博客的主要内容是记录将一个Unity项目打包成WebGL项目,并集成MQTT进行json数据传输的过程。

3. 项目搭建

3.1 UI界面

Unity3D打包WebGL并使用MQTT(二):使用json
Unity3D打包WebGL并使用MQTT(二):使用json
Unity3D打包WebGL并使用MQTT(二):使用json

3.2 添加插件

  1. 添加WebGLInput插件

用于解决WebGL中输入框无法输入/显示中文的问题
详情参考:
Unity WebGL 输入框(InputField)接受中文输入
unity在webgl端 输入框无法输入中文和中文显示问题的解决
下载地址: 使用github包【WebGLInput】:
https://github.com/kou-yeung/WebGLInput

Unity3D打包WebGL并使用MQTT(二):使用json
2. 添加系统中文字体

将系统中的字体文件导入Unity
详情参考:Unity3D添加使用系统中的字体

Unity3D打包WebGL并使用MQTT(二):使用json
Unity3D打包WebGL并使用MQTT(二):使用json

  1. 修改InputField中文显示和字体
    Unity3D打包WebGL并使用MQTT(二):使用json

Unity3D打包WebGL并使用MQTT(二):使用json
Unity3D打包WebGL并使用MQTT(二):使用json

  1. 添加Pathfinding.JsonFx.dll

用于进行json数据的序列化和反序列化
JsonFX Unity3D 如何使用JsonFX

Unity3D打包WebGL并使用MQTT(二):使用json

3.3 添加脚本

  1. .jslib文件

详细内容参考:Unity(WebGL)与JS通讯2022最新姿势
Unity3D打包WebGL并使用MQTT(二):使用jsonUnity3D打包WebGL并使用MQTT(二):使用json
Unity3D打包WebGL并使用MQTT(二):使用json

mergeInto(LibraryManager.library, {Hello: function () {window.alert("Hello, world!");},HelloString: function (str) {// window.alert(Pointer_stringify(str));window.alert(UTF8ToString(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]);},JsConnect: function (clientId, host, port, username, password, objName, objFunc) {mqttConnect(UTF8ToString(clientId), UTF8ToString(host), UTF8ToString(port), UTF8ToString(username), UTF8ToString(password), UTF8ToString(objName), UTF8ToString(objFunc));},JsSubscribe: function (clientId, name, objName, objFunc) {mqttSubscribe(UTF8ToString(clientId), UTF8ToString(name), UTF8ToString(objName), UTF8ToString(objFunc))},JsSend: function (clientId, name, payload) {mqttSend(UTF8ToString(clientId), UTF8ToString(name), UTF8ToString(payload))},JsUnsubscribe: function(clientId, name) {mqttUnsubscribe(UTF8ToString(clientId), UTF8ToString(name));},JsDisconnect: function(clientId) {mqttDisconnect(UTF8ToString(clientId), UTF8ToString(clientId));}
});
  1. Cube添加脚本Cube.cs

用于显示基本函数功能

Unity3D打包WebGL并使用MQTT(二):使用json
Unity3D打包WebGL并使用MQTT(二):使用json
Unity3D打包WebGL并使用MQTT(二):使用json

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;public class Cube : 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);[System.Obsolete]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());}
}
  1. 数据类Result.cs

用于json数据的序列化和反序列化

Unity3D打包WebGL并使用MQTT(二):使用json
Unity3D打包WebGL并使用MQTT(二):使用json

[System.Serializable]
public class Result {public int errCode;public string errInfo;public object data;
}
  1. 数据类Mqtt.cs

用于配置MQTT相关内容

using System.Collections.Generic;
using System.Runtime.InteropServices;[System.Serializable]
public class Mqtt {public string host;public string port;public string clientId;public string username;public string password;public string objName;public string objFunc;public List<Result> resultList;[DllImport("__Internal")]private static extern void JsConnect(string clientId, string host, string port, string username, string password, string objName, string objFunc);[DllImport("__Internal")]private static extern void JsSubscribe(string clientId, string topic, string objName, string objFunc);[DllImport("__Internal")]private static extern void JsSend(string clientId, string topic, string payload);[DllImport("__Internal")]private static extern void JsUnsubscribe(string clientId, string topic);[DllImport("__Internal")]private static extern void JsDisconnect(string clientId);public void Connect() {JsConnect(clientId, host, port, username, password, objName, objFunc);}public void SubscribeTopic(string topic) {topic = "/topic/" + topic;JsSubscribe(clientId, topic, objName, objFunc);}public void UnscbscribeTopic(string topic) {topic = "/topic/" + topic;JsUnsubscribe(clientId, topic);}public void SendTopic(string topic, string payload) {topic = "/topic/" + topic;JsSend(clientId, topic, payload);}public void SubscriveQueue(string queue) {queue = "/queue/" + queue;JsSubscribe(clientId, queue, objName, objFunc);}public void UnsubscribeQueue(string queue) {queue = "/queue/" + queue;JsUnsubscribe(clientId, queue);}public void SendQueue(string queue, string payload) {queue = "/queue/" + queue;JsSend(clientId, queue, payload);}public void Disconnect() {JsDisconnect(clientId);}
}
  1. Canvas添加脚本PanelController_2.cs

用于测试mqtt相关函数

Unity3D打包WebGL并使用MQTT(二):使用json
Unity3D打包WebGL并使用MQTT(二):使用json

Unity3D打包WebGL并使用MQTT(二):使用json

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using Newtonsoft.Json;public class PanelController_2 : MonoBehaviour
{public Button connectBtn;public Button subscribeBtn;public Button sendBtn;public Button unsubscribeBtn;public Button disconnectBtn;private InputField hostInput;private InputField portInput;private InputField clientIdInput;private InputField usernameInput;private InputField passwordInput;private InputField destinationInput;private InputField topicInput;private InputField messageInput;private Text scrollLogText;private Mqtt mqtt;// Start is called before the first frame updatevoid Start(){connectBtn.onClick.AddListener(HandleConnect);subscribeBtn.onClick.AddListener(HandleSubscribe);sendBtn.onClick.AddListener(HandleSend);unsubscribeBtn.onClick.AddListener(HandleUnsubscribe);disconnectBtn.onClick.AddListener(HandleDisconnect);foreach (UnityEngine.UI.InputField textInput in GetComponentsInChildren<UnityEngine.UI.InputField>()) {// Debug.Log(textInput.name);switch (textInput.name) {case "host_input": {hostInput = textInput;break;}case "port_input": {portInput = textInput;break;}case "client_id_input": {clientIdInput = textInput;break;}case "username_input": {usernameInput = textInput;break;}case "password_input": {passwordInput = textInput;break;}case "destination_input": {destinationInput = textInput;break;}case "topic_input": {topicInput = textInput;break;}case "message_input": {messageInput = textInput;break;}}}foreach (Text textItem in GetComponentsInChildren<Text>()) {switch (textItem.name) {case "scroll_log_text": {scrollLogText = textItem;break;}}}mqtt = new Mqtt();}void HandleConnect() {Debug.Log("unity: connect");mqtt.host = hostInput.text;mqtt.port = portInput.text;mqtt.clientId = clientIdInput.text;mqtt.username = usernameInput.text;mqtt.password = passwordInput.text;mqtt.resultList = new List<Result>();mqtt.objName = name;mqtt.objFunc = "HandleMqttMessage";mqtt.Connect();}void HandleSubscribe() {Debug.Log("unity: subscribe");string topic = topicInput.text;mqtt.SubscribeTopic(topic);}void HandleSend() {Debug.Log("unity: send");string topic = topicInput.text;string payload = messageInput.text;mqtt.SendTopic(topic, payload);}void HandleUnsubscribe() {Debug.Log("unity: unsubscribe");string topic = topicInput.text;mqtt.UnscbscribeTopic(topic);}void HandleDisconnect() {Debug.Log("unity: disconnect");mqtt.Disconnect();}void HandleMqttMessage(string message) {Debug.Log("unity: get message");SetLogScroll(message);Result result = JsonConvert.DeserializeObject<Result>(message);mqtt.resultList.Add(result);}void SetLogScroll(string log) {scrollLogText.text += "\\n" + log;}
}

3.4 构建WebGL

  1. 选择平台
    Unity3D打包WebGL并使用MQTT(二):使用json
    Unity3D打包WebGL并使用MQTT(二):使用json
  2. 配置
  • 分辨率设置
    Unity3D打包WebGL并使用MQTT(二):使用json
  • image设置
    Unity3D打包WebGL并使用MQTT(二):使用json
  • 其他设置
    Unity3D打包WebGL并使用MQTT(二):使用json
    Unity3D打包WebGL并使用MQTT(二):使用json
    Unity3D打包WebGL并使用MQTT(二):使用json
  • 发布设置
    Unity3D打包WebGL并使用MQTT(二):使用json
  1. 构建
    Unity3D打包WebGL并使用MQTT(二):使用json
    Unity3D打包WebGL并使用MQTT(二):使用json

Unity3D打包WebGL并使用MQTT(二):使用json

Unity3D打包WebGL并使用MQTT(二):使用json

3.5 集成MQTT功能

3.5.1 版本一: 单文件实现

  1. 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 | Web Demo</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"><canvas id="unity-canvas" width=960 height=600></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">Web Demo</div></div></div><!-- 引入stomp.min.js --><script src="Plugins/stomp.min.js"></script><script>var mqttClient = {}var subscribeIdObj = {}function mqttConnect(clientId, host, port, username, password, objName, objFunc) {// clientId不能重复if (checkClientIdExists(clientId)) {console.log('html: clientId重复, 不能连接');alert('clientId重复, 不能连接')return}let url = 'ws://' + host + ':' + port + '/stomp'console.log("html: connect " + url);// 创建一个client实例mqttClient[clientId] = {host: host,port: port,username: username,password: password,objName: objName,objFunc: objFunc,client: null,subscribe: {topic: {},queue: {}}}mqttClient[clientId]['client'] = Stomp.client(url)console.log(mqttClient)let headers = {login: username,passcode: password,'client-id': clientId}mqttClient[clientId]['client'].connect(headers, () => {console.log('connect success');sendMessage(clientId, {errCode: 0,errInfo: 'connect success'})})}// 检查clientId 是否已存在function checkClientIdExists(clientId) {if (clientId in mqttClient) {return true} else {return false}}// 检查订阅类型是topic还是queuefunction checkMessageType(name) {// 检查订阅类型是topic还是queueif (name.search(/^\\/topic\\/.*/gm) >= 0) {// topicreturn 1} else if (name.search(/^\\/queue\\/.*/gm) >= 0) {//  queuereturn 0} else {// stomp默认是queuereturn -1}}// 订阅消息function mqttSubscribe(clientId, name, objName, objFunc) {if (!checkClientIdExists(clientId)) {alert('clientId 不存在, 无法订阅')return}let messageType = checkMessageType(name)if (messageType < 0) {console.log('消息类型不符合要求, /topic/* 或者 /queue/*');alert('消息类型不符合要求, /topic/* 或者 /queue/*')return}console.log("html: subscribe " + name);sendMessage(clientId, {errCode: 0,errInfo: "html: subscribe " + name})// 订阅let subscribeId = mqttClient[clientId]['client'].subscribe(name, message => {if (message.body) {console.log('message body: ' + message.body)// 将接收到的消息发送给unity对象sendMessage(clientId, {errCode: 0,errInfo: 'message body: ' + message.body})} else {console.log('empty message')sendMessage(clientId, {errCode: 0,errInfo: 'empty message'})}})// 保存if (messageType === 1) {mqttClient[clientId]['subscribe']['topic'][name] = subscribeId} else if (messageType === 0) {mqttClient[clientId]['subscribe']['queue'][name] = subscribeId}}// 发送消息function mqttSend(clientId, name, payload) {if (!checkClientIdExists(clientId)) {alert('clientId 不存在, 无法发送')return}let messageType = checkMessageType(name)if (messageType < 0) {console.log('消息类型不符合要求, /topic/* 或者 /queue/*');alert('消息类型不符合要求, /topic/* 或者 /queue/*')return}console.log("html: send " + name + ", " + payload);sendMessage(clientId, {errCode: 0,errInfo: "html: send " + name + ", " + payload})let headers = {}mqttClient[clientId]['client'].send(name, headers, payload)}// 取消订阅function mqttUnsubscribe(clientId, name) {if (!checkClientIdExists(clientId)) {alert('clientId 不存在, 无法取消订阅')return}let messageType = checkMessageType(name)if (messageType < 0) {console.log('消息类型不符合要求, /topic/* 或者 /queue/*');alert('消息类型不符合要求, /topic/* 或者 /queue/*')return}if (messageType === 1) {// topicif (name in mqttClient[clientId]['subscribe']['topic']) {mqttClient[clientId]['subscribe']['topic'][name].unsubscribe();} else {alert(`未订阅此消息 ${name}`)}} else if (messageType === 0) {// queueif (name in mqttClient[clientId]['subscribe']['queue']) {mqttClient[clientId]['subscribe']['queue'][name].unsubscribe();} else {alert(`未订阅此消息 ${name}`)}}console.log("html: unsubscribe " + name);sendMessage(clientId, {errCode: 0,errInfo: "html: unsubscribe " + name})}// 断开连接function mqttDisconnect(clientId) {if (!checkClientIdExists(clientId)) {alert('clientId 不存在')return}mqttClient[clientId]['client'].disconnect(() => {console.log("html: disconnect " + clientId);// unityInstance.SendMessage('Canvas', 'SetLogScroll', 'html: disconnect ' + clientId)sendMessage(clientId, {errCode: 0,errInfo: 'html: disconnect ' + clientId})// 删除clientIddelete mqttClient[clientId]})}// 发送消息function sendMessage(clientId, data) {unityInstance.SendMessage(mqttClient[clientId]['objName'], mqttClient[clientId]['objFunc'], JSON.stringify(data))}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 + "/Web2.loader.js";var config = {dataUrl: buildUrl + "/Web2.data",frameworkUrl: buildUrl + "/Web2.framework.js",codeUrl: buildUrl + "/Web2.wasm",streamingAssetsUrl: "StreamingAssets",companyName: "DefaultCompany",productName: "Web Demo",productVersion: "0.1",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;unityShowBanner('WebGL builds are not supported on mobile devices.');} else {// Desktop style: Render the game canvas in a window that can be maximized to fullscreen:canvas.style.width = "960px";canvas.style.height = "600px";}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) => {loadingBar.style.display = "none";window.unityInstance = unityInstancefullscreenButton.onclick = () => {unityInstance.SetFullscreen(1);};}).catch((message) => {alert(message);});};document.body.appendChild(script);</script></body>
</html>

3.5.2 版本二: 多文件实现

  1. 添加iceMqtt.js文件

抽象MQTT功能

import './stomp.min.js'
var iceMqtt = {mqttClient: {},unityInstance: null,// 创建一个对象mqttCreateClient: function(clientId, host, port, username, password, objName, objFunc) {// clientId不能重复if (this.checkClientIdExists(clientId)) {console.log(`clientId重复, 不能创建 ${clientId}`);return}// 创建一个client实例this.mqttClient[clientId] = {host: host,port: port,username: username,password: password,client: null,subscribe: {topic: {},queue: {}},objName: objName,objFunc: objFunc}console.log('iceMqtt.js : ', this.mqttClient)},// 进行连接mqttConnect: function(clientId) {// clientId需要存在if (!this.checkClientIdExists(clientId)) {console.log(`clientId不存在, 不能连接 ${clientId}, 请创建client实例`);return}let url = 'ws://' + this.mqttClient[clientId]['host'] + ':' + this.mqttClient[clientId]['port'] + '/stomp'console.log(`connect url is ${url}`);this.mqttClient[clientId]['client'] = Stomp.client(url)let headers = {login: this.mqttClient[clientId]['username'],passcode: this.mqttClient[clientId]['password'],'client-id': clientId}this.mqttClient[clientId]['client'].connect(headers, () => {console.log('connect success');this.sendMessage(clientId, {errCode: 0,errInfo: `${clientId} connect success`})})},// 检查clientId 是否已存在checkClientIdExists: function(clientId) {if (clientId in this.mqttClient) {return true} else {return false}},// 检查订阅类型是topic还是queuecheckMessageType: function(name) {// 检查订阅类型是topic还是queueif (name.search(/^\\/topic\\/.*/gm) >= 0) {// topicreturn 1} else if (name.search(/^\\/queue\\/.*/gm) >= 0) {//  queuereturn 0} else {// stomp默认是queuereturn -1}},// 订阅消息mqttSubscribe: function(clientId, name, messageCallback) {if (!this.checkClientIdExists(clientId)) {console.log(`clientId 不存在, 没有连接服务器, 请连接`)return}let messageType = this.checkMessageType(name)if (messageType < 0) {console.log('消息类型不符合要求, /topic/* 或者 /queue/*');return}// 订阅let subscribeId = this.mqttClient[clientId]['client'].subscribe(name, messageCallback)// 保存if (messageType === 1) {this.mqttClient[clientId]['subscribe']['topic'][name] = subscribeId} else if (messageType === 0) {this.mqttClient[clientId]['subscribe']['queue'][name] = subscribeId}console.log(`${clientId} 订阅了 ${name}`)this.sendMessage(clientId, {errCode: 0,errInfo: `${clientId} 订阅了 ${name}`})},// 发送消息mqttSend: function(clientId, name, payload) {if (!this.checkClientIdExists(clientId)) {console.log('clientId 不存在, 无法发送')return}let messageType = this.checkMessageType(name)if (messageType < 0) {console.log('消息类型不符合要求, /topic/* 或者 /queue/*');return}let headers = {}this.mqttClient[clientId]['client'].send(name, headers, payload)console.log(`${clientId}${name} 发送了 ${payload}`)this.sendMessage(clientId, {errCode: 0,errInfo: `${clientId}${name} 发送了 ${payload}`})},// 取消订阅mqttUnsubscribe: function(clientId, name) {if (!this.checkClientIdExists(clientId)) {console.log(`clientId 不存在, 无法取消订阅 ${name}`)return}let messageType = this.checkMessageType(name)if (messageType < 0) {console.log('消息类型不符合要求, /topic/* 或者 /queue/*');return}if (messageType === 1) {// topicif (name in this.mqttClient[clientId]['subscribe']['topic']) {this.mqttClient[clientId]['subscribe']['topic'][name].unsubscribe();} else {console.log(`未订阅此消息 ${name}`)}} else if (messageType === 0) {// queueif (name in this.mqttClient[clientId]['subscribe']['queue']) {this.mqttClient[clientId]['subscribe']['queue'][name].unsubscribe();} else {console.log(`未订阅此消息 ${name}`)}}console.log(`${clientId} 取消订阅 ${name}`)this.sendMessage(clientId, {errCode: 0,errInfo: `${clientId} 取消订阅 ${name}`})},// 断开连接mqttDisconnect: function(clientId) {if (!this.checkClientIdExists(clientId)) {console.log('clientId 不存在')return}this.mqttClient[clientId]['client'].disconnect(() => {console.log(`${clientId} 断开连接`);this.sendMessage(clientId, {errCode: 0,errInfo: `${clientId} 断开连接`})// 删除clientIddelete this.mqttClient[clientId]})},// 执行unity默认对象的默认方法sendMessage: function(clientId, data) {this.unityInstance.SendMessage(this.mqttClient[clientId]['objName'], this.mqttClient[clientId]['objFunc'], JSON.stringify(data))}
}export { iceMqtt }
  1. index.html文件

测试MQTT功能
html文件引入其他js文件的格式,
具体参考: webGl使用jsLib与Js交互
Unity3D打包WebGL并使用MQTT(二):使用json

<!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 | Web Demo</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"><canvas id="unity-canvas" width=960 height=600></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">Web Demo</div></div></div><script>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 + "/Web2.loader.js";var config = {dataUrl: buildUrl + "/Web2.data",frameworkUrl: buildUrl + "/Web2.framework.js",codeUrl: buildUrl + "/Web2.wasm",streamingAssetsUrl: "StreamingAssets",companyName: "DefaultCompany",productName: "Web Demo",productVersion: "0.1",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;unityShowBanner('WebGL builds are not supported on mobile devices.');} else {// Desktop style: Render the game canvas in a window that can be maximized to fullscreen:canvas.style.width = "960px";canvas.style.height = "600px";}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) => {loadingBar.style.display = "none";// 挂载unityInstance对象window.unityInstance = unityInstanceiceMqtt.unityInstance = unityInstanceconsole.log("unity load", window.iceMqtt)console.log('iceMqtt : ', iceMqtt)fullscreenButton.onclick = () => {unityInstance.SetFullscreen(1);};}).catch((message) => {alert(message);});};document.body.appendChild(script);</script><script type="module">// 先执行import { iceMqtt } from './Plugins/iceMqtt.js'window.onload = () => {window.iceMqtt = iceMqttconsole.log("module iceMqtt", window.iceMqtt)}// window.iceMqtt = iceMqtt</script><script>// 连接function mqttConnect(clientId, host, port, username, password, objName, objFunc) {// 创建一个mqttClienticeMqtt.mqttCreateClient(clientId, host, port, username, password, objName, objFunc)// 检查clientIdif (!iceMqtt.checkClientIdExists(clientId)) {console.log(`clientId ${clientId} 创建失败`)return}console.log('iceMqtt mqttClient: ', iceMqtt.mqttClient)// 进行连接, 创建一个client实例iceMqtt.mqttConnect(clientId)}// 订阅消息function mqttSubscribe(clientId, name, objName, objFunc) {iceMqtt.mqttSubscribe(clientId, name, message => {if (message.body) {console.log(`get message : ${message.body}`)unityInstance.SendMessage(objName, objFunc, JSON.stringify({errCode: 0,errInfo: `get message : ${message.body}`}))} else {console.log(`get empty message ...`)unityInstance.SendMessage(objName, objFunc, JSON.stringify({errCode: 0,errInfo: `get empty message ...`}))}})}// 发送消息function mqttSend(clientId, name, payload) {iceMqtt.mqttSend(clientId, name, payload)}// 取消订阅function mqttUnsubscribe(clientId, name) {iceMqtt.mqttUnsubscribe(clientId, name)}// 断开连接function mqttDisconnect(clientId) {iceMqtt.mqttDisconnect(clientId)}</script></body>
</html>

4. 测试

4.1 版本一:

Unity3D打包WebGL并使用MQTT(二):使用json

  1. 连接
    Unity3D打包WebGL并使用MQTT(二):使用json

  2. 订阅
    Unity3D打包WebGL并使用MQTT(二):使用json

  3. 发送消息
    Unity3D打包WebGL并使用MQTT(二):使用json

  4. 取消订阅
    Unity3D打包WebGL并使用MQTT(二):使用json
    Unity3D打包WebGL并使用MQTT(二):使用json

  5. 断开连接
    Unity3D打包WebGL并使用MQTT(二):使用json

4.2 版本二:

Unity3D打包WebGL并使用MQTT(二):使用json

  1. 连接
    Unity3D打包WebGL并使用MQTT(二):使用json

  2. 订阅
    Unity3D打包WebGL并使用MQTT(二):使用json

  3. 发送消息
    Unity3D打包WebGL并使用MQTT(二):使用json

  4. 取消订阅
    Unity3D打包WebGL并使用MQTT(二):使用json
    Unity3D打包WebGL并使用MQTT(二):使用json

  5. 断开连接
    Unity3D打包WebGL并使用MQTT(二):使用json

5. 问题

  1. 使用<script type="module"></script>后,.jslib文件无法发现内部的函数
    解决办法:

参考: 不同js的加载顺序 js文件模块化引用问题(JavaScript modules)
Unity3D打包WebGL并使用MQTT(二):使用json

考虑将module中的内容挂载到window

	......<script type="module">// 先执行import { iceMqtt } from './Plugins/iceMqtt.js'window.onload = () => {window.iceMqtt = iceMqttconsole.log("module iceMqtt", window.iceMqtt)}// window.iceMqtt = iceMqtt</script>......

X. 参考

  1. 查找物体和组件
    Unity 之 查找游戏物体的几种方式解析
    Unity 常用API之Component,GameObject获取组件

  2. Unity与Js互相调用
    Unity WebGL C#调用JS脚本
    unity开发webGL,引用js功能。
    Unity(WebGL)与JS通讯2022最新姿势
    webGl使用jsLib与Js交互

  3. Unity在WebGL中InputField无法输入中文
    Unity WebGL 输入框(InputField)接受中文输入
    unity在webgl端 输入框无法输入中文和中文显示问题的解决
    Unity3D添加使用系统中的字体

  4. unityInstance is not defined
    [Unity转小游戏]微信开发者工具/微信小游戏中找不到unityInstance.(unityInstance is not defined)

  5. 其他
    2021-09-29 Unity WebGL平台开发遇到的坑

  6. Unity构建WebGL
    Unity-WebGL-打包流程以及遇到的各种坑
    unity打包webgl 部署到本地Web服务器
    【Unity】打包WebGL项目遇到的问题及解决记录

  7. Unity使用JsonFx
    JsonFX Unity3D 如何使用JsonFX

  8. Unity WebGL常见错误
    unity webgl踩坑指南

  9. This value was evaluated upon firstexpanding. It may have changed since then…
    This value was evaluated upon firstexpanding. It may have changed since then…

  10. JS export
    JS 之export、export default和module.exports

  11. scritp 使用type=“module”
    在index.html中直接调用import,export需要注意的事项
    js文件模块化引用问题(JavaScript modules)