概述

        在传统的 Unity 开发中,UI 系统(如 UGUI/UI Toolkit)在处理复杂数据可视化、动态布局以及 Web 生态集成时往往面临开发周期长、迭代成本高的挑战。Ploy3D  插件为 Unity 开发者提供了一套高性能、跨平台的 Web 界面支持方案,让前端生态的灵活性与 Unity 3D 的引擎实力很好结合。
 

核心价值:为什么选择 Web 界面?

1. 更快的界面开发效率

利用前端成熟的 React/Vue/Echarts 生态,在数字孪生项目中快速构建复杂的大屏数据面板。

2. 敏捷的更新与迭代

对于 UI 逻辑变更频繁的工具类软件,Web 界面支持远程部署与热更新,无需重新打包 Unity 工程即可上线新功能。

3. AI 与开源生态无缝集成

原生支持集成 ComfyUI 等基于 Web 的 AI 工具流,轻松构建端到端的 AI 创作工具。

4. 友好的跨平台开发体验

支持 WindowsmacOS 平台的 Unity 编辑器直接预览与调试,保持开发环境与生产环境的一致性。

5. 高性能底层通信

基于本地回环地址(Loopback)的 WebSocket 通信机制,确保 C# 与 JavaScript 之间的数据传输延迟极低,满足实时交互需求。

交互机制与流程

Ploy3D 构建了一个高效的数据双向通道:

  • Unity -> Web: 场景与UI状态同步。

  • Web -> Unity: 逻辑控制、相机操作、参数配置。

快速上手指南

1. 启用 Web 界面

在主脚本中设置启用状态并设置页面URL,即可激活透明或覆盖式的 Web 容器。

Ploy3D.g_instance.webui = true;
Ploy3D.g_instance.webuiUrl_ = "http://localhost/ui.html";

2. 从 JS 发送指令 (以相机控制为例)

通过 window.ws.send 接口发送,可发送二进制 ArrayBuffer 和 文本字符串 数据。

canvas2d.addEventListener("wheel", (e) => {
    // 发送给"CameraCtrl"对象、发送给"Scale"方法、参数0
    window.ws.send(new Int32Array([0, 0, Math.floor(e.wheelDelta) / Math.abs(e.wheelDelta)]).buffer);
});

canvas2d.addEventListener("pointermove", (e) => {
    if ((e.buttons & 1) == 1) {
        // 发送给"CameraCtrl"对象、发送给"Move"方法、参数0、参数1
        window.ws.send(new Int32Array([0, 1, Math.floor(e.movementX), -Math.floor(e.movementY)]).buffer);
    }
    else if ((e.buttons & 2) == 2) {
        // 发送给"CameraCtrl"对象、发送给"Rotate"方法、参数0、参数1
        window.ws.send(new Int32Array([0, 2, Math.floor(e.movementX), Math.floor(e.movementY)]).buffer);
    }
    else if ((e.buttons & 4) == 4) {
        // 发送给"CameraCtrl"对象、发送给"Translate"方法、参数0、参数1
        window.ws.send(new Int32Array([0, 3, Math.floor(e.movementX), Math.floor(e.movementY)]).buffer);
    }
});

3. C# 接收并分发消息

通过 Ploy3D.g_instance.onMessage = (int type, int byteLength, IntPtr bytes) => {} 绑定监听方法。

可以通过 BinaryReader 读取二进制数据,或者通过 Marshal.PtrToStructure 等方法转换为结构体。

Ploy3D.g_instance.onMessage = (int type, int byteLength, IntPtr bytes) =>
{
    // 确认消息有效
    if (!(bytes == IntPtr.Zero || byteLength <= 0))
    {
        // 文本消息类型
        if (type == 0)
        {
            // 转为 string 类型
            byte[] buffer = new byte[byteLength];
            Marshal.Copy(bytes, buffer, 0, byteLength);
            string msg = System.Text.Encoding.UTF8.GetString(buffer);

            Debug.LogError($"OnMessage: {msg}");

            return;
        }
        // 二进制消息
        else if (type == 1)
        {
            // 可以自动反序列化为消息结构体
            // [StructLayout(LayoutKind.Sequential, Pack = 1)]
            // [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            // Message msg = Marshal.PtrToStructure<Message>(bytes);

            // 这里我们随机读取二进制消息(安全上下文中我们必须拷贝出缓存)
            byte[] buffer = new byte[byteLength];
            Marshal.Copy(bytes, buffer, 0, byteLength);
            BinaryReader reader = new BinaryReader(new MemoryStream(buffer));

            int obj = reader.ReadInt32();
            if (obj == 0)
            {
                int func = reader.ReadInt32();
                if (func == 0)
                {
                    float param0 = reader.ReadInt32();

                    Ploy3D.g_instance.cameraCtrl.Scale(param0);
                }
                else if (func == 1)
                {
                    float param0 = reader.ReadInt32();
                    float param1 = reader.ReadInt32();

                    Ploy3D.g_instance.cameraCtrl.Move(param0, param1);
                }
                else if (func == 2)
                {
                    float param0 = reader.ReadInt32();
                    float param1 = reader.ReadInt32();

                    Ploy3D.g_instance.cameraCtrl.Rotate(-param0, -param1);
                }
                else if (func == 3)
                {
                    float param0 = reader.ReadInt32();
                    float param1 = reader.ReadInt32();

                    Ploy3D.g_instance.cameraCtrl.Translate(param0, -param1);
                }
            }

            return;
        }
    }

    Debug.LogError($"OnMessage: {type} {byteLength} {byteLength}");
};

4. 从 C# 同步状态至 Web

通过 Ploy3D.g_instance.SendMessageA 接口发送文本消息。

通过 Ploy3D.g_instance.SendMessage 接口发送二进制消息。

// 发送文本消息
Ploy3D.g_instance.SendMessageA("发送文本消息!");
// 发送二进制消息
Ploy3D.g_instance.SendMessage(new byte[] { 1, 2, 3, 5 });

5. JS 端响应消息

通过 window.ploy_on_ws_msg = function (msg) {} 绑定监听方法。接收 C# 发送的消息。

window.ploy_on_ws_msg = function (msg) {
    if (typeof msg.data === "string") {
        console.error(msg.data);
    }
    else {
        msg.data.arrayBuffer().then(buffer => {
            console.error(buffer);
        });
    }
}

基于 PLOY3D 开发的 “数字孪生演示汇报平台” 即将登录 UNITY。其中一个的功能便是支持自定义汇报内容看板界面,而自定义界面在Unity中是很难实现的,而用 Web 技术我们可以很简单的实现这些。

下载插件与Unity工程:1046017356

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