Unity-原生 JsonUtility 实现 JSON 与TMP UI 交互实战

适用场景:工业仿真、PLC 参数配置、单机工具存档;无需第三方插件,原生 API

目录

  1. 前言
  2. 基础原理
  3. 基础版:JSON 读写最简代码
  4. 进阶实战:TMP UI 输入 + 保存 / 读取按钮交互
  5. List 列表 JSON 存储方案
  6. 高频踩坑大全
  7. 优缺点总结

1. 前言

在 Unity 项目开发中,经常需要保存设备参数、配置信息。很多开发者直接引入Newtonsoft.Json,但中小型项目、工业仿真项目完全不需要第三方插件 。 Unity 内置JsonUtility,原生支持、跨平台、打包无冲突、轻量零依赖。

本文分为两大模块: ✅ 基础:通用 JSON 读写工具类 ✅ 进阶:结合 TMP 输入框、按钮,实现可视化配置保存加载

开发环境:Unity 2021+,TextMeshPro

整体结构:

2. 基础核心原理

核心 API

  1. JsonUtility.ToJson(obj,prettyPrint:true) 对象 → JSON 字符串
  2. JsonUtility.FromJson<T>(jsonString) JSON 字符串 → 对象

三条硬性规则(99% 报错根源)

  1. 数据模型类必须添加 [System.Serializable]
  2. 需要序列化的字段必须标记为 public
  3. JsonUtility不支持直接序列化顶层 List / 数组,需要包装类

路径选择

推荐路径:Application.persistentDataPath ✅ 编辑器、打包后均可读写 ❌ 禁止使用Application.dataPath,打包后只读,无法写入文件


3. 基础版代码实现

3.1 数据模型(PLC 配置示例)

cs 复制代码
using System;

[Serializable]
public class PlcConfigData
{
    public string Ip;
    public int Rack;
    public int Slot;
    public float Speed;
    public bool IsConnected;
}

3.2 通用静态 JSON 工具类(全局调用)

cs 复制代码
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 基础调用示例

cs 复制代码
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 编辑器)

cs 复制代码
C:\Users\用户名\AppData\LocalLow\公司名称\项目名称\plcConfig.json

AppData 为隐藏文件夹,直接粘贴路径到资源管理器地址栏打开。


4. 进阶实战:TMP UI 可视化配置

需求:

  1. TMP 输入框填写 IP、机架、槽号、速度 2.【保存按钮】读取输入框内容,写入 JSON 3.【读取按钮】加载 JSON 数据,自动回填输入框

4.1 场景准备

在 Canvas 下创建 UI 组件:

  • TMP_InputField ×4:IP 地址、机架号、槽号、速度
  • Button ×2:【保存配置】、【读取配置】 将脚本挂载到场景物体,Inspector 面板拖拽绑定组件。

4.2 UI 交互完整脚本

cs 复制代码
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);
    }
}

可选优化建议

  1. 限制输入框只能输入数字 选中 TMP InputField 组件 → ContentType 设置为 Number
  2. 启动自动加载 取消 Start 函数中 OnLoadClick() 的注释
  3. 新增 TMP 文本组件,展示「保存成功 / 失败」提示

保存数据:

保存效果:

修改数据:

读取数据:


5. List 列表数据存储方案

问题

JsonUtility 不支持直接序列化顶层数组 / List,会报错。

解决方案:外层包装类

cs 复制代码
using System;
using System.Collections.Generic;

[Serializable]
public class DataListWrapper
{
    public List<PlcConfigData> DataList;
}

使用示例

cs 复制代码
//保存列表
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. 高频踩坑大全

  1. ❌ 数据类缺少 [Serializable] 现象:生成空 json {},读取所有字段为空,无报错 ✅ 解决方案:必须添加序列化标签

  2. ❌ 字段使用 private 修饰 现象:字段无法序列化,json 看不到数据 ✅ 解决方案:序列化字段使用 public

  3. ❌ 保存后立刻读取,偶尔读取失败 原因:操作系统磁盘写入延迟 ✅ 解决方案:使用协程延迟读取

  4. ❌ 尝试序列化 Dictionary 原生 JsonUtility 不支持字典,方案:改用 List 键值对模型 / Newtonsoft.Json

  5. ❌ 使用Application.dataPath做存档路径 打包后目录只读,无法创建文件,必须使用persistentDataPath


7. 优缺点总结

✅ 优点

  • 零第三方插件,原生自带,无版本冲突
  • 跨平台兼容 Windows / Android / IOS
  • 轻量高效,工业仿真、工具项目完全够用

❌ 缺点

  • 不支持顶层数组、List
  • 原生不支持 Dictionary 类型
相关推荐
chenzhou__2 小时前
独立游戏开发日志 ①:从信号博弈到涂色对战——一次玩法重构始
笔记·后端·学习·unity·go·独立游戏
五仁烧饼3 小时前
Unity Addressables 资源依赖治理:规则约束、自动修复与全链路防护
java·unity·游戏引擎
qq_3707730917 小时前
Unity游戏xLua逆向实战:从SO版本识别到Lua脚本热替换全链路
游戏·unity·lua
mxwin19 小时前
Unity Shader 水面 Shader 教程 从 Gerstner 波浪到屏幕折射、焦散、泡沫的完整实现
unity·游戏引擎
噗噗夹的TA之旅20 小时前
Shader 学习 21:自定义 Render Feature
学习·游戏·unity·c#·游戏引擎·图形渲染
zcmodeltech1 天前
化工装置沙盘模型多设备协同控制系统设计:基于STM32与Modbus RTU的装置-流程-安全联动方案
大数据·stm32·嵌入式硬件·安全·unity·制造
暗影凋落2 天前
Unity 射线检测优化:使用 Job System 实现高性能射线批处理
unity·游戏引擎·lucene
淡海水2 天前
12-01-YooAsset工程化-Unity资源管理规范
unity·c#·游戏引擎·yooasset
BuHuaX2 天前
U3D抖微小游戏开发流程 (一)
unity·c#·游戏引擎·游戏策划