Unity3D打包WebGL并使用MQTT

Unity3D打包WebGL并使用MQTT

1. 环境准备

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

2. 项目搭建

2.1 项目场景

  1. UI界面和元素
    Unity3D打包WebGL并使用MQTT_第1张图片
    Unity3D打包WebGL并使用MQTT_第2张图片
    Unity3D打包WebGL并使用MQTT_第3张图片
  2. 添加中文字体

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

Unity3D打包WebGL并使用MQTT_第4张图片
Unity3D打包WebGL并使用MQTT_第5张图片
3. 添加插件

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

Unity3D打包WebGL并使用MQTT_第6张图片

  1. 修改需要中文显示和输入的元素
    Unity3D打包WebGL并使用MQTT_第7张图片

Unity3D打包WebGL并使用MQTT_第8张图片
Unity3D打包WebGL并使用MQTT_第9张图片

2.2 添加.jslib文件

详细内容参考:Unity(WebGL)与JS通讯2022最新姿势
Unity3D打包WebGL并使用MQTT_第10张图片Unity3D打包WebGL并使用MQTT_第11张图片
Unity3D打包WebGL并使用MQTT_第12张图片

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]);
  },

  Connect: function (host, port, clientId, username, password, destination) {
    mqttConnect(UTF8ToString(host), UTF8ToString(port), UTF8ToString(clientId), UTF8ToString(username), UTF8ToString(password), UTF8ToString(destination));
  },

  Subscribe: function (topic) {
    mqttSubscribe(UTF8ToString(topic))
  },

  Send: function (topic, payload) {
    mqttSend(UTF8ToString(topic), UTF8ToString(payload))
  },

  Unsubscribe: function(topic) {
    mqttUnsubscribe(UTF8ToString(topic));
  },

  Disconnect: function() {
    mqttDisconnect();
  }
});

2.3 添加脚本

  1. Cube添加脚本

用于显示基本函数功能

Unity3D打包WebGL并使用MQTT_第13张图片
Unity3D打包WebGL并使用MQTT_第14张图片
Unity3D打包WebGL并使用MQTT_第15张图片

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. Canvas添加脚本PanelController.cs

用于测试mqtt相关函数

Unity3D打包WebGL并使用MQTT_第16张图片
Unity3D打包WebGL并使用MQTT_第17张图片
Unity3D打包WebGL并使用MQTT_第18张图片

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using UnityEngine.UI;

public class PanelController : MonoBehaviour
{
    [DllImport("__Internal")]
    private static extern void Connect(string host, string port, string clientId, string username, string password, string destination);
    [DllImport("__Internal")]
    private static extern void Subscribe(string topic);
    [DllImport("__Internal")]
    private static extern void Send(string topic, string payload);
    [DllImport("__Internal")]
    private static extern void Unsubscribe(string topic);
    [DllImport("__Internal")]
    private static extern void Disconnect();

    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;

    // 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;
                }
            }
        }
    }

    void HandleConnect() {
        Debug.Log("unity: connect");
        string host = hostInput.text;
        string port = portInput.text;
        string clientId = clientIdInput.text;
        string username = usernameInput.text;
        string password = passwordInput.text;
        string destination = destinationInput.text;
        Connect(host, port, clientId, username, password, destination);
    }

    void HandleSubscribe() {
        Debug.Log("unity: subscribe");
        // string topic = "unity_test";
        string topic = topicInput.text;
        Subscribe(topic);
    }

    void HandleSend() {
        Debug.Log("unity: send");
        // string topic = "unity_test";
        // string payload = "你好";
        string topic = topicInput.text;
        string payload = messageInput.text;
        Send(topic, payload);
    }

    void HandleUnsubscribe() {
        Debug.Log("unity: unsubscribe");
        string topic = topicInput.text;
        Unsubscribe(topic);
    }

    void HandleDisconnect() {
        Debug.Log("unity: disconnect");
        Disconnect();
    }

    void SetLogScroll(string log) {
        scrollLogText.text += "\n" + log;
    }
}

2.4 构建WebGL项目

  1. 选择平台
    Unity3D打包WebGL并使用MQTT_第19张图片
    Unity3D打包WebGL并使用MQTT_第20张图片
  2. 配置
  • 分辨率设置
    Unity3D打包WebGL并使用MQTT_第21张图片
  • image设置
    Unity3D打包WebGL并使用MQTT_第22张图片
  • 其他设置
    Unity3D打包WebGL并使用MQTT_第23张图片
    Unity3D打包WebGL并使用MQTT_第24张图片
    Unity3D打包WebGL并使用MQTT_第25张图片
  • 发布设置
    Unity3D打包WebGL并使用MQTT_第26张图片
  1. 构建
    Unity3D打包WebGL并使用MQTT_第27张图片
    Unity3D打包WebGL并使用MQTT_第28张图片
    Unity3D打包WebGL并使用MQTT_第29张图片
    Unity3D打包WebGL并使用MQTT_第30张图片

