Unity-原生 JsonUtility 实现 JSON 与TMP UI 交互实战
适用场景:工业仿真、PLC 参数配置、单机工具存档;无需第三方插件,原生 API
目录
- 前言
- 基础原理
- 基础版:JSON 读写最简代码
- 进阶实战:TMP UI 输入 + 保存 / 读取按钮交互
- List 列表 JSON 存储方案
- 高频踩坑大全
- 优缺点总结
1. 前言
在 Unity 项目开发中,经常需要保存设备参数、配置信息。很多开发者直接引入Newtonsoft.Json,但中小型项目、工业仿真项目完全不需要第三方插件。 Unity 内置JsonUtility,原生支持、跨平台、打包无冲突、轻量零依赖。
本文分为两大模块: ✅ 基础:通用 JSON 读写工具类 ✅ 进阶:结合 TMP 输入框、按钮,实现可视化配置保存加载
开发环境:Unity 2021+,TextMeshPro
整体结构:
2. 基础核心原理
核心 API
JsonUtility.ToJson(obj,prettyPrint:true)对象 → JSON 字符串JsonUtility.FromJson<T>(jsonString)JSON 字符串 → 对象
三条硬性规则(99% 报错根源)
- 数据模型类必须添加
[System.Serializable] - 需要序列化的字段必须标记为
public - JsonUtility不支持直接序列化顶层 List / 数组,需要包装类
路径选择
推荐路径:Application.persistentDataPath✅ 编辑器、打包后均可读写 ❌ 禁止使用Application.dataPath,打包后只读,无法写入文件
3. 基础版代码实现
3.1 数据模型(PLC 配置示例)
using System; [Serializable] public class PlcConfigData { public string Ip; public int Rack; public int Slot; public float Speed; public bool IsConnected; }3.2 通用静态 JSON 工具类(全局调用)
using UnityEngine; using System.IO; public static class JsonTool { /// <summary> /// 保存对象到JSON文件 /// </summary> /// <typeparam name="T">数据模型</typeparam> /// <param name="data">数据源</param> /// <param name="filePath">完整保存路径</param> public static void SaveJson<T>(T data, string filePath) { // prettyPrint=true 格式化输出json,方便手动查看 string jsonStr = JsonUtility.ToJson(data, true); File.WriteAllText(filePath, jsonStr); Debug.Log($"JSON保存成功:{filePath}"); } /// <summary> /// 读取JSON文件转为对象 /// </summary> public static T LoadJson<T>(string filePath) { if (!File.Exists(filePath)) { Debug.LogWarning("JSON配置文件不存在"); return default; } string jsonStr = File.ReadAllText(filePath); return JsonUtility.FromJson<T>(jsonStr); } }3.3 基础调用示例
using UnityEngine; using System.IO; public class JsonTest : MonoBehaviour { private string SavePath => Path.Combine(Application.persistentDataPath, "plcConfig.json"); void Start() { // 构造测试数据 PlcConfigData data = new PlcConfigData() { Ip = "192.168.0.1", Rack = 0, Slot = 1, Speed = 35.5f, IsConnected = false }; // 保存 JsonTool.SaveJson(data, SavePath); // 读取 PlcConfigData loadData = JsonTool.LoadJson<PlcConfigData>(SavePath); if (loadData != null) { Debug.Log("读取IP:" + loadData.Ip); } } }文件查找路径(Windows 编辑器)
C:\Users\用户名\AppData\LocalLow\公司名称\项目名称\plcConfig.jsonAppData 为隐藏文件夹,直接粘贴路径到资源管理器地址栏打开。
4. 进阶实战:TMP UI 可视化配置
需求:
- TMP 输入框填写 IP、机架、槽号、速度 2.【保存按钮】读取输入框内容,写入 JSON 3.【读取按钮】加载 JSON 数据,自动回填输入框
4.1 场景准备
在 Canvas 下创建 UI 组件:
- TMP_InputField ×4:IP 地址、机架号、槽号、速度
- Button ×2:【保存配置】、【读取配置】 将脚本挂载到场景物体,Inspector 面板拖拽绑定组件。
4.2 UI 交互完整脚本
using UnityEngine; using TMPro; using UnityEngine.UI; using System.IO; public class PlcConfigUI : MonoBehaviour { [Header("TMP输入框绑定")] public TMP_InputField tmpIp; public TMP_InputField tmpRack; public TMP_InputField tmpSlot; public TMP_InputField tmpSpeed; [Header("按钮绑定")] public Button btnSave; public Button btnLoad; // 配置文件路径 private string SavePath => Path.Combine(Application.persistentDataPath, "plcConfig.json"); void Start() { // 绑定按钮点击事件 btnSave.onClick.AddListener(OnSaveClick); btnLoad.onClick.AddListener(OnLoadClick); // 可选:启动游戏自动加载上次保存的参数 // OnLoadClick(); } /// <summary> /// 保存按钮回调:读取UI数据 → 写入JSON /// </summary> void OnSaveClick() { PlcConfigData config = new PlcConfigData(); config.Ip = tmpIp.text; // TryParse安全转换,非法输入不会导致程序崩溃 int.TryParse(tmpRack.text, out config.Rack); int.TryParse(tmpSlot.text, out config.Slot); float.TryParse(tmpSpeed.text, out config.Speed); JsonTool.SaveJson(config, SavePath); Debug.Log("参数保存完成"); } /// <summary> /// 读取按钮回调:加载JSON → 回填UI输入框 /// </summary> void OnLoadClick() { PlcConfigData config = JsonTool.LoadJson<PlcConfigData>(SavePath); if (config == null) { Debug.LogError("配置文件不存在,无法读取!"); return; } tmpIp.text = config.Ip; tmpRack.text = config.Rack.ToString(); tmpSlot.text = config.Slot.ToString(); tmpSpeed.text = config.Speed.ToString(); Debug.Log("参数读取成功,已回填界面"); } private void OnDestroy() { // 移除监听,防止内存泄漏 btnSave.onClick.RemoveListener(OnSaveClick); btnLoad.onClick.RemoveListener(OnLoadClick); } }可选优化建议
- 限制输入框只能输入数字选中 TMP InputField 组件 →
ContentType设置为Number - 启动自动加载取消 Start 函数中
OnLoadClick()的注释 - 新增 TMP 文本组件,展示「保存成功 / 失败」提示
保存数据:
保存效果:
修改数据:
读取数据:
5. List 列表数据存储方案
问题
JsonUtility 不支持直接序列化顶层数组 / List,会报错。
解决方案:外层包装类
using System; using System.Collections.Generic; [Serializable] public class DataListWrapper { public List<PlcConfigData> DataList; }使用示例
//保存列表 DataListWrapper wrapper = new DataListWrapper(); wrapper.DataList = new List<PlcConfigData>(); wrapper.DataList.Add(new PlcConfigData() { Ip = "192.168.0.2", Rack = 0, Slot = 2 }); JsonTool.SaveJson(wrapper, SavePath); //读取列表 DataListWrapper loadWrap = JsonTool.LoadJson<DataListWrapper>(SavePath); if (loadWrap != null) { foreach (var item in loadWrap.DataList) { Debug.Log(item.Ip); } }6. 高频踩坑大全
❌ 数据类缺少
[Serializable]现象:生成空 json{},读取所有字段为空,无报错 ✅ 解决方案:必须添加序列化标签❌ 字段使用 private 修饰 现象:字段无法序列化,json 看不到数据 ✅ 解决方案:序列化字段使用 public
❌ 保存后立刻读取,偶尔读取失败 原因:操作系统磁盘写入延迟 ✅ 解决方案:使用协程延迟读取
❌ 尝试序列化 Dictionary 原生 JsonUtility 不支持字典,方案:改用 List 键值对模型 / Newtonsoft.Json
❌ 使用
Application.dataPath做存档路径 打包后目录只读,无法创建文件,必须使用persistentDataPath
7. 优缺点总结
✅ 优点
- 零第三方插件,原生自带,无版本冲突
- 跨平台兼容 Windows / Android / IOS
- 轻量高效,工业仿真、工具项目完全够用
❌ 缺点
- 不支持顶层数组、List
- 原生不支持 Dictionary 类型
