Unity之Serialized序列化:从原理到实践

打印 上一主题 下一主题

主题 1035|帖子 1035|积分 3105

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
内容将会连续更新,有错误的地方欢迎指正,谢谢!
 
   Unity之Serialized序列化:从原理到实践       
   
             TechX 对峙将创新的科技带给世界!

拥有更好的学习体验 —— 不停积极,不停进步,不停探索
      
   
             TechX —— 心探索、心进取!

助力快速把握 Serialized序列化 学习

为初学者节流名贵的学习时间,克制困惑!
      
  


  

一、 引言


在Unity开发中,序列化(Serialization) 是贯穿整个开发流程的核心技术。无论是游戏对象的长期化存储、Inspector面板的变量显示,照旧跨平台数据交换,都离不开序列化的支持。对于编辑器扩展开发而言,深入明白序列化机制更是实现自界说工具和高效数据管理的关键。
为什么必要关注序列化?


  • 数据长期化:保存玩家存档、配置数据、场景状态。
  • Inspector交互:在编辑器中直观调整脚本参数。
  • 跨平台兼容:通过标准格式(如JSON)实现不同平台间的数据交换。
  • 性能优化:合理的序列化策略可淘汰内存占用和加载时间。


二、什么是序列化?


序列化是将对象转换为可存储或传输的格式(如二进制、JSON等)的过程,使得数据能在不同系统或平台间共享。反向的反序列化则是将这些格式数据还原为原始对象的过程。
在Unity中,序列化的典型场景包括:


  • 场景文件:将游戏对象条理布局和组件数据保存为.unity文件
  • Prefab:保存预设对象的模板
  • Inspector面板:显示并修改脚本的序列化字段
  • 数据长期化:存档、配置文件或网络通信


三、Unity游戏对象的序列化


游戏对象与组件的自动序列化
Unity的GameObject和Component(如Transform、Renderer等)默认支持序列化。其数据保存在场景或Prefab文件中,包括层级关系、组件属性等。
场景文件示例
打开一个.unity场景文件,可以看到类似以下布局的序列化数据:
  1. --- !u!1 &12345
  2. GameObject:
  3.   m_Name: Player
  4.   m_Component:
  5.   - component: {fileID: 123456}
  6. --- !u!4 &123456
  7. Transform:
  8.   m_LocalPosition: {x: 0, y: 0, z: 0}
  9.   m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
复制代码
序列化的限制
并非所有类型都能被Unity自动序列化。以下环境会导致字段不被序列化:


  • 静态字段(static)
  • 非继承自UnityEngine.Object的类引用
  • 标志为[NonSerialized]的字段
  • 属性(Properties):仅支持字段序列化。
  • 未标志的私有字段:需使用[SerializeField]。
  • 某些引用类型:如Dictionary(需自界说序列化)。


四、脚本变量的序列化


通过特性标志,开发者可以控制脚本中哪些字段必要序列化并在Inspector中显示。 1、 [SerializeField]:私有字段的序列化


Unity默认只会序列化public字段。若需序列化私有或受掩护字段,可使用[SerializeField]。
  1. public class Player : MonoBehaviour {
  2.     [SerializeField]
  3.     private int health = 100;  // Inspector中可见
  4. }
复制代码
2、 [System.Serializable]:自界说类的序列化


自界说类或布局体需添加[System.Serializable]属性,才能在Inspector中显示或被Unity序列化。
  1. [System.Serializable]
  2. public class Weapon {
  3.     public string name;
  4.     public int damage;
  5. }
  6. public class Inventory : MonoBehaviour {
  7.     public List<Weapon> weapons;  // 列表中每个Weapon属性都可在Inspector编辑
  8. }
复制代码
最佳实践
克制过分序列化:仅序列化须要数据以淘汰内存开销
版本兼容性:修改序列化类时注意向后兼容性(如添加新字段不影响旧数据)


五、ScriptableObject序列化


