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

news2025/3/12 23:56:56

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 添加插件

  1. 添加WebGLInput插件

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

在这里插入图片描述
2. 添加系统中文字体

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

在这里插入图片描述
在这里插入图片描述

  1. 修改InputField中文显示和字体
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. 添加Pathfinding.JsonFx.dll

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

在这里插入图片描述

3.3 添加脚本

  1. .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));
  }
});
  1. 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());
    }
}

  1. 数据类Result.cs

用于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相关函数

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

  1. 选择平台
    在这里插入图片描述
    在这里插入图片描述
  2. 配置
  • 分辨率设置
    在这里插入图片描述
  • image设置
    在这里插入图片描述
  • 其他设置
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 发布设置
    在这里插入图片描述
  1. 构建
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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还是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 版本二: 多文件实现

  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还是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 }

  1. 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 版本一:

在这里插入图片描述

  1. 连接
    在这里插入图片描述

  2. 订阅
    在这里插入图片描述

  3. 发送消息
    在这里插入图片描述

  4. 取消订阅
    在这里插入图片描述
    在这里插入图片描述

  5. 断开连接
    在这里插入图片描述

4.2 版本二:

在这里插入图片描述

  1. 连接
    在这里插入图片描述

  2. 订阅
    在这里插入图片描述

  3. 发送消息
    在这里插入图片描述

  4. 取消订阅
    在这里插入图片描述
    在这里插入图片描述

  5. 断开连接
    在这里插入图片描述

5. 问题

  1. 使用<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. 参考

  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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/413480.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

〖Python网络爬虫实战⑬〗- XPATH实战案例

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;目前专栏免费订阅&#xff0c;在转为付费专栏前订阅本专栏的&#xff0c;可以免费订阅付费…

一篇文章理解堆栈溢出

一篇文章理解堆栈溢出引言栈溢出ret2text答案ret2shellcode答案ret2syscall答案栈迁移答案堆溢出 unlink - UAF堆结构小提示向前合并/向后合并堆溢出题答案引言 让新手快速理解堆栈溢出&#xff0c;尽可能写的简单一些。 栈溢出 代码执行到进入函数之前都会记录返回地址到SP…

wsl区分和切换,安装NVIDIA驱动+cuda+ffmpeg

wsl区分和切换&#xff0c;安装NVIDIA驱动cudaffmpeg 安装Nvidia驱动 打开terminal wsl --update进入wsl nvidia-smi网上找了一些博客&#xff0c;获取信息&#xff1a; window安装好驱动即可wsl有1和2 我的win10已经安装了驱动 wsl1和wsl怎么区分&#xff1f;切换 区分…

实时聊天提示的最佳做法

本文将教您更多关于有效的实时聊天功能对您的品牌的重要性&#xff0c;以及您可以使用的一些最佳实践来确保您的实时聊天功能尽可能好。实时聊天提示是为您的网站访问者显示的自动聊天消息。在SaleSmartly&#xff08;ss客服&#xff09;中&#xff0c;如您将聊天插件安装到您的…

Win10 安装 MongoDB 5.0.16

一、MongoDB 的官方&#xff1a;http://www.mongodb.com/ 下载&#xff1a;​ ​https://www.mongodb.com/download-center/community​ 二、下载msi文件&#xff0c;双击该文件进行安装。 &#xff08;1&#xff09;打开对话框 ,单击“Next” &#xff08;2&#xff09;请勾…

总结815

4月&#xff08;复习完高数18讲内容&#xff0c;背诵21篇短文&#xff0c;熟词僻义300词基础词&#xff09; 4.8 英语&#xff1a;早上继续背昨天没背完的第15篇文章单词&#xff0c;感觉效率极高&#xff0c;可能是昨晚睡眠质量特别好。下午抄写第16篇文章每日一句长难句看《…

Meetup 直播预告|助力企业数字化转型,8 大微服务容器开源实践亮点抢先看

随着数字化、智能化发展趋势不断加快&#xff0c;大中小型企业纷纷将企业“上云”提上日程&#xff0c;推动企业数字化转型。云时代下&#xff0c;企业需要新技术架构&#xff0c;使之更好地利用云计算优势&#xff0c;让业务更敏捷、成本更低、可伸缩性更强&#xff0c;云原生…

进程概念详解

目录 进程是什么&#xff1f; 描述进程&#xff1a;进程控制块-PCB task_struct task_struct 是什么&#xff1f; task_struct内容分类 组织进程 查看进程 fork创建子进程 进程状态 僵尸进程 孤儿进程 进程优先级 其他概念 进程是什么&#xff1f; 一般书上…

