Unity3D打包WebGL并使用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();
  }
});

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

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>

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

猜你喜欢

转载自blog.csdn.net/ice_bear221/article/details/130163051
今日推荐