ScriptableObject是Unity提供的一个功能强盛的工具,通常用于保存游戏数据、配置数据等。ScriptableObject在Unity中是非常重要的,它不仅可以或许序列化数据,还可以或许让你克制创建多个实例化的对象。
ScriptableObject核心特性:


  • 独立数据容器:不与场景绑定,保存于.asset文件
  • 运行时修改长期化:编辑器模式下修改可保存到项目
  • 高效引用系统:作为资产被多个对象引用
  1. using UnityEngine;
  2. #if UNITY_EDITOR
  3. using UnityEditor;
  4. #endif
  5. [System.Serializable]
  6. public class SkillEffect {
  7.     public enum EffectType { Damage, Heal, Buff }
  8.     public EffectType type;
  9.     public float value;
  10.     public float duration;
  11. }
  12. [System.Serializable]
  13. public class SkillLevel {
  14.     [Range(1, 100)]
  15.     public int requiredLevel = 1;
  16.     public float manaCost;
  17.     public SkillEffect[] effects;
  18. }
  19. // 主ScriptableObject类
  20. [CreateAssetMenu(fileName = "New Skill", menuName = "RPG/Skill Config")]
  21. public class SkillConfig : ScriptableObject {
  22.     public string skillName;
  23.     public Sprite icon;
  24.     public float baseCooldown;
  25.    
  26.     [TextArea(3, 5)]
  27.     public string description;
  28.    
  29.     [Space]
  30.     public SkillLevel[] levels = new SkillLevel[5]; // 最多5级技能
  31. }
  32. public class SkillSystemTest : MonoBehaviour {
  33.     void Start() {
  34.         // 创建临时技能配置
  35.         SkillConfig tempSkill = CreateSkill("雷电术",
  36.             new SkillLevel {
  37.                 requiredLevel = 5,
  38.                 manaCost = 30,
  39.                 effects = new[] {
  40.                     new SkillEffect {
  41.                         type = SkillEffect.EffectType.Damage,
  42.                         value = 50,
  43.                         duration = 2
  44.                     }
  45.                 }
  46.             }
  47.         );
  48.         // 在编辑器模式下保存资产
  49.         #if UNITY_EDITOR
  50.         AssetDatabase.CreateAsset(tempSkill, "Assets/Skills/Lightning.asset");
  51.         AssetDatabase.SaveAssets();
  52.         #endif
  53.     }
  54.     SkillConfig CreateSkill(string name, params SkillLevel[] levels) {
  55.         SkillConfig config = ScriptableObject.CreateInstance<SkillConfig>();
  56.         config.skillName = name;
  57.         config.levels = levels;
  58.         return config;
  59.     }
  60. }
复制代码


六、常见序列化格式


1、 JSON序列化(Newtonsoft.Json)


为什么选择Newtonsoft?


  • 原生JsonUtility不足:不支持字典、私有字段嵌套类
  • Newtonsoft特性:高效机动,支持复杂类型转换
  • 安装方法:通过Unity Package Manager添加com.unity.nuget.newtonsoft-json
  1. using Newtonsoft.Json;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. // 复杂数据结构示例(包含嵌套类和字典)
  5. [System.Serializable]
  6. public class Player {
  7.     public string name;
  8.     [SerializeField]
  9.     private int _health; //私有字段支持序列化
  10.     public Dictionary<string, int> skills = new Dictionary<string, int>();
  11.    
  12.     // 自定义Vector3序列化(需转换)
  13.     [JsonIgnore]
  14.     public Vector3 position;
  15.    
  16.     [JsonProperty("position")]
  17.     private Vector3Serializable PositionSerializable {
  18.         get => new Vector3Serializable(position);
  19.         set => position = value.ToVector3();
  20.     }
  21. }
  22. // Vector3序列化辅助类
  23. [System.Serializable]
  24. public struct Vector3Serializable {
  25.     public float x;
  26.     public float y;
  27.     public float z;
  28.     public Vector3Serializable(Vector3 v) {
  29.         x = v.x;
  30.         y = v.y;
  31.         z = v.z;
  32.     }
  33.     public Vector3 ToVector3() => new Vector3(x, y, z);
  34. }
  35. public class JsonExample : MonoBehaviour {
  36.     void Start() {
  37.         Player player = new Player {
  38.             name = "Arthur",
  39.             _health = 200,
  40.             position = new Vector3(10, 2, 5)
  41.         };
  42.         player.skills.Add("Attack", 85);
  43.         player.skills.Add("Defense", 60);
  44.         // 序列化
  45.         string json = JsonConvert.SerializeObject(player, Formatting.Indented);
  46.         Debug.Log("JSON Output:\n" + json);
  47.         
  48.         // 反序列化
  49.         Player loadedPlayer = JsonConvert.DeserializeObject<Player>(json);
  50.         Debug.Log($"Position: {loadedPlayer.position}");
  51.     }
  52. }
