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
界面
3.2 添加插件
- 添加
WebGLInput
插件
用于解决
WebGL
中输入框无法输入/显示中文的问题
详情参考:
Unity WebGL 输入框(InputField)接受中文输入
unity在webgl端 输入框无法输入中文和中文显示问题的解决
下载地址: 使用github包【WebGLInput】:
https://github.com/kou-yeung/WebGLInput
2. 添加系统中文字体
将系统中的字体文件导入
Unity
详情参考:Unity3D添加使用系统中的字体
- 修改
InputField
中文显示和字体
- 添加
Pathfinding.JsonFx.dll
用于进行
json
数据的序列化和反序列化
JsonFX Unity3D 如何使用JsonFX
3.3 添加脚本
.jslib文件
详细内容参考:Unity(WebGL)与JS通讯2022最新姿势
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));
}
});
- 给
Cube
添加脚本Cube.cs
用于显示基本函数功能
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());
}
}
- 数据类
Result.cs
用于
json
数据的序列化和反序列化
[System.Serializable]
public class Result {
public int errCode;
public string errInfo;
public object data;
}
- 数据类
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);
}
}
- 给
Canvas
添加脚本PanelController_2.cs
用于测试
mqtt
相关函数
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 update
void 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
- 选择平台
- 配置
- 分辨率设置
- image设置
- 其他设置
- 发布设置
- 构建
3.5 集成MQTT
功能
3.5.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还是queue
function checkMessageType(name) {
// 检查订阅类型是topic还是queue
if (name.search(/^\/topic\/.*/gm) >= 0) {
// topic
return 1
} else if (name.search(/^\/queue\/.*/gm) >= 0) {
// queue
return 0
} else {
// stomp默认是queue
return -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) {
// topic
if (name in mqttClient[clientId]['subscribe']['topic']) {
mqttClient[clientId]['subscribe']['topic'][name].unsubscribe();
} else {
alert(`未订阅此消息 ${name}`)
}
} else if (messageType === 0) {
// queue
if (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
})
// 删除clientId
delete 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 = unityInstance
fullscreenButton.onclick = () => {
unityInstance.SetFullscreen(1);
};
}).catch((message) => {
alert(message);
});
};
document.body.appendChild(script);
</script>
</body>
</html>
3.5.2 版本二: 多文件实现
- 添加
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还是queue
checkMessageType: function(name) {
// 检查订阅类型是topic还是queue
if (name.search(/^\/topic\/.*/gm) >= 0) {
// topic
return 1
} else if (name.search(/^\/queue\/.*/gm) >= 0) {
// queue
return 0
} else {
// stomp默认是queue
return -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) {
// topic
if (name in this.mqttClient[clientId]['subscribe']['topic']) {
this.mqttClient[clientId]['subscribe']['topic'][name].unsubscribe();
} else {
console.log(`未订阅此消息 ${name}`)
}
} else if (messageType === 0) {
// queue
if (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} 断开连接`
})
// 删除clientId
delete this.mqttClient[clientId]
})
},
// 执行unity默认对象的默认方法
sendMessage: function(clientId, data) {
this.unityInstance.SendMessage(this.mqttClient[clientId]['objName'], this.mqttClient[clientId]['objFunc'], JSON.stringify(data))
}
}
export { iceMqtt }
index.html
文件
测试
MQTT
功能
在html
文件引入其他js
文件的格式,
具体参考: webGl使用jsLib与Js交互
<!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 = unityInstance
iceMqtt.unityInstance = unityInstance
console.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 = iceMqtt
console.log("module iceMqtt", window.iceMqtt)
}
// window.iceMqtt = iceMqtt
</script>
<script>
// 连接
function mqttConnect(clientId, host, port, username, password, objName, objFunc) {
// 创建一个mqttClient
iceMqtt.mqttCreateClient(clientId, host, port, username, password, objName, objFunc)
// 检查clientId
if (!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 版本一:
-
连接
-
订阅
-
发送消息
-
取消订阅
-
断开连接
4.2 版本二:
-
连接
-
订阅
-
发送消息
-
取消订阅
-
断开连接
5. 问题
- 使用
<script type="module"></script>
后,.jslib
文件无法发现内部的函数
解决办法:
参考:
不同js的加载顺序
js文件模块化引用问题(JavaScript modules)
考虑将module
中的内容挂载到window
......
<script type="module">
// 先执行
import { iceMqtt } from './Plugins/iceMqtt.js'
window.onload = () => {
window.iceMqtt = iceMqtt
console.log("module iceMqtt", window.iceMqtt)
}
// window.iceMqtt = iceMqtt
</script>
......
X. 参考
-
查找物体和组件
Unity 之 查找游戏物体的几种方式解析
Unity 常用API之Component,GameObject获取组件 -
Unity与Js互相调用
Unity WebGL C#调用JS脚本
unity开发webGL,引用js功能。
Unity(WebGL)与JS通讯2022最新姿势
webGl使用jsLib与Js交互 -
Unity在WebGL中InputField无法输入中文
Unity WebGL 输入框(InputField)接受中文输入
unity在webgl端 输入框无法输入中文和中文显示问题的解决
Unity3D添加使用系统中的字体 -
unityInstance is not defined
[Unity转小游戏]微信开发者工具/微信小游戏中找不到unityInstance.(unityInstance is not defined) -
其他
2021-09-29 Unity WebGL平台开发遇到的坑 -
Unity构建WebGL
Unity-WebGL-打包流程以及遇到的各种坑
unity打包webgl 部署到本地Web服务器
【Unity】打包WebGL项目遇到的问题及解决记录 -
Unity使用JsonFx
JsonFX Unity3D 如何使用JsonFX -
Unity WebGL常见错误
unity webgl踩坑指南 -
This value was evaluated upon firstexpanding. It may have changed since then…
This value was evaluated upon firstexpanding. It may have changed since then… -
JS export
JS 之export、export default和module.exports -
scritp 使用type=“module”
在index.html中直接调用import,export需要注意的事项
js文件模块化引用问题(JavaScript modules)