当前位置:   article > 正文

Unity3D打包WebGL并使用MQTT(一)_unity mqtt

unity mqtt

Unity3D打包WebGL并使用MQTT

1. 环境准备

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

2. 项目搭建

这篇博客的主要内容是记录将一个Unity项目打包WebGL项目,并集成MQTT

2.1 项目场景

  1. UI界面和元素
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 添加中文字体

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

在这里插入图片描述
在这里插入图片描述
3. 添加插件

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

在这里插入图片描述

  1. 修改需要中文显示和输入的元素
    在这里插入图片描述

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

2.2 添加.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]);
  },

  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();
  }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

2.3 添加脚本

  1. Cube添加脚本

用于显示基本函数功能

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

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  1. Canvas添加脚本PanelController.cs

用于测试mqtt相关函数

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

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135

2.4 构建WebGL项目

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

2.5 修改index.html内容

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>

    <!-- 引入stomp.min.js -->
    <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>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205

2.6 重新构建并运行WebGL项目

在这里插入图片描述

3. 测试

在这里插入图片描述

3.1 连接

在这里插入图片描述

3.2 订阅

在这里插入图片描述

3.3 发送消息

在这里插入图片描述

3.4 取消订阅

在这里插入图片描述

3.5 断开连接

在这里插入图片描述

4. 相关问题

4.1 InputField不能输入/显示中文

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

4.2 unityInstance is not defined

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

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

项目地址

https://download.csdn.net/download/ice_bear221/87690683

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/83232
推荐阅读
相关标签
  

闽ICP备14008679号