复制代码
控制台输出:
  1. {
  2.   "name": "Arthur",
  3.   "skills": {
  4.     "Attack": 85,
  5.     "Defense": 60
  6.   },
  7.   "position": {
  8.     "x": 10.0,
  9.     "y": 2.0,
  10.     "z": 5.0
  11.   }
  12. }
复制代码
关键解释:


  • [JsonIgnore]:标志不参与序列化的字段
  • [JsonProperty]:自界说字段映射名称
  • Vector3Serializable:必要手动处理处罚Unity引擎特有类型

2、CSV序列化(CsvHelper库)


CsvHelper核心功能


  • 自动映射类属性到CSV列
  • 处理处罚标题行复杂数据类型
  • 安装方法:通过NuGet安装CsvHelper
  1. using CsvHelper;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using UnityEngine;
  6. // CSV数据模型类
  7. public class EnemyData {
  8.     public int Id { get; set; }
  9.     public string Name { get; set; }
  10.     public float Speed { get; set; }
  11.     public string PrefabPath { get; set; }
  12. }
  13. public class CsvExample : MonoBehaviour {
  14.     private string csvPath = "Enemies.csv";
  15.     void Start() {
  16.         WriteCSV();
  17.         ReadCSV();
  18.     }
  19.        
  20.         //序列化 将数据写入CSV文件中
  21.     void WriteCSV() {
  22.         List<EnemyData> enemies = new List<EnemyData> {
  23.             new EnemyData { Id = 1, Name = "Goblin", Speed = 3.5f, PrefabPath = "Prefabs/Enemies/Goblin" },
  24.             new EnemyData { Id = 2, Name = "Dragon", Speed = 8.0f, PrefabPath = "Prefabs/Enemies/Dragon" }
  25.         };
  26.         using (var writer = new StreamWriter(csvPath))
  27.         using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) {
  28.             csv.WriteRecords(enemies);
  29.         }
  30.         Debug.Log("CSV写入完成!");
  31.     }
  32.        
  33.         //反序列化 将读取到的数据转换成类
  34.     void ReadCSV() {
  35.         using (var reader = new StreamReader(csvPath))
  36.         using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) {
  37.             var records = csv.GetRecords<EnemyData>();
  38.             foreach (var enemy in records) {
  39.                 Debug.Log($"ID: {enemy.Id}, Name: {enemy.Name}, Speed: {enemy.Speed}");
  40.             }
  41.         }
  42.     }
  43. }
复制代码
生成CSV内容:
  1. Id,Name,Speed,PrefabPath
  2. 1,Goblin,3.5,Prefabs/Enemies/Goblin
  3. 2,Dragon,8,Prefabs/Enemies/Dragon
复制代码

3、Excel序列化 (EPPlus库)


