简介:XCharts 3.14 是 Unity 平台上广受欢迎的免费开源图表插件,面向 Unity 开发者、游戏制作人和数据可视化需求方。它内置折线图、柱状图、饼图、雷达图、散点图、热力图、环形图、K线图、极坐标和平行坐标等常见图表,并扩展了3D饼图、3D柱图、3D金字塔、漏斗图、仪表盘、水位图、象形柱图、甘特图、矩形树图、桑基图、关系图等高级图表,基本覆盖二维到三维、常规统计到专业分析的可视化场景。压缩包共906个文件,约5.32MB,以C#源码脚本、meta索引、png示例图、md说明文档和asset/asmdef配置文件为主,整体轻量,便于导入和二次开发。该插件上手快速,官方示例和文档能帮助开发者理解调用方式,节省底层图表开发时间,快速获得可交互、可自定义样式的图表组件;已有512人浏览学习,适合需要在Unity中高效实现数据面板或游戏内统计界面的开发者。
1. 先别急着拖预制体:XCharts 3.14 到底是什么
你第一次打开 XCharts 3.14 的包,大概率和我一样先翻示例场景。一看效果,觉得这插件就是“调好的预制体,拖进去改改数据就行”。真开工就发现没那么简单:这个版本的接口和网上大部分教程对不上,2.x 时代那套AddData、ClearData的写法全变了,图表结构也从“一个预制体包一切”改成“代码装配”。做可视化大屏、实时数据看板、比赛复盘这类项目时,3.14 强调的是用代码动态装配图表,而不是在编辑器里手点配置。想用它做运营报表、性能监控、多维度数据对比的,可以继续往下看。这篇就顺着我实际接项目的路径,把选型、接入、配参、踩坑一次说清。
2. 选 3.14 还是守着 2.x:先看懂它的数据模型,再决定要不要升
很多人被 3.x 劝退,不是因为功能不够,而是因为上手成本变了。2.x 走“场景里放预制体,脚本里一行一行加数据”的路子,3.x 则把图表拆成三个层次:Series(系列)、Axis(坐标轴)、Component(组件)。理解了这三个层次,后面所有配置和排查都是顺水推舟。
2.1 三个层次拆明白,你就懂 3.14 的一半
以柱状图为例,一个图表对象内部是这样的:Series 负责画“柱子”,每个系列对应一组数据,可以同时放多个系列做分组对比;Axis 负责画坐标轴,包括 X 轴、Y 轴以及它们的刻度、网格线、标签;Component 是辅助组件,比如图例、数据缩放、提示框、视觉映射。坐标轴和组件不是“一个 chart 只有一个”的固定结构,而是可以按需增删的。比如你要在同一个图表里显示销售额和增长率,两个数据的量纲差别很大,就可以加两个 Y 轴组件,一个在左侧,一个在右侧。
这种设计带来的直接好处是:图表和预制体解耦。你不需要为了一个新的图表形态去复制一堆 Prefab,而是用代码创建 chart 对象,按需装配组件。坏处也很直接:如果你习惯了拖拽式配置,面对 3.14 会有一段不适应期。我在某数据中台项目里第一次迁移时,光是把 2.x 的“柱状图 + 折线图叠加”改成 3.14 的写法,就花了大半天翻源码。
2.2 从 2.x 升到 3.14:接口变化比想象中更伤
先说结论:如果你的项目还停留在 Unity 2019 或更早的版本,别升 3.14。3.x 对引擎版本有最低要求,强行升只会换来一堆编译错误。引擎版本没问题,也要做好接口迁移的心理准备。2.x 里最常用的追加数据写法,到 3.14 基本都被重命名或挪了位置。
我随便列几个常见的迁移点:2.x 的chart.ClearData()/AddData()按点追加模式,在 3.14 里被整体刷新式的SetData()取代,语义从“我加一条”变成“我换一版”;图例、提示框的开关属性从 chart 主类上挪到了各自的 Component 类里;主题颜色不再是写死的字段,而是一套 Theme 对象,图表初始化后可以整体替换。这些都是 3.0 就开始的调整,3.14 延续了这套规则。
我一般建议团队里两种人分头处理迁移:业务开发直接看插件的示例代码,对着改最省事;负责基础架构的人去翻BaseChart.cs和XChartMgr.cs,搞清楚生命周期和刷新机制。如果只是把数据塞进去出个效果,看示例就够了;如果要做动态实时刷新、性能优化,不看源码后面必踩坑。
2.3 什么时候不该用它:3.14 不是万能图表方案
说实话,3.14 在业务看板、数据可视化大屏、运营报表这些场景里非常好用,但有几类需求别硬上。第一类是静态装饰图,就一张提前做好的饼图放界面角落,这种用图片渲染成本更低,没必要引一个图表库。第二类是百万级数据的散点图或者超长时序曲线,这类需求对顶点数和合批策略要求极高,直接画点比用图表库划算。第三类是 3D 图表,这个插件渲染目标是 UGUI 的 Canvas 体系,本质上是 2D 网格驱动,硬要套 3D 场景会非常别扭。
还有一点要提醒:如果你的项目 UI 部分已经重度使用了自绘 Mesh 或自定义 Shader,引入 XCharts 3.14 之前先做一轮 Canvas 合批评估。因为图表本身会生成大量 UGUI 顶点,几个图表叠在一个 Canvas 下,批次可能瞬间翻倍。想清楚这些边界,再动手接入不迟。
3. 动手接入 3.14:从导包到画出第一张柱状图
选型定了,后面就是纯执行。这一章按我实际接项目的顺序来:导包、版本自检、创建图表、喂数据。每一步给可复制的代码,并解释参数怎么调。
3.1 导入插件包与运行时版本自检
XCharts 3.14 属于源码型插件,不是编译好的 DLL,导入方式比较灵活。我习惯不用 Asset Store 的自动导入,而是把包目录整份放到项目的 Packages 或 Assets 目录下,这样版本信息一目了然,后续排查问题也知道去哪翻源码。放到 Packages 下时,Unity 会自动识别为本地包,重新编译一次即可。
导入后先做一次运行时版本自检,避免多个版本或旧残留文件混在项目里。写一个临时脚本:
using UnityEngine; using XCharts; public class VersionCheck : MonoBehaviour { void Start() { var asm = typeof(BaseChart).Assembly; Debug.Log("XCharts Assembly Version: " + asm.GetName().Version); } }这段代码的核心作用是确认当前编译进工程的 XCharts 程序集版本。如果你在项目里同时放了两份插件拷贝,编译不报错但行为诡异,这一步能直接暴露真相。参数说明只有一个:typeof(BaseChart).Assembly拿到的程序集是插件主程序集,版本号与包的版本号通常一一对应。如果这个日志打不出来,说明XCharts命名空间没有被正确编译,优先检查目录结构和脚本编译错误。
3.2 用 XChartMgr 创建第一个图表实例
3.14 里创建图表不靠菜单拖预制体,而是通过XChartMgr.Instance.AddChart<T>()在代码中动态创建。我通常在场景里放一个空的 RectTransform 作为图表容器,然后挂一个初始化脚本:
using System.Collections.Generic; using UnityEngine; using XCharts; public class ChartInit : MonoBehaviour { [SerializeField] private RectTransform chartContainer; void Start() { // 在指定容器下创建柱状图,chartName 会用于内部管理 var chart = XChartMgr.Instance.AddChart<BarChart>("scoreChart"); // 放到我们的 UI 容器里,并铺满 chart.transform.SetParent(chartContainer, false); chart.transform.localScale = Vector3.one; chart.GetComponent<RectTransform>().sizeDelta = Vector2.zero; // 准备类别轴数据与数值数据 var categories = new List<string> { "第1题", "第2题", "第3题", "第4题" }; var values = new List<float> { 88f, 92f, 76f, 95f }; // 3.x 的整体刷新式接口:一次 SetData 替换全部数据 chart.SetData(categories, values); } }代码逻辑分三步:第一步调用AddChart<BarChart>,泛型参数决定图表类型,这里是柱状图,换折线图就写成LineChart;第二步把生成好的图表对象挂到指定 UI 容器下,sizeDelta置零表示跟随父节点拉伸,这样后续改容器大小图表会自动适配;第三步准备两个列表,categories对应 X 轴分类,values对应柱子高度。参数说明里最需要注意的是SetData的语义:它是一次性全量替换,而不是追加。如果你在Start之后还要更新数据,继续调用SetData即可,不需要先清空。
3.3 数据不对齐时的精细控制接口
业务场景不会总那么简单。比如某次你要做双系列柱状图,左边一组“计划值”,右边一组“实际值”,两个系列的类别轴是同一个,但数值来源是两个数据集。这种需求直接用SetData的重载版本,指定系列索引:
var chart = XChartMgr.Instance.AddChart<BarChart>("planAndActual"); var categories = new List<string> { "1月", "2月", "3月", "4月" }; var plan = new List<float> { 100f, 120f, 110f, 130f }; var actual = new List<float> { 90f, 115f, 108f, 142f }; chart.SetData(categories, plan); chart.AddSeriesData(1, actual);这里第一行SetData会创建并填充系列 0;第二行AddSeriesData的第二参数指定数据所属系列索引,从 0 计数,所以 1 表示第二个系列。这是 3.x 系列里常用的“先整体后局部”的数据填充方式。参数层面要确认两件事:系列索引是否已经在图表中存在,不存在时要先chart.AddSeries()建系列,否则这行代码会拿不到目标系列;数据长度与类别轴长度不一致时,多余的数据会被截断,缺失的留空,不会报错。这也是很多人数据对不齐时找不到原因的坑。
3.4 坐标轴相关的最简配置
默认创建的图表带了基本的 X 轴和 Y 轴,但项目里几乎都要改轴名、改刻度范围。3.14 中坐标轴也是一种组件,通过GetOrAddChartComponent<T>获取:
var xAxis = chart.GetOrAddChartComponent<XAxis>(); xAxis.name = "答题编号"; xAxis.axisLabel.formatter = "{value} 题"; var yAxis = chart.GetOrAddChartComponent<YAxis>(); yAxis.name = "得分"; yAxis.minMax = new MinMax(0f, 100f); yAxis.interval = 10f;坐标轴配置的关键点:formatter里的{value}是占位符,实际显示时会被刻度值替换,比如“1 题”“2 题”;minMax直接控制数值轴显示范围,适合固定评判标准的数据看板,例如百分制就固定 0 到 100;interval是刻度间距,10 意味着 0、10、20 这种节奏。需要注意GetOrAddChartComponent的行为:如果图表里已经存在这个组件,它返回现有实例,不存在才创建。所以这段代码放在Start里安全,放在频繁调用的方法里则要小心重复设置的开销。
4. 把默认图调成能上线的图:主题、格式化与刷新节奏
图表能显示只是第一步,真正能交到业务方手里,需要解决三件事:颜色搭配不上品牌规范、坐标轴单位不清晰、数据刷新时界面卡顿。这一章专门讲参数怎么调。
4.1 主题与调色板:一套代码适配多套主题
XCharts 3.14 的主题体系是独立于图表数据存在的,切换主题相当于换皮肤,不影响数据内容。主题里包含背景色、文本色、坐标轴线色、网格线色以及系列调色板。我在一个多品牌运营项目中,就是通过切换主题让同一套图表适配不同子产品的视觉规范:
chart.theme.backgroundColor = new Color32(0x16, 0x1B, 0x2D, 0xFF); chart.theme.textColor = new Color32(0xC0, 0xD0, 0xE0, 0xFF); chart.theme.colorPalette = new List<Color> { new Color32(0x4A, 0x90, 0xFE, 0xFF), new Color32(0x53, 0xD7, 0x69, 0xFF), new Color32(0xFF, 0x8A, 0x3B, 0xFF) };颜色配置的规则是:前两项分别控制背景和文字基调,colorPalette是系列自动取色的顺序列表。比如图表有三个系列时,第一个系列用4A90FE,第二个用53D769,第三个用FF8A3B。如果系列数量超过调色板长度,它会从头循环取色,所以至少保证调色板颜色数不少于最大系列数,不然两个系列会撞色。这个坑我踩过一次,某次上线后业务方反馈“两根折线颜色分不清”,排查半天就是调色板只有两组颜色却画了三条线。
4.2 坐标轴格式化的几个实用模式
做数据看板最常遇到的是单位混用,比如金额可能需要显示成“1.2万”,百分比需要“88%”,时间需要“12:30”。XCharts 的 axisLabel 格式化字符串支持占位符,也支持代码动态生成。常见的格式化语法如下表:
| 场景 | 配置示例 | 显示效果 |
|---|---|---|
| 数字加单位 | {value} 人 | 1200 人 |
| 百分比 | {value}% | 88% |
| 固定小数位 | {value:F1} | 88.0 |
| 自定义转换 | {value}配合formatter回调 | 取决于你的转换逻辑 |
最实用的是formatter的回调版本。当你想把数值从“毫秒”显示成“秒”,或把 10000 显示成“1万”,格式化字符串就不够用了,需要给 axisLabel 挂一个转换方法。我一般在脚本里这样写:
xAxis.axisLabel.formatterFunction = (double value) => { if (value >= 10000) { return (value / 10000f).ToString("0.0") + "万"; } return value.ToString(); };参数说明:formatterFunction接收原始数值,返回字符串。这个回调在每次重绘时都会被调用,所以不要在回调里做重量级计算,比如查表或数据库查询。它更适合纯数值转换。数据量特别大的场景下,字符串拼接也会成为开销,建议把格式化成固定精度,不要用默认的浮点转字符串。
4.3 数据刷新节奏与动画开关
3.14 默认对图表做了入场动画,数据从 0 生长到目标值。这个动画在第一次展示时观感很好,但在实时刷新场景里就是拖后腿的:每 1 秒刷新一次,动画还没播完又被打断,图表会不停抖动,CPU 占用也高得不合理。
做法是关掉动画,改成数据静默刷新:
chart.animation.enable = false;然后定时更新数据:
IEnumerator RefreshDataLoop(float interval) { while (true) { var newValues = FetchLatestValues(); // 模拟从接口拉数据 chart.SetData(categories, newValues); yield return new WaitForSeconds(interval); } }这里最关键的是刷新频率。我一般建议按业务需求反向设置,比如看板要求“秒级更新”,就先用 2 秒间隔起测,逐步缩短。间隔太短不仅消耗 CPU,而且用户肉眼根本来不及看数据变化,纯属浪费。另外,如果数据是整体替换,SetData是正确选择;如果只是修改单条数据,可以走系列数据更新的细粒度接口,减少重绘范围。具体方法名在不同小版本有改动,最稳的思路是在包里搜Refresh或UpdateData关键字,找到后先看它的注释,明确是“全量重建”还是“局部更新”再使用。
5. 3.14 做好 3.14 的事:五条避坑记录,按现象照抄
这一章是血泪经验汇总,每条都是真实项目中碰到并解决的。按“现象 → 原因 → 解决”写,方便你遇到类似问题时对号入座。
5.1 编译时报错:找不到 AddData / ClearData
现象是网上复制一段 2.x 教程代码,贴到项目里直接红线,提示BaseChart没有AddData、ClearData方法。原因是 3.14 把追加式数据接口整体移除了,所有数据推送统一走SetData和系列数据更新接口。解决方法是删除旧接口调用,换成SetData全量刷新,或先用chart.ClearSeries()再重新填充。提示:遇到不知道有没有的方法,按住 Ctrl 点进BaseChart.cs看它真实暴露了哪些公共方法,比自己猜接口靠谱得多。
5.2 图表刷新时整个 Canvas 卡顿
现象是SetData后帧率骤降,Profiler 里Canvas.SendWillRenderCanvases耗时暴涨。原因是每次数据刷新都会触发图表网格重建,如果 Canvas 下还有其他复杂 UI,所有 UI 会一起重建。解决思路是控制刷新频率和重建范围。我当时的处理方式:把图表单独放到一个子 Canvas 下,让图表刷新不影响主 UI;同时把刷新间隔从 0.5 秒调整到 1.5 秒,肉眼完全无感,帧率立刻恢复。
5.3 中文字体显示成方块或默认字体
现象是坐标轴标签和标题里的中文全变成小方块,或者字体跟整体 UI 不一致。原因是 XCharts 的主题默认字体指向 Arial,而 Unity 动态字体在部分平台下对中文的支持不稳定。解决方法是给 theme 指定项目里的中文字体,并在字体导入设置里开启Include Font Data选项,确保运行时字体信息完整打包。这一步顺序不能反:先设置字体,再刷新图表,否则已经生成的字形不会自动重建。
5.4 大屏上图表模糊或偏移
现象是同一套图表在编辑器里正常,发布到高分屏或不同分辨率屏幕上时文字发虚、位置错位。原因是 Canvas 的缩放模式与 XCharts 的 DPI 设置没有对齐。解决方法是先确认 Canvas 使用Scale With Screen Size模式,再调整图表的 DPI 属性,让它与设计稿分辨率匹配。大屏项目里我习惯把设计稿分辨率定成一个固定值,然后把图表容器的 RectTransform 按参考分辨率铺满,不用运行时反复计算坐标。
5.5 图表的图例拖拽和页面滚动冲突
现象是图表嵌在 ScrollRect 页面里,手指滚动页面时总触发图例或数据缩放组件的拖拽,页面滚不动。原因是图例、DataZoom 等组件默认接收全部拖拽事件,ScrollRect 没有机会响应。解决方法是给这些交互组件关闭不需要的事件类型,或者在图表外层加一层 Mask 阻断事件透传。具体配置在对应 Component 的interactive属性上,逐个关掉用不到的交互项,保留下拉缩放和高亮提示即可。
6. 把图表做成可复用组件:封装、验证与导出截图
前面的章节能帮你把图表跑起来、调到能看,但真正交付一个长期维护的项目,还需要把图表封装成交互稳定的组件。我的习惯是最后做三件事:封装数据接口、验证性能、落地导出工具。
第一个封装点是把数据源与图表分离。不要在每个页面里直接 new 一个图表再 SetData,而是做一个简单的图表代理类,对外只暴露业务语义方法:
public class ScoreChartPresenter { private readonly BarChart _chart; public ScoreChartPresenter(BarChart chart) { _chart = chart; } public void ShowScoreByQuestion(List<string> questionIds, List<float> scores) { _chart.SetData(questionIds, scores); } public void ShowScoreComparison(List<string> groups, List<float> teamA, List<float> teamB) { _chart.SetData(groups, teamA); _chart.AddSeriesData(1, teamB); } }这样上层逻辑不需要知道图表内部是 BarChart 还是 LineChart,后续替换图表形态也不影响业务代码。
第二个验证点是性能验收清单。图表类项目上线前,我必查三项:Canvas 顶点数是否在合理范围;图表所在的 Canvas 是否与高频更新的 UI 分离;刷新数据的间隔是否满足业务秒级要求。Profiler 里重点看Mesh生成相关耗时,如果一次刷新超过 3 毫秒,就要考虑降低数据粒度或拆分子 Canvas。
第三个落地工具是截图导出。图表跑在 UGUI 上,直接截图最省事的方式是 RenderTexture 转 PNG:
public Texture2D CaptureChart(BaseChart chart, int width, int height) { var rt = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGB32); var camera = chart.canvas.rootCanvas.worldCamera; if (camera != null) { camera.targetTexture = rt; camera.Render(); camera.targetTexture = null; } var tex = new Texture2D(width, height, TextureFormat.RGBA32, false); RenderTexture.active = rt; tex.ReadPixels(new Rect(0, 0, width, height), 0, 0); tex.Apply(); RenderTexture.active = null; RenderTexture.ReleaseTemporary(rt); return tex; }这段代码的要点是:如果项目的 Canvas 是 Overlay 模式,worldCamera可能为 null,需要临时指定一个正交相机才能渲染。参数width、height要和 Canvas 的实际渲染尺寸匹配,否则导出图片会拉伸。后续要保存 PNG,用File.WriteAllBytes把tex.EncodeToPNG()写出去即可。
最后说个习惯:我拿到新版本插件,第一件事不是写功能,而是花半小时跑最小示例,确认接口、刷新、释放三条链路是通的。这样真到排期紧张的时候,我不会因为基础链路不熟而丢时间。XCharts 3.14 虽然接口变化大,但它把组件化做得很彻底,顺着它的代码结构走,遇到问题翻源码,基本都能解决。希望这篇能帮你在 3.14 上少踩几个坑。
本文还有配套的精品资源,点击获取