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

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity3D 中,可以使用 WebCamTexture 类来访问摄像头。WebCamTexture 可以从摄像头捕获视频流,并将其作为纹理传递给 Unity3D。以下是在 Unity3D打包 WebGL 并调用手机摄像头的步骤: 1. 在 Unity3D 中创建一个新的场景。 2. 在场景中创建一个 Plane 对象,并将其放置在场景中心。 3. 在 Inspector 窗口中,将 Plane 对象的 Scale 设置为 (10, 1, 10)。 4. 在场景中创建一个 Cube 对象,将其放置在 Plane 对象上方,并将其旋转 45 度。 5. 在 Cube 对象上添加一个新的脚本,并将其命名为 WebcamTextureScript。 6. 在脚本中编写以下代码: ``` using UnityEngine; using System.Collections; public class WebcamTextureScript : MonoBehaviour { // The webcam texture private WebCamTexture webcamTexture; // Use this for initialization void Start () { // Get the webcam device WebCamDevice[] devices = WebCamTexture.devices; if (devices.Length > 0) { // Create a new webcam texture webcamTexture = new WebCamTexture(devices[0].name); // Set the texture on the material GetComponent<Renderer>().material.mainTexture = webcamTexture; // Start the webcam webcamTexture.Play(); } } } ``` 7. 在 Unity3D 菜单中选择 File > Build Settings。 8. 在 Build Settings 窗口中,选择 WebGL 平台,并点击 Build 按钮。 9. 在生成的项目目录中,找到 index.html 文件并编辑它。 10. 在文件中找到以下代码: ``` <script src="Build/UnityLoader.js"></script> <script> var gameInstance = UnityLoader.instantiate("gameContainer", "Build/Build.json", {onProgress: UnityProgress}); </script> ``` 11. 在上面的代码后面添加以下代码: ``` <script> navigator.mediaDevices.getUserMedia({video: true}).then(function(stream) { var video = document.querySelector('video'); video.srcObject = stream; video.onloadedmetadata = function(e) { video.play(); }; }).catch(function(err) { console.log(err.name + ": " + err.message); }); </script> ``` 12. 保存并关闭 index.html 文件。 13. 在浏览器中打开 index.html 文件,应该可以看到摄像头的视频流在 Unity3D 场景中。 注意:调用摄像头需要 HTTPS 协议或 localhost 环境。如果您使用的是 localhost 环境,则需要在浏览器中输入 https://localhost:port 打开项目。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值