EPPlus库核心本事


  • 创建、修改.xlsx文件
  • 支持公式、样式、图表
  • 安装:从NuGet安装EPPlus(注意Unity需使用兼容版本)
  1. using OfficeOpenXml;
  2. using System;
  3. using System.IO;
  4. using UnityEngine;
  5. public class ExcelExample : MonoBehaviour {
  6.     private string excelPath = "GameData.xlsx";
  7.     void Start() {
  8.         WriteExcel();
  9.         ReadExcel();
  10.     }
  11.     private void WriteExcel() {
  12.         ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
  13.         // 序列化 写入数据 创建新Excel文件
  14.         using (ExcelPackage package = new ExcelPackage()) {
  15.             // 添加工作表
  16.             ExcelWorksheet sheet = package.Workbook.Worksheets.Add("玩家数据");
  17.             // 填充标题行
  18.             sheet.Cells[1, 1].Value = "ID";
  19.             sheet.Cells[1, 2].Value = "Name";
  20.             sheet.Cells[1, 3].Value = "Level";
  21.             sheet.Cells[1, 4].Value = "LastLogin";
  22.             // 填充数据
  23.             sheet.Cells[2, 1].Value = 101;
  24.             sheet.Cells[2, 2].Value = "Player1";
  25.             sheet.Cells[2, 3].Value = 15;
  26.             sheet.Cells[2, 4].Value = DateTime.Now;
  27.             // 保存文件
  28.             FileStream fileStream = new FileStream(excelPath, FileMode.Create);
  29.             package.SaveAs(fileStream);
  30.             fileStream.Close();
  31.         }
  32.         Debug.Log("Excel写入完成!");
  33.     }
  34.        
  35.         //反序列化 读取数据
  36.     private void ReadExcel() {
  37.         using (ExcelPackage package = new ExcelPackage(new FileInfo(excelPath))) {
  38.             ExcelWorksheet sheet = package.Workbook.Worksheets["玩家数据"];
  39.             int rowCount = sheet.Dimension.Rows;
  40.             for (int row = 2; row <= rowCount; row++) { //跳过标题行
  41.                 int id = int.Parse(sheet.Cells[row, 1].Text);
  42.                 string name = sheet.Cells[row, 2].Text;
  43.                 int level = int.Parse(sheet.Cells[row,3].Text);
  44.                 DateTime lastLogin = DateTime.Parse(sheet.Cells[row,4].Text);
  45.                 Debug.Log($"读取到玩家:{name} (等级{level}), 最后登录时间:{lastLogin}");
  46.             }
  47.         }
  48.     }
  49. }
复制代码

4、XML序列化


