
1. 项目概述为什么Unity开发者需要Newtonsoft.Json在Unity项目里处理JSON数据几乎是每个开发者都会遇到的日常。Unity自带的JsonUtility类用过的朋友都知道它确实轻量、开箱即用但限制也多得让人头疼不支持字典、不能序列化私有字段、对复杂继承和多态结构的处理几乎为零。当你需要把一个包含字典、列表、自定义类型嵌套的游戏存档对象序列化成JSON时JsonUtility就会立刻“罢工”抛出一个冷冰冰的NotSupportedException。这时候一个更强大、更灵活的JSON库就成了刚需。Newtonsoft.Json又名Json.NET在.NET生态里是当之无愧的王者它以极高的灵活性、丰富的特性和强大的性能著称。但直接把它扔进Unity项目里大概率会碰到各种编译错误和运行时异常因为Unity使用的是经过裁剪的、特定版本的.NET框架或.NET Standard并且其IL2CPP编译后端对反射和泛型的处理与标准.NET有所不同。“Unity项目集成Newtonsoft.Json完整实践指南”这个标题瞄准的正是这个痛点。它不是一个简单的“把DLL拖进Plugins文件夹”的教程而是一份涵盖从选型、集成、适配、到高级功能使用和性能优化的全链路指南。本文的目标就是让你能绕过所有坑在Unity中安全、高效地驾驭Newtonsoft.Json这把瑞士军刀无论是处理简单的配置表还是复杂的网络协议数据都能得心应手。2. 核心选型与集成方案深度解析2.1 Newtonsoft.Json-for-Unity官方推荐的兼容版本直接去NuGet下载最新的Newtonsoft.Json包比如13.0.3导入Unity是最快踩坑的方法。Unity的脚本运行时环境特殊尤其是启用IL2CPP后端以发布到WebGL或iOS平台时标准的Newtonsoft.Json会因依赖某些不被支持的API如System.Reflection.Emit而编译失败。正确的起点是Newtonsoft.Json-for-Unity。这不是一个山寨版而是由社区维护、并得到Newtonsoft.Json原作者James Newton-King认可的官方兼容版本。它的核心价值在于针对Unity编译环境适配它移除了标准库中与Unity/IL2CPP不兼容的代码路径例如DynamicMethod的创建确保基础功能能在所有Unity支持的平台上编译通过。提供版本化支持它通常提供多个基础版本供选择如13.0.0、12.0.0等。对于绝大多数Unity项目尤其是使用较新Unity版本的项目建议选择基于13.0.0的兼容包以获得最新的功能和安全修复。开箱即用的包格式它通常以.unitypackage或通过Unity的Package ManagerUPM提供集成过程对Unity项目结构友好。注意在Asset Store或GitHub上寻找时请认准“Newtonsoft.Json for Unity”或类似描述并查看其更新日期和Issue列表确保其维护状态良好。2.2 备选方案评估System.Text.Json 与 LitJson除了Newtonsoft.JsonUnity开发者还有其他选择了解它们有助于你做出更合适的决策。System.Text.Json (STJ) 这是微软官方推出的新一代高性能JSON库集成在.NET Core 3.0及以上版本中。它的最大优势是性能在序列化/反序列化速度上远超Newtonsoft.Json并且内存分配更少。Unity兼容性Unity 2021.2及以上版本当使用.NET Standard 2.1或.NET 6/7/8作为脚本后端时理论上可以引用System.Text.Json。但是这存在巨大风险。Unity对这些新版本.NET的支持并非100%完整且IL2CPP转换过程中可能遇到未知问题。此外STJ的API设计更偏向于新的SpanT和异步模型在Unity的同步主线程环境中优势不一定能完全发挥且其灵活性如自定义转换器的学习曲线可能更陡。结论除非你的项目是全新的、基于高版本Unity且愿意承担前沿技术的潜在风险并极度追求性能否则不推荐在大型生产项目中将STJ作为首选。Newtonsoft.Json-for-Unity的稳定性和社区支持目前是更安全的选择。LitJson 这是一个轻量级的、纯C#编写的JSON库很早就在Unity社区流行。它非常小巧编译后体积小且天然兼容Unity和IL2CPP。优势零依赖、集成简单、体积小、兼容性极佳。劣势功能相对薄弱性能在大多数场景下不如Newtonsoft.Json对复杂类型和自定义序列化的支持不够强大且项目维护活跃度一般。结论适合处理极其简单、结构固定的JSON数据且对包体大小极度敏感的超轻量级项目如某些小游戏或工具。对于中大型商业项目Newtonsoft.Json-for-Unity在功能、性能和可维护性上仍是更优解。集成决策流程图 对于大多数Unity项目我的建议路径非常明确需求是否极简且固定是 - 考虑LitJson。项目是否全新且团队愿意为极致性能挑战兼容性是 - 谨慎评估System.Text.Json。其他所有情况涵盖90%以上项目- 选择Newtonsoft.Json-for-Unity。它提供了功能、性能、稳定性和社区支持的最佳平衡。3. 分步集成与基础配置实战3.1 通过Unity Package Manager (UPM) 集成推荐这是目前最干净、最便于管理的集成方式尤其适合使用Unity 2019.4版本的项目。打开清单文件在项目资源管理器中找到并打开Packages/manifest.json文件。添加包仓库在manifest.json的scopedRegistries部分添加Newtonsoft.Json-for-Unity的包仓库。通常它来自OpenUPM或GitHub Packages。你需要确切的仓库URL和scope。例如请以该包最新官方文档为准{ scopedRegistries: [ { name: Package Registry Name, url: https://package.openupm.com, scopes: [com.unity.nuget.newtonsoft-json] } ], dependencies: { ... } }添加依赖在dependencies部分加入对应版本的包。dependencies: { com.unity.nuget.newtonsoft-json: 13.0.102, ... }保存并等待解析保存manifest.json文件Unity编辑器会自动解析并下载该包。完成后你可以在Packages目录下看到Newtonsoft Json。实操心得使用UPM集成包依赖关系清晰升级和移除都非常方便。务必从官方文档获取正确的仓库信息和包名/版本避免使用来源不明的包。3.2 通过 .unitypackage 文件集成如果无法使用UPM或者你下载到了.unitypackage文件这是传统但有效的方法。获取包文件从GitHub Releases页面或Asset Store下载Newtonsoft.Json-for-Unity.x.x.x.unitypackage。导入项目在Unity编辑器中双击该.unitypackage文件或通过Assets - Import Package - Custom Package...导入。选择导入内容在弹出的窗口中通常全选所有文件点击Import。这会将必要的DLL和链接文件导入到项目的Assets文件夹下通常是Assets/Packages/Newtonsoft.Json这样的结构。注意事项这种方式会将库文件作为项目资源管理可能会被误操作。建议导入后检查一下是否有示例场景或文档被一并导入并根据需要决定是否删除以保持项目整洁。3.3 基础功能验证与初步配置集成完成后必须写一个简单的测试脚本来验证功能是否正常并了解基本配置。创建测试脚本与数据类using Newtonsoft.Json; using UnityEngine; [System.Serializable] public class PlayerData { public string PlayerName; public int Level; public Vector3 Position; // Unity内置类型 public string[] Inventory; // 注意默认情况下属性get;set;和私有字段不会被序列化除非使用[JsonProperty] } public class NewtonsoftJsonTest : MonoBehaviour { void Start() { // 1. 创建测试对象 PlayerData data new PlayerData { PlayerName 开发者, Level 99, Position new Vector3(1, 2, 3), Inventory new string[] { Sword, Shield, Potion } }; // 2. 序列化对象 - JSON字符串 string jsonString JsonConvert.SerializeObject(data, Formatting.Indented); Debug.Log(序列化结果\n jsonString); // 输出会是美观的格式化JSON包含Vector3被序列化为{x,y,z}对象。 // 3. 反序列化JSON字符串 - 对象 PlayerData deserializedData JsonConvert.DeserializeObjectPlayerData(jsonString); Debug.Log($反序列化后玩家名{deserializedData.PlayerName}, 等级{deserializedData.Level}); Debug.Log($位置{deserializedData.Position}); } }将此脚本挂载到场景中任意GameObject上运行。如果能在Console中看到正确的JSON输出和反序列化后的数据恭喜你基础集成成功了全局序列化设置 Newtonsoft.Json的强大之处在于其高度可配置的JsonSerializerSettings。建议在项目初期建立一个全局配置单例或静态类来统一管理设置避免散落各处。public static class JsonSettings { public static readonly JsonSerializerSettings Default new JsonSerializerSettings { Formatting Formatting.None, // 生产环境为了节省空间通常不格式化 NullValueHandling NullValueHandling.Ignore, // 忽略null值减少JSON体积 DefaultValueHandling DefaultValueHandling.Ignore, // 忽略类型默认值 ContractResolver new DefaultContractResolver { NamingStrategy new CamelCaseNamingStrategy() // 使用驼峰命名JavaScript风格 }, Converters new ListJsonConverter // 注册自定义转换器后文详述 { // 例如new UnityVector3Converter() } }; // 提供便捷方法 public static string Serialize(object obj) { return JsonConvert.SerializeObject(obj, Default); } public static T DeserializeT(string json) { return JsonConvert.DeserializeObjectT(json, Default); } }这样在整个项目中就可以使用JsonSettings.Serialize(data)和JsonSettings.DeserializePlayerData(json)保证行为一致。4. 高级特性与Unity特殊类型处理4.1 处理Unity特有类型Vector3, Quaternion, Color等Unity的内置类型如Vector3,Quaternion,Color,Rect不是简单的POCOPlain Old CLR ObjectNewtonsoft.Json无法直接理解它们的序列化格式。直接序列化会产生包含所有公共字段的复杂对象可能不是你想要的比如网络传输时希望更精简。解决方案是使用自定义JsonConverter。例如创建一个将Vector3序列化为[x, y, z]数组的转换器using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; public class Vector3Converter : JsonConverterVector3 { public override void WriteJson(JsonWriter writer, Vector3 value, JsonSerializer serializer) { // 序列化为JSON数组 [x, y, z] writer.WriteStartArray(); writer.WriteValue(value.x); writer.WriteValue(value.y); writer.WriteValue(value.z); writer.WriteEndArray(); } public override Vector3 ReadJson(JsonReader reader, Type objectType, Vector3 existingValue, bool hasExistingValue, JsonSerializer serializer) { // 从JSON数组反序列化 JArray array JArray.Load(reader); return new Vector3((float)array[0], (float)array[1], (float)array[2]); } public override bool CanRead true; public override bool CanWrite true; }然后你有两种方式使用它全局注册将其添加到上述JsonSettings.Default.Converters列表中对所有Vector3生效。特性标注在类的属性上使用[JsonConverter(typeof(Vector3Converter))]。public class TransformData { [JsonConverter(typeof(Vector3Converter))] public Vector3 Position { get; set; } }实操心得对于网络传输将Vector3序列化为数组比默认的对象形式{x:1,y:2,z:3}更节省带宽。你可以根据需求为ColorRGBA数组、QuaternionXYZW数组等创建类似的转换器。4.2 控制序列化过程特性Attributes的运用Newtonsoft.Json提供了丰富的特性来控制序列化行为这是其灵活性的核心。[JsonProperty]最常用的特性。可以指定JSON中的属性名、顺序、是否必需等。public class Item { [JsonProperty(id)] // 在JSON中字段名为 id public int ItemId { get; set; } [JsonProperty(name, Order -1)] // Order为-1使其在JSON中靠前 public string DisplayName { get; set; } [JsonProperty(Required Required.Always)] // 反序列化时此字段必须存在 public string Type { get; set; } }[JsonIgnore]完全忽略该字段或属性不参与序列化和反序列化。常用于存储临时计算数据或敏感信息。[JsonConverter]如上所示为特定属性指定自定义转换器。[ObjectCreationHandling]控制反序列化时是替换现有对象还是复用并填充其属性。在处理Unity的MonoBehaviour或ScriptableObject时可能需要关注。4.3 处理多态类型与继承游戏数据中常有继承关系。例如有一个BaseItem派生出WeaponItem和ConsumableItem。反序列化时Newtonsoft.Json需要知道如何根据JSON数据创建正确的子类实例。使用TypeNameHandling 这是最简单但有安全风险的方法。它会在JSON中嵌入类型信息。var settings new JsonSerializerSettings { TypeNameHandling TypeNameHandling.Auto // 或 Objects, All }; string json JsonConvert.SerializeObject(itemList, settings); // json中会包含类似 $type: MyGame.WeaponItem, Assembly-CSharp 的信息警告TypeNameHandling在反序列化不受信任的JSON数据时如来自网络可能导致安全漏洞反序列化攻击。在Unity网络通信中强烈不建议使用。推荐方案自定义JsonConverter或使用JsonSubTypes库 更安全的做法是在JSON数据中包含一个明确的类型标识符如itemType: Weapon然后编写一个自定义的JsonConverter来根据这个标识符创建对应的对象。社区也有成熟的库如JsonSubTypes需确认其Unity兼容性可以通过特性优雅地处理此问题。5. 性能优化与内存管理实战在Unity中尤其是移动端或高频调用的场景如每帧处理网络消息JSON序列化的性能开销不容忽视。5.1 缓存序列化设置与合约Contract每次调用JsonConvert.SerializeObject/DeserializeObject时如果不传递settings参数Newtonsoft.Json会为类型创建一个临时的JsonSerializer并解析其合约Contract即决定如何序列化该类型的元数据。这个解析过程反射是有开销的。优化策略缓存JsonSerializer。public static class JsonSerializerCache { private static readonly DictionaryType, JsonSerializer _serializerCache new DictionaryType, JsonSerializer(); public static JsonSerializer GetSerializer(JsonSerializerSettings settings) { // 如果settings总是相同可以只缓存一个。如果不同可以用settings的哈希值作为Key的一部分。 // 这里简化演示为每种设置缓存一个序列化器。 var key settings?.GetHashCode() ?? 0; // 实际实现需要更严谨的Key生成例如基于settings的各个属性组合。 // 对于固定settings的项目直接缓存一个全局实例最简单。 return JsonSerializer.CreateDefault(settings); // CreateDefault会使用全局默认设置合并传入的settings } // 更激进的优化为特定类型T缓存序列化器如果settings固定 private static JsonSerializer _cachedSerializer; public static string FastSerializeT(T obj) { if (_cachedSerializer null) { var settings JsonSettings.Default; // 你的全局设置 _cachedSerializer JsonSerializer.Create(settings); } using (var sw new StringWriter()) using (var writer new JsonTextWriter(sw)) { _cachedSerializer.Serialize(writer, obj); return sw.ToString(); } } }对于高频序列化同一种类型的情况使用缓存的JsonSerializer可以避免重复的合约解析开销。5.2 使用流式API处理大型JSON当需要处理非常大的JSON文件如整个游戏世界的配置表时将整个文件读入内存再反序列化可能会造成内存压力。Newtonsoft.Json提供了流式读取器 (JsonTextReader) 和写入器 (JsonTextWriter)允许你按需读取/写入。using (StreamReader sr new StreamReader(large_config.json)) using (JsonTextReader reader new JsonTextReader(sr)) { while (reader.Read()) { if (reader.TokenType JsonToken.StartObject reader.Path items[0]) { // 定位到数组的第一个元素开始反序列化单个Item var serializer new JsonSerializer(); Item item serializer.DeserializeItem(reader); // 处理item... // 然后可以继续reader.Read()处理下一个而不必加载整个数组到内存 } } }这种方式内存效率高但代码更复杂。需要权衡开发复杂度与性能需求。5.3 避免装箱和减少分配使用强类型方法DeserializeObjectT(string)比非泛型的DeserializeObject(string, Type)更好。重用对象对于需要频繁更新的对象如玩家状态考虑在反序列化时提供现有对象实例进行填充而不是每次都创建新对象。可以使用JsonConvert.PopulateObject(string, object)方法。PlayerStats currentStats ...; string updatedJson ...; JsonConvert.PopulateObject(updatedJson, currentStats); // 更新现有对象的字段注意字符串分配序列化产生的字符串是新的内存分配。在热路径如Update循环中频繁序列化小对象可能触发GC。可以考虑使用StringBuilder结合JsonTextWriter进行更可控的字符串构建或者使用对象池来复用字符串缓冲区高级优化。6. 平台特定问题与IL2CPP深度适配6.1 AOT编译与代码裁剪问题Unity在构建到iOS、WebGL、以及部分Android如IL2CPP后端平台时使用的是Ahead-Of-Time (AOT) 编译。这意味着所有执行的代码必须在编译时就被确定。Newtonsoft.Json大量依赖反射Reflection来动态获取和操作类型信息这在AOT环境下可能出问题。常见错误ExecutionEngineException: Attempting to call method Newtonsoft.Json.Utilities.ReflectionDelegateFactory... for which no ahead of time (AOT) code was generated.解决方案使用link.xml文件在项目的Assets文件夹下创建一个名为link.xml的文件。这个文件用于告诉Unity的代码裁剪器Linker不要裁剪掉指定的程序集、命名空间或类型。linker assembly fullnameNewtonsoft.Json preserveall/ !-- 或者更精细地控制 -- assembly fullnameNewtonsoft.Json namespace fullnameNewtonsoft.Json.Serialization preserveall/ namespace fullnameNewtonsoft.Json.Converters preserveall/ type fullnameNewtonsoft.Json.JsonConvert preserveall/ /assembly !-- 同样需要保留你使用了反射的自定义类型 -- assembly fullnameAssembly-CSharp namespace fullnameMyGame.DataContracts preserveall/ /assembly /linkerpreserveall会保留该程序集/命名空间下的所有内容这可能会增加最终包体大小。在生产环境中可以尝试更精细的配置只保留真正用到的类型。6.2 反射与泛型方法的替代方案在某些极端情况下即使使用了link.xmlIL2CPP可能仍然无法正确处理通过反射动态创建的泛型方法调用。Newtonsoft.Json-for-Unity版本已经处理了大部分此类问题但如果你编写了高度依赖反射的自定义转换器或合约解析器仍需注意。建议尽量使用JsonConverter特性或已知的、固定的类型减少运行时动态类型解析。如果必须用反射考虑在AOT平台下使用预生成代码Code Generation或接口约束等模式来规避。不过这属于比较高级的优化在Newtonsoft.Json的常规使用中较少需要。6.3 WebGL平台的特殊性WebGL平台除了AOT问题还有单线程和性能限制。避免主线程阻塞反序列化非常大的JSON数据可能在WebGL上造成主线程卡顿。考虑将大文件拆分成小块。使用JsonTextReader进行流式、增量式处理。如果数据来自网络在Worker线程通过Unity的WebWorker或jslib中完成反序列化后再传回主线程这需要额外的架构设计。内存碎片WebGL的JavaScript内存模型与.NET不同频繁创建和丢弃大型字符串可能加剧内存碎片。重用StringBuilder和缓冲区有助于缓解。7. 常见问题排查与调试技巧7.1 编译错误与运行时异常速查表问题现象可能原因解决方案导入后大量CSxxxx编译错误1. 导入了错误非for Unity的Newtonsoft.Json DLL。2. Unity版本与包版本不兼容。3. 其他包冲突。1. 确认使用的是Newtonsoft.Json-for-Unity包。2. 检查包文档的Unity版本要求降级包或升级Unity。3. 尝试新建空白项目导入隔离问题。在编辑器运行正常打包后尤其是iOS/WebGL报AOT或MissingMethodExceptionIL2CPP代码裁剪掉了必要的反射代码。1. 在Assets下添加或完善link.xml文件。2. 确保使用了Newtonsoft.Json-for-Unity的AOT兼容版本。序列化时忽略了我想要的字段/属性1. 字段是私有的且未标记[JsonProperty]。2. 使用了DefaultValueHandling.Ignore且字段为默认值。3. 合约解析器ContractResolver配置有误。1. 为私有字段添加[JsonProperty]特性。2. 检查JsonSerializerSettings。3. 使用[JsonIgnore]的反面是确保想要序列化的成员是公共字段或带有getter/setter的属性。反序列化时对象为null或字段为默认值1. JSON字符串格式错误或为空。2. JSON中的属性名与C#类成员名不匹配大小写、命名规范。3. 缺少必需的字段Required Required.Always。4. 自定义JsonConverter逻辑有误。1. 打印或调试JSON字符串验证其正确性。2. 使用[JsonProperty(jsonName)]指定映射。3. 检查异常信息或使用Error事件处理反序列化错误。4. 调试自定义转换器的ReadJson方法。循环引用导致栈溢出或数据膨胀对象之间存在双向引用如Parent.Child和Child.Parent。在JsonSerializerSettings中设置ReferenceLoopHandling ReferenceLoopHandling.Ignore或Serialize。对于已知结构也可以用[JsonIgnore]手动断开一环。序列化Unity组件如MonoBehaviour时报错试图序列化整个UnityEngine.Object这通常不是你想要做的。只序列化组件的数据部分自定义的数据类而不是组件本身。在组件中提供Save()和Load(Data)方法。7.2 使用错误事件进行调试Newtonsoft.Json提供了Error事件可以在序列化/反序列化出错时捕获异常而不中断流程非常适合日志记录和调试。JsonSerializerSettings settings new JsonSerializerSettings { Error (sender, args) { // args.ErrorContext.Error 包含了具体的异常 Debug.LogError($JSON处理错误: {args.ErrorContext.Error.Message}); Debug.LogError($路径: {args.ErrorContext.Path}); // 标记错误为已处理防止抛出异常 args.ErrorContext.Handled true; } }; var obj JsonConvert.DeserializeObjectMyClass(problematicJson, settings);7.3 性能分析与工具Unity Profiler在Profiler的CPU使用率模块中观察JsonConvert相关方法的耗时。如果发现其占用过高考虑应用第5节的优化策略。自定义性能测试对于关键路径编写简单的性能测试代码比较不同设置或不同数据量下的耗时。System.Diagnostics.Stopwatch sw new System.Diagnostics.Stopwatch(); sw.Start(); for (int i 0; i 1000; i) { JsonConvert.SerializeObject(testData); } sw.Stop(); Debug.Log($序列化1000次耗时: {sw.ElapsedMilliseconds} ms);8. 实战案例构建一个可配置的JSON数据管理器最后我们综合运用以上知识构建一个简单的、用于管理游戏配置如物品表、关卡数据的JSON数据管理器。设计目标支持热重载编辑器下修改JSON文件后游戏内数据自动更新。类型安全使用泛型。处理Unity特殊类型。具备简单的缓存机制。实现步骤定义数据模型与存储路径// Assets/Scripts/Data/ItemData.cs [System.Serializable] public class ItemData { [JsonProperty(id)] public int Id { get; set; } [JsonProperty(name)] public string Name { get; set; } [JsonProperty(icon)] public string IconName { get; set; } // 图标资源名 [JsonProperty(stats)] public ItemStats Stats { get; set; } } public class ItemStats { /* ... */ } // 存储路径常量 public static class DataPaths { public const string Items Data/Items.json; // 在Unity中可以将JSON文件放在Resources文件夹下或使用Addressables/StreamingAssets。 // 这里以Resources为例。 }创建单例数据管理器// Assets/Scripts/Managers/JsonDataManager.cs using Newtonsoft.Json; using System.Collections.Generic; using System.IO; using UnityEngine; public class JsonDataManager : MonoBehaviour { public static JsonDataManager Instance { get; private set; } private DictionarySystem.Type, object _dataCache new DictionarySystem.Type, object(); private JsonSerializerSettings _settings; void Awake() { if (Instance ! null Instance ! this) { Destroy(gameObject); return; } Instance this; DontDestroyOnLoad(gameObject); InitializeSettings(); } void InitializeSettings() { _settings new JsonSerializerSettings { Formatting Formatting.Indented, NullValueHandling NullValueHandling.Ignore, Converters { new Vector3Converter() } // 注册自定义转换器 }; } public T LoadDataT(string resourcePath) where T : class { // 1. 检查缓存 if (_dataCache.TryGetValue(typeof(T), out var cachedObj)) { return cachedObj as T; } // 2. 从Resources加载JSON文本 TextAsset textAsset Resources.LoadTextAsset(resourcePath); if (textAsset null) { Debug.LogError($找不到资源: {resourcePath}); return null; } // 3. 反序列化 T data JsonConvert.DeserializeObjectT(textAsset.text, _settings); if (data ! null) { _dataCache[typeof(T)] data; } // 4. 仅编辑器监听文件变化以实现热重载 #if UNITY_EDITOR StartCoroutine(MonitorFileChangesT(resourcePath, textAsset)); #endif return data; } #if UNITY_EDITOR private System.Collections.IEnumerator MonitorFileChangesT(string path, TextAsset asset) where T : class { // 简化示例实际应用中需要更健壮的文件系统监听 // 可以使用 FileSystemWatcher 或定期检查文件最后修改时间 string fullPath UnityEditor.AssetDatabase.GetAssetPath(asset); var lastWriteTime File.GetLastWriteTime(fullPath); while (true) { yield return new WaitForSeconds(2); // 每2秒检查一次 var currentWriteTime File.GetLastWriteTime(fullPath); if (currentWriteTime lastWriteTime) { Debug.Log($检测到 {path} 文件变化重新加载数据。); lastWriteTime currentWriteTime; TextAsset newTextAsset Resources.LoadTextAsset(path); if (newTextAsset ! null) { T newData JsonConvert.DeserializeObjectT(newTextAsset.text, _settings); if (newData ! null) { _dataCache[typeof(T)] newData; // 可以在这里触发一个事件通知其他系统数据已更新 Debug.Log($数据 {typeof(T).Name} 热重载完成。); } } } } } #endif // 清空缓存例如在切换语言或重置游戏时 public void ClearCache() { _dataCache.Clear(); } }在游戏中使用public class ItemManager : MonoBehaviour { private ListItemData _allItems; void Start() { LoadItemData(); } void LoadItemData() { _allItems JsonDataManager.Instance.LoadDataListItemData(DataPaths.Items); if (_allItems ! null) { Debug.Log($成功加载 {_allItems.Count} 个物品。); foreach (var item in _allItems) { Debug.Log($物品: {item.Name}, ID: {item.Id}); } } } public ItemData GetItemById(int id) { return _allItems?.Find(item item.Id id); } }这个管理器提供了基础的数据加载、缓存和编辑器下的热重载功能。在实际项目中你可能需要根据资源管理方案Addressables, AssetBundles调整加载路径并增加更完善的内存管理和错误处理机制。但它的核心——安全、高效地使用Newtonsoft.Json处理游戏数据——已经得到了完整的体现。