2.5 修改index.html内容

html文件引入其他js文件的格式,
具体参考: webGl使用jsLib与Js交互
Unity3D打包WebGL并使用MQTT_第31张图片

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 client
      var subscribeIdObj = {}

      function mqttConnect(host, port, clientId, username, password, destination) {
        let url = 'ws://' + host + ':' + port + '/stomp'
        console.log("html: connect " + url);

        // let host = '127.0.0.1'
        // let port = '61614'
        // let clientId = 'example-unity'
        // let user = 'user'
        // let password = 'pass'
        
        // 创建一个client实例
        client = Stomp.client(url)
        console.log(client)
        let headers = {
          login: username,
          passcode: password,
          'client-id': clientId
        }
        client.connect(headers, () => {
          console.log('connect success');
          unityInstance.SendMessage('Canvas', 'SetLogScroll', 'connect success')
          // 订阅主题
          subscribeIdObj[destination] = client.subscribe('/topic/' + destination, message => {
            if (message.body) {
              console.log('get body : ' + message.body)
              unityInstance.SendMessage('Canvas', 'SetLogScroll', 'get body : ' + message.body)
            } else {
              console.log('get empty message')
              unityInstance.SendMessage('Canvas', 'SetLogScroll', 'get empty message')
            }
          }, () => {
            console.log('connect failed')
            unityInstance.SendMessage('Canvas', 'SetLogScroll', 'connect failed')
          })
        })
      }

      function mqttSubscribe(topic) {
        console.log("html: subscribe " + topic);
        unityInstance.SendMessage('Canvas', 'SetLogScroll', "html: subscribe " + topic)
        // 保存topic对应的subscribeId
        subscribeIdObj[topic] = client.subscribe('/topic/' + topic, message => {
          if (message.body) {
            console.log('message body: ' + message.body)
            unityInstance.SendMessage('Canvas', 'SetLogScroll', 'message body: ' + message.body)
          } else {
            console.log('empty message')
            unityInstance.SendMessage('Canvas', 'SetLogScroll', 'empty message')
          }
        }, {id: '1212'})
      }

      function mqttSend(topic, payload) {
        console.log("html: send " + topic + ", " + payload);
        unityInstance.SendMessage('Canvas', 'SetLogScroll', "html: send " + topic + ", " + payload)
        let headers = {}
        client.send('/topic/' + topic, headers, payload)
      }

      function mqttUnsubscribe(topic) {
        console.log("html: unsubscribe");
        unityInstance.SendMessage('Canvas', 'SetLogScroll', "html: unsubscribe")
        console.log(subscribeIdObj);
        subscribeIdObj[topic].unsubscribe()
      }

      // 断开连接
      function mqttDisconnect() {
        console.log("html: disconnect");
        client.disconnect(() => {
          console.log('disconnect')
          unityInstance.SendMessage('Canvas', 'SetLogScroll', 'html: disconnect')
        })
      }

      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 + "/Web.loader.js";
      var config = {
        dataUrl: buildUrl + "/Web.data",
        frameworkUrl: buildUrl + "/Web.framework.js",
        codeUrl: buildUrl + "/Web.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对象
          window.unityInstance = unityInstance
          fullscreenButton.onclick = () => {
            unityInstance.SetFullscreen(1);
          };
        }).catch((message) => {
          alert(message);
        });
      };
      document.body.appendChild(script);
    script>
  body>
html>

2.6 重新构建并运行WebGL项目

Unity3D打包WebGL并使用MQTT_第32张图片

3. 测试

Unity3D打包WebGL并使用MQTT_第33张图片

3.1 连接

Unity3D打包WebGL并使用MQTT_第34张图片

3.2 订阅

Unity3D打包WebGL并使用MQTT_第35张图片

3.3 发送消息

Unity3D打包WebGL并使用MQTT_第36张图片

3.4 取消订阅

Unity3D打包WebGL并使用MQTT_第37张图片

3.5 断开连接

Unity3D打包WebGL并使用MQTT_第38张图片

4. 相关问题

4.1 InputField不能输入/显示中文

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

4.2 unityInstance is not defined

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

5. 参考

  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项目遇到的问题及解决记录

你可能感兴趣的:(webgl,unity,游戏引擎,activemq)