核心优势:


  • 严酷的树状数据格式,支持复杂嵌套布局
  • 内置Schema验证(XSD)保证数据完整性
  • 支持解释和CDATA块处理处罚特殊字符
  1. using System.Collections.Generic;
  2. using System.IO;
  3. using System.Xml.Serialization;
  4. using UnityEngine;
  5. // 装备品类(嵌套定义)
  6. [System.Serializable]
  7. public class EquipmentItem {
  8.     public enum ItemRarity { Common, Rare, Epic }
  9.     [XmlAttribute("id")]  // 设置为XML属性
  10.     public string itemID;
  11.    
  12.     [XmlElement("displayName")] // 设置为XML元素
  13.     public string itemName;
  14.    
  15.     [XmlIgnore]  // 不参与XML序列化
  16.     public ItemRarity rarity;
  17.    
  18.     [XmlElement("durability")]
  19.     public int currentDurability = 100;
  20.     // 自定义字段序列化转换器
  21.     [XmlElement("rarity")]
  22.     public string RarityString {
  23.         get => rarity.ToString();
  24.         set => rarity = (ItemRarity)System.Enum.Parse(typeof(ItemRarity), value);
  25.     }
  26. }
  27. // 角色装备数据容器
  28. [System.Serializable]
  29. public class CharacterEquipment {
  30.     [XmlArray("WeaponSlots")] // 定义数组包裹元素
  31.     [XmlArrayItem("Weapon")]
  32.     public List<EquipmentItem> weapons = new List<EquipmentItem>();
  33.     [XmlArray("ArmorSlots")]
  34.     [XmlArrayItem("Armor")]
  35.     public EquipmentItem[] armors = new EquipmentItem[4]; // 固定长度数组
  36. }
  37. public class XMLSerializeExample : MonoBehaviour {
  38.     private string xmlPath = "CharacterData.xml";
  39.     void Start() {
  40.         // 创建测试数据
  41.         CharacterEquipment equipment = new CharacterEquipment {
  42.             weapons = {
  43.                 new EquipmentItem { itemID = "w_001", itemName = "Steel Sword", rarity = EquipmentItem.ItemRarity.Epic },
  44.                 new EquipmentItem { itemID = "w_002", itemName = "Wooden Bow", rarity = EquipmentItem.ItemRarity.Common }
  45.             },
  46.             armors = {
  47.                 new EquipmentItem { itemID = "a_001", itemName = "Iron Helmet" },
  48.                 null, // 演示空元素处理方法
  49.                 new EquipmentItem { itemID = "a_003", itemName = "Chainmail" }
  50.             }
  51.         };
  52.         // 序列化到文件
  53.         SerializeToXML(equipment, xmlPath);
  54.         
  55.         // 从文件反序列化
  56.         CharacterEquipment loadedData = DeserializeFromXML<CharacterEquipment>(xmlPath);
  57.         Debug.Log($"加载的武器数量: {loadedData.weapons.Count}");
  58.     }
  59.     void SerializeToXML<T>(T data, string path) {
  60.         XmlSerializer serializer = new XmlSerializer(typeof(T));
  61.         using (StreamWriter stream = new StreamWriter(path)) {
  62.             serializer.Serialize(stream, data);
  63.         }
  64.         Debug.Log($"XML序列化完成,文件尺寸:{new FileInfo(path).Length} bytes");
  65.     }
  66.     T DeserializeFromXML<T>(string path) {
  67.         XmlSerializer serializer = new XmlSerializer(typeof(T));
  68.         using (StreamReader stream = new StreamReader(path)) {
  69.             return (T)serializer.Deserialize(stream);
  70.         }
  71.     }
  72. }
复制代码
XML输出布局:
  1. <CharacterEquipment>
  2.   <WeaponSlots>
  3.     <Weapon id="w_001">
  4.       <displayName>Steel Sword</displayName>
  5.       <rarity>Epic</rarity>
  6.       <durability>100</durability>
  7.     </Weapon>
  8.     <Weapon id="w_002">
  9.       <displayName>Wooden Bow</displayName>
  10.       <rarity>Common</rarity>
  11.       <durability>100</durability>
  12.     </Weapon>
  13.   </WeaponSlots>
  14.   <ArmorSlots>
  15.     <Armor id="a_001">
  16.       <displayName>Iron Helmet</displayName>
  17.       <rarity>Common</rarity>
  18.       <durability>100</durability>
  19.     </Armor>
  20.     <Armor />
  21.     <Armor id="a_003">
  22.       <displayName>Chainmail</displayName>
  23.       <rarity>Common</rarity>
  24.       <durability>100</durability>
  25.     </Armor>
  26.   </ArmorSlots>
  27. </CharacterEquipment>
复制代码
关键注解:


  • [XmlAttribute]:将字段序列化为XML属性(紧凑格式)
  • [XmlElement]:自界说元素名称(默认使用字段名)
  • [XmlArray]+[XmlArrayItem]:控制集合的嵌套布局
  • 自界说属性转换器:处理处罚罗列类型与字符串的转换




   
             TechX —— 心探索、心进取!

每一次跌倒都是一次成长

每一次积极都是一次进步
      
  

   END  感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何题目或意见,或者想要相识更多关于本主题的信息,欢迎在批评区留言与我交换。我会非常乐意与大家讨论和分享更多风趣的内容。
如果您喜欢本博客,请点赞和分享给更多的朋侪,让更多人受益。同时,您也可以关注我的博客,以便实时获取最新的更新和文章。
在将来的写作中,我将继承积极,分享更多风趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

三尺非寒

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表