UE接入ECharts,让数据会说话

UE 接入 ECharts,让数据会说话

先说结论:UE 里不需要自己画图表。把 ECharts 跑在 Web 面板里,通过消息通道跟 UE 三维场景双向联动,前端写图表,UE 做渲染,两边对接消息契约就行。

很多 UE 项目里,数据图表是刚需——设备监控面板、实时统计大屏、AI 分析结果展示——但用 UMG 画图表是一件极其痛苦的事。一个折线图,你要自己处理坐标轴缩放、数据映射、悬停提示、动画过渡,做出来的效果还比不上前端同学五分钟写出来的 ECharts demo。

这不是你的问题。ECharts 背后是百度整个前端团队十年的积累,它有 50+ 种图表类型、内置交互和动画、百万级数据渲染优化。你用 UMG 从零造轮子,就是在跟一个成熟生态打肉搏战。

ECharts 到底强在哪

给没接触过的 UE 开发者简单介绍一下:

  • 50+ 图表类型:折线图、柱状图、饼图、散点图、热力图、地图、3D 地球、仪表盘、桑基图、关系图……覆盖几乎所有数据可视化场景
  • 开箱即用的交互:缩放、拖拽、图例切换、数据点选中、tooltip 悬停,一行代码不用写
  • 百万级数据量:支持增量渲染和 WebGL 加速,大屏场景不卡顿
  • 高度可定制:颜色、字体、动画、主题、坐标轴,全都可以配
  • 生态丰富:Vue/React 封装、地图插件、时序数据插件、AI 辅助生成配置

所以问题不是"要不要用 ECharts",而是"怎么在 UE 里跑 ECharts"。

方案:把 ECharts 跑在 Web 面板里

架构很简单:

ECharts HTML 页面
       ↓
 WebNativeBrowser(CEF GPU 渲染)
       ↓
   UE 场景中的 Web 面板
       ↓
 JS ↔ UE 消息通道(< 1ms 延迟)
       ↓
   UE 蓝图 / C++ 业务逻辑

和原生 UE 控件不同,Web 面板里的 ECharts 是通过 GPU 共享纹理直接渲染到 UE 视口的,不经过 CPU 拷贝,不影响引擎渲染性能。

第一步:创建一个 ECharts 页面

你只需要写一个普通的 HTML 文件,放在插件的 Content 目录下:

<!-- Content/webnative/html/charts/monitor.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { background: rgba(0,0,0,0); overflow: hidden; }
        #chart { width: 100vw; height: 100vh; }
    </style>
</head>
<body>
    <div id="chart"></div>
    <script>
        const chart = echarts.init(document.getElementById('chart'), 'dark');

        // 从 UE 接收数据并更新图表
        WebNative.on('Chart.Data.Update', (messageBody) => {
            const data = JSON.parse(messageBody);
            chart.setOption({
                title: { text: data.title, textStyle: { color: '#fff' } },
                tooltip: { trigger: 'axis' },
                legend: { data: data.legends, textStyle: { color: '#aaa' } },
                xAxis: { type: 'category', data: data.xAxis },
                yAxis: { type: 'value' },
                series: data.series
            });
        });

        // 图表点击 → 通知 UE
        chart.on('click', (params) => {
            WebNative.send('Chart.Point.Click', JSON.stringify({
                seriesName: params.seriesName,
                dataIndex: params.dataIndex,
                value: params.value,
                name: params.name
            }));
        });

        // 图表缩放 → 通知 UE
        chart.on('datazoom', (params) => {
            WebNative.send('Chart.Zoom.Change', JSON.stringify({
                start: params.start,
                end: params.end
            }));
        });

        // 通知 UE 图表已就绪
        WebNative.send('Chart.Ready', '{}');
    </script>
</body>
</html>

第二步:UE 端发送数据到图表

在 UE 蓝图或 C++ 中,调用 SendMessageToJS 推送数据:

// UE C++ 端:构造 ECharts 数据并发送
void AMyDataActor::UpdateChart()
{
    TSharedPtr<FJsonObject> Root = MakeShareable(new FJsonObject);
    Root->SetStringField("title", "设备实时温度监控");

    // xAxis:时间点
    TArray<TSharedPtr<FJsonValue>> XAxisArray;
    XAxisArray.Add(MakeShareable(new FJsonValueString("08:00")));
    XAxisArray.Add(MakeShareable(new FJsonValueString("09:00")));
    XAxisArray.Add(MakeShareable(new FJsonValueString("10:00")));
    XAxisArray.Add(MakeShareable(new FJsonValueString("11:00")));
    Root->SetArrayField("xAxis", XAxisArray);

    // legends:图例
    TArray<TSharedPtr<FJsonValue>> LegendsArray;
    LegendsArray.Add(MakeShareable(new FJsonValueString("注塑机A")));
    LegendsArray.Add(MakeShareable(new FJsonValueString("注塑机B")));
    Root->SetArrayField("legends", LegendsArray);

    // series:数据系列(ECharts 标准格式)
    TArray<TSharedPtr<FJsonValue>> SeriesArray;

    TSharedPtr<FJsonObject> SeriesA = MakeShareable(new FJsonObject);
    SeriesA->SetStringField("name", "注塑机A");
    SeriesA->SetStringField("type", "line");
    TArray<TSharedPtr<FJsonValue>> DataA;
    DataA.Add(MakeShareable(new FJsonValueNumber(120)));
    DataA.Add(MakeShareable(new FJsonValueNumber(132)));
    DataA.Add(MakeShareable(new FJsonValueNumber(101)));
    DataA.Add(MakeShareable(new FJsonValueNumber(134)));
    SeriesA->SetArrayField("data", DataA);
    SeriesArray.Add(MakeShareable(new FJsonValueObject(SeriesA)));

    TSharedPtr<FJsonObject> SeriesB = MakeShareable(new FJsonObject);
    SeriesB->SetStringField("name", "注塑机B");
    SeriesB->SetStringField("type", "line");
    TArray<TSharedPtr<FJsonValue>> DataB;
    DataB.Add(MakeShareable(new FJsonValueNumber(220)));
    DataB.Add(MakeShareable(new FJsonValueNumber(182)));
    DataB.Add(MakeShareable(new FJsonValueNumber(191)));
    DataB.Add(MakeShareable(new FJsonValueNumber(234)));
    SeriesB->SetArrayField("data", DataB);
    SeriesArray.Add(MakeShareable(new FJsonValueObject(SeriesB)));

    Root->SetArrayField("series", SeriesArray);

    FString JsonString;
    TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&JsonString);
    FJsonSerializer::Serialize(Root.ToSharedRef(), Writer);

    SendMessageToJS("Chart.Data.Update", JsonString);
}