【微信小程序】父子组件之间传值

微信小程序父子组件之间传值有两种&#xff1a; 1.父组件向子组件传值 2.子组件向父组件传值 区别&#xff1a; 父向子传值使用的是属性绑定&#xff0c;子组件中的properties对象进行接收父组件传递过来的值。子向父传值使用的是自定义事件&#xff0c;父组件通过自定义事件…

VUE 学习笔记(一)开发环境搭建

1、Visual Studio Code安装及使用 下载地址官网&#xff1a;https://code.visualstudio.com/ 直接点击下载按钮即可&#xff0c;会根据系统自动下载合适的版本&#xff0c;无需自行选择。 2、VSCode 上安装&#xff1a;JavaScript Debugger 目前 Debugger for Chrome 已经处…

基于html+css的盒子内容旋转

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

【目标检测论文阅读笔记】Reducing Label Noise in Anchor-Free Object Detection

&#xff08;Augmentation for small object detection&#xff09; Abstract 当前的 anchor-free无锚目标检测器 将空间上落在真值框预定义中心区域内的所有特征标记为正。这种方法会在训练过程中产生 标签噪声&#xff0c;因为这些 正标记的特征中的一些 可能位于背景或遮挡…

2023最新8个电脑必装软件,新电脑装完好用又高效

新买的笔记本电脑到手了&#xff0c;需要安装什么软件&#xff1f;不会真的有人这样问吧&#xff0c;万一真的有人不知道需要安装什么软件呢&#xff1f;好吧&#xff0c;提醒一下各位&#xff0c;新电脑不要乱安装软件啊&#xff0c;不然电脑很容易中病毒的。根据我多次换电脑…

MyBatis配置文件 —— 相关标签详解

目录 一、Mybatis配置文件 — properties标签 二、Mybatis配置文件 — settings标签 三、Mybatis配置文件 — plugins标签 四、Mybatis配置文件 — typeAliases标签 五、Mybatis配置文件 — environments标签 六、Mybatis配置文件 — mappers标签 一、Mybatis配置文件 —…

burp抓包https链接不安全解决方法

在浏览器已经导入Burpsuite的证书之后,抓包,浏览器仍然显示抓取https包提示不是私密链接解决方法 适用于没有继续访问的按钮。 方法一: 浏览器输入 chrome://flags 搜索 Allow invalid certificates for resources loaded from localhost.翻译过来就是 允许从本地主机加载资…

2023 年十大目标检测模型!

2023 年十大目标检测模型&#xff01; 使用深度学习革新对象检测的综合指南。 对象检测示例 “目标检测是计算机视觉中最令人兴奋和最具挑战性的问题之一&#xff0c;而深度学习已成为解决它的强大工具。” 对象检测是计算机视觉中的一项基本任务&#xff0c;涉及识别和定位…

SpringBoot与RabbitMQ 集成以及死信队列,TTL,延迟队列

简单示例 项目结构依赖配置生产者消费者 消息的可靠投递示例 confirmreturn 消费者确认机制消费端限流TTL 单条消息整个队列设置 TTL 死信队列 死信队列的实现步骤 延迟队列消息幂等设计 简单示例 项目结构 依赖 <dependencies><dependency><groupId>org.…

【Linux 裸机篇(一)】ARM Cortex-A 架构基础、汇编基础

目录一、ARM Cortex-A 架构基础1. Cortex-A 处理器运行模型2. Cortex-A 寄存器组2.1 通用寄存器2.1.1 未备份寄存器2.1.2 备份寄存器2.1.3 程序计数器 R152.2 程序状态寄存器二、ARM 汇编基础1. GNU 汇编语法1.1 语句1.2 段1.3 伪操作1.4 函数2. 常用汇编指令2.1 处理器内部数据…

你的订婚|结婚纪念日是质数吗?进来测算看看……

今年开年以来&#xff0c;随着ChatGPT的爆火&#xff0c;原本一直平静的三六零安全科技股份有限公司&#xff08;下称360&#xff09;股价仅2月以来涨幅就达到近200%。然而4月4日晚间&#xff0c;360发布公告称&#xff0c;公司董事长周鸿祎与妻子胡欢离婚。有意思的是&#xf…

【Java版oj】day25星际密码、数根

目录 一、洗牌 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 二、数根 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 一、洗牌 &#xff08;1&…