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界面

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

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

Unity3D打包WebGL并使用MQTT(二):使用json_第1张图片

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数据的序列化和反序列化

Unity3D打包WebGL并使用MQTT(二):使用json_第2张图片
Unity3D打包WebGL并使用MQTT(二):使用json_第3张图片

[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_第4张图片
Unity3D打包WebGL并使用MQTT(二):使用json_第5张图片

Unity3D打包WebGL并使用MQTT(二):使用json_第6张图片

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. 构建
    在这里插入图片描述
    Unity3D打包WebGL并使用MQTT(二):使用json_第7张图片

Unity3D打包WebGL并使用MQTT(二):使用json_第8张图片

Unity3D打包WebGL并使用MQTT(二):使用json_第9张图片

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 Demotitle>
    <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 Demodiv>
      div>
    div>

    
    <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 Demotitle>
    <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 Demodiv>
      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 版本一:

Unity3D打包WebGL并使用MQTT(二):使用json_第10张图片

  1. 连接
    Unity3D打包WebGL并使用MQTT(二):使用json_第11张图片

  2. 订阅
    Unity3D打包WebGL并使用MQTT(二):使用json_第12张图片

  3. 发送消息
    Unity3D打包WebGL并使用MQTT(二):使用json_第13张图片

  4. 取消订阅
    Unity3D打包WebGL并使用MQTT(二):使用json_第14张图片
    Unity3D打包WebGL并使用MQTT(二):使用json_第15张图片

  5. 断开连接
    Unity3D打包WebGL并使用MQTT(二):使用json_第16张图片

4.2 版本二:

Unity3D打包WebGL并使用MQTT(二):使用json_第17张图片

  1. 连接
    Unity3D打包WebGL并使用MQTT(二):使用json_第18张图片

  2. 订阅
    Unity3D打包WebGL并使用MQTT(二):使用json_第19张图片

  3. 发送消息
    Unity3D打包WebGL并使用MQTT(二):使用json_第20张图片

  4. 取消订阅
    Unity3D打包WebGL并使用MQTT(二):使用json_第21张图片
    Unity3D打包WebGL并使用MQTT(二):使用json_第22张图片

  5. 断开连接
    Unity3D打包WebGL并使用MQTT(二):使用json_第23张图片

5. 问题

  1. 使用后,.jslib文件无法发现内部的函数
    解决办法:

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

考虑将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)

你可能感兴趣的:(webgl,json,unity)