第三步:图表交互反馈到三维场景

当用户点击图表上的数据点时,三维场景可以做出响应:

void AMyDataActor::OnMessageReceived(const FString& FunctionName, const FString& MessageBody)
{
    if (FunctionName == "Chart.Point.Click")
    {
        TSharedPtr<FJsonObject> Json;
        TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(MessageBody);
        if (FJsonSerializer::Deserialize(Reader, Json))
        {
            FString SeriesName = Json->GetStringField("seriesName");
            int32 DataIndex = Json->GetIntegerField("dataIndex");

            // 三维场景聚焦对应设备
            AActor* Device = FindDeviceByName(SeriesName);
            if (Device)
            {
                FocusCameraOn(Device);
                HighlightActor(Device);
            }
        }
    }
}

进阶:复杂图表场景

实时数据流

如果你需要每秒多次推送数据(比如设备振动曲线),建议做批量处理:

// 前端:合并高频数据,按动画帧更新
let pendingData = [];
let scheduled = false;

WebNative.on('Chart.RealTime.Data', (messageBody) => {
    pendingData.push(JSON.parse(messageBody));
    if (!scheduled) {
        scheduled = true;
        requestAnimationFrame(() => {
            const last = pendingData[pendingData.length - 1];
            chart.setOption({
                series: [{
                    data: chart.getOption().series[0].data.concat(pendingData.map(d => d.value)).slice(-100)
                }]
            });
            pendingData = [];
            scheduled = false;
        });
    }
});

3D 图表与场景联动

ECharts 支持 WebGL 3D 渲染,可以做 3D 地球、3D 散点图、3D 曲面。配合 UE 的场景相机:

// 当 UE 相机移动时,同步更新 3D 图表的视角
WebNative.on('Scene.Camera.Move', (messageBody) => {
    const camera = JSON.parse(messageBody);
    // ECharts GL 图表联动
    my3DChart.setOption({
        globe: {
            viewControl: {
                alpha: camera.pitch,
                beta: camera.yaw
            }
        }
    });
});

地图 + 三维场景

ECharts 内置中国地图和世界地图(需注册),可以作为 UE 三维场景的鸟瞰概览:

用户在地图上点击上海园区 → UE 三维场景飞到上海 → 显示园区细节
用户在 UE 里切换到全局视角 → 地图自动缩放到对应区域

性能:消息通道是关键

ECharts 和 UE 之间的联动性能,取决于中间的消息通道。WebNativeBrowser 的消息通道:

指标 数据
单条消息延迟 < 1ms
10 万条消息 ~220ms
每秒最大吞吐 45 万条/秒
每帧最大派发 10 万条

对于图表联动场景来说,这个性能绰绰有余。即使每秒 60 帧刷数据,每帧 800 条消息,延迟仍然在毫秒级。

对比:你用 UMG 画图表要多久

用 UMG 实现一个带 tooltip、缩放、图例切换的折线图:

  1. 用 UCanvas 自己画坐标轴——半天
  2. 处理数据到像素的映射——半天
  3. 实现鼠标悬停 tooltip——半天
  4. 实现拖拽缩放和滚轮缩放——一天
  5. 调试边界情况、性能优化——一天
  6. 换个项目想复用——发现耦合太死,重写

用 ECharts + WebNativeBrowser:

  1. 写一个 50 行 HTML 配置——十分钟
  2. 换项目:复制 HTML 文件,改配置——五分钟
  3. 效果:吊打手写版

把时间花在三维场景和业务逻辑上,图表的事交给 ECharts。

总结

UE 里做数据可视化,最优路径不是自己画——是把成熟的 Web 生态引进来。ECharts 经过了百万开发者和万亿次渲染的验证,UE 的三维渲染能力和 WebNativeBrowser 的 GPU 直通渲染让它能完美嵌入 UE 视口。

  • 前端写图表配置 → 十分钟一个复杂图表
  • UE 推送数据 → 一行 SendMessageToJS
  • 图表交互回传 → 三维场景响应
  • 同一套页面 → Windows / Linux 通用

让 UE 做它最擅长的事,让 Web 做它最擅长的事。


如果这篇对你有帮助,欢迎点赞收藏。关于 WebNativeBrowser 的完整文档和示例,可以访问我们的 GitHub 仓库:starTechnology1994/UEWebNativeBrowser

商务合作 / 授权咨询:startechnology1994@163.com

posted @ 2026-08-17 08:58  StarTechnology  阅读(26)  评论(0)    收藏  举报