Unity之Serialized序列化:从原理到实践
- 手机
- 2025-08-23 12:39:02

内容将会持续更新,有错误的地方欢迎指正,谢谢!
Unity之Serialized序列化:从原理到实践TechX 坚持将创新的科技带给世界! 拥有更好的学习体验 —— 不断努力,不断进步,不断探索 TechX —— 心探索、心进取! 助力快速掌握 Serialized序列化 学习 为初学者节省宝贵的学习时间,避免困惑!
文章目录 一、 引言二、什么是序列化?三、Unity游戏对象的序列化四、脚本变量的序列化1、 [SerializeField]:私有字段的序列化2、 [System.Serializable]:自定义类的序列化 五、ScriptableObject序列化六、常见序列化格式1、 JSON序列化(Newtonsoft.Json)2、CSV序列化(CsvHelper库)3、Excel序列化 (EPPlus库)4、XML序列化
一、 引言
在Unity开发中,序列化(Serialization) 是贯穿整个开发流程的核心技术。无论是游戏对象的持久化存储、Inspector面板的变量显示,还是跨平台数据交换,都离不开序列化的支持。对于编辑器扩展开发而言,深入理解序列化机制更是实现自定义工具和高效数据管理的关键。
为什么需要关注序列化?
数据持久化:保存玩家存档、配置数据、场景状态。
Inspector交互:在编辑器中直观调整脚本参数。
跨平台兼容:通过标准格式(如JSON)实现不同平台间的数据交换。
性能优化:合理的序列化策略可减少内存占用和加载时间。
二、什么是序列化?
序列化是将对象转换为可存储或传输的格式(如二进制、JSON等)的过程,使得数据能在不同系统或平台间共享。反向的反序列化则是将这些格式数据还原为原始对象的过程。
在Unity中,序列化的典型场景包括:
场景文件:将游戏对象层次结构和组件数据保存为.unity文件Prefab:保存预设对象的模板Inspector面板:显示并修改脚本的序列化字段数据持久化:存档、配置文件或网络通信三、Unity游戏对象的序列化
游戏对象与组件的自动序列化 Unity的GameObject和Component(如Transform、Renderer等)默认支持序列化。其数据保存在场景或Prefab文件中,包括层级关系、组件属性等。
场景文件示例 打开一个.unity场景文件,可以看到类似以下结构的序列化数据:
--- !u!1 &12345 GameObject: m_Name: Player m_Component: - component: {fileID: 123456} --- !u!4 &123456 Transform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}序列化的限制 并非所有类型都能被Unity自动序列化。以下情况会导致字段不被序列化:
静态字段(static)非继承自UnityEngine.Object的类引用标记为[NonSerialized]的字段属性(Properties):仅支持字段序列化。未标记的私有字段:需使用[SerializeField]。某些引用类型:如Dictionary(需自定义序列化)。四、脚本变量的序列化 通过特性标记,开发者可以控制脚本中哪些字段需要序列化并在Inspector中显示。 1、 [SerializeField]:私有字段的序列化
Unity默认只会序列化public字段。若需序列化私有或受保护字段,可使用[SerializeField]。
public class Player : MonoBehaviour { [SerializeField] private int health = 100; // Inspector中可见 } 2、 [System.Serializable]:自定义类的序列化自定义类或结构体需添加[System.Serializable]属性,才能在Inspector中显示或被Unity序列化。
[System.Serializable] public class Weapon { public string name; public int damage; } public class Inventory : MonoBehaviour { public List<Weapon> weapons; // 列表中每个Weapon属性都可在Inspector编辑 }✅ 最佳实践 避免过度序列化:仅序列化必要数据以减少内存开销 版本兼容性:修改序列化类时注意向后兼容性(如添加新字段不影响旧数据)
五、ScriptableObject序列化
ScriptableObject是Unity提供的一个功能强大的工具,通常用于保存游戏数据、配置数据等。ScriptableObject在Unity中是非常重要的,它不仅能够序列化数据,还能够让你避免创建多个实例化的对象。
ScriptableObject核心特性:
独立数据容器:不与场景绑定,保存于.asset文件运行时修改持久化:编辑器模式下修改可保存到项目高效引用系统:作为资产被多个对象引用 using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif [System.Serializable] public class SkillEffect { public enum EffectType { Damage, Heal, Buff } public EffectType type; public float value; public float duration; } [System.Serializable] public class SkillLevel { [Range(1, 100)] public int requiredLevel = 1; public float manaCost; public SkillEffect[] effects; } // 主ScriptableObject类 [CreateAssetMenu(fileName = "New Skill", menuName = "RPG/Skill Config")] public class SkillConfig : ScriptableObject { public string skillName; public Sprite icon; public float baseCooldown; [TextArea(3, 5)] public string description; [Space] public SkillLevel[] levels = new SkillLevel[5]; // 最多5级技能 } public class SkillSystemTest : MonoBehaviour { void Start() { // 创建临时技能配置 SkillConfig tempSkill = CreateSkill("雷电术", new SkillLevel { requiredLevel = 5, manaCost = 30, effects = new[] { new SkillEffect { type = SkillEffect.EffectType.Damage, value = 50, duration = 2 } } } ); // 在编辑器模式下保存资产 #if UNITY_EDITOR AssetDatabase.CreateAsset(tempSkill, "Assets/Skills/Lightning.asset"); AssetDatabase.SaveAssets(); #endif } SkillConfig CreateSkill(string name, params SkillLevel[] levels) { SkillConfig config = ScriptableObject.CreateInstance<SkillConfig>(); config.skillName = name; config.levels = levels; return config; } }六、常见序列化格式 1、 JSON序列化(Newtonsoft.Json)
为什么选择Newtonsoft?
原生JsonUtility不足:不支持字典、私有字段嵌套类Newtonsoft特性:高效灵活,支持复杂类型转换安装方法:通过Unity Package Manager添加com.unity.nuget.newtonsoft-json using Newtonsoft.Json; using System.Collections.Generic; using UnityEngine; // 复杂数据结构示例(包含嵌套类和字典) [System.Serializable] public class Player { public string name; [SerializeField] private int _health; //私有字段支持序列化 public Dictionary<string, int> skills = new Dictionary<string, int>(); // 自定义Vector3序列化(需转换) [JsonIgnore] public Vector3 position; [JsonProperty("position")] private Vector3Serializable PositionSerializable { get => new Vector3Serializable(position); set => position = value.ToVector3(); } } // Vector3序列化辅助类 [System.Serializable] public struct Vector3Serializable { public float x; public float y; public float z; public Vector3Serializable(Vector3 v) { x = v.x; y = v.y; z = v.z; } public Vector3 ToVector3() => new Vector3(x, y, z); } public class JsonExample : MonoBehaviour { void Start() { Player player = new Player { name = "Arthur", _health = 200, position = new Vector3(10, 2, 5) }; player.skills.Add("Attack", 85); player.skills.Add("Defense", 60); // 序列化 string json = JsonConvert.SerializeObject(player, Formatting.Indented); Debug.Log("JSON Output:\n" + json); // 反序列化 Player loadedPlayer = JsonConvert.DeserializeObject<Player>(json); Debug.Log($"Position: {loadedPlayer.position}"); } }控制台输出:
{ "name": "Arthur", "skills": { "Attack": 85, "Defense": 60 }, "position": { "x": 10.0, "y": 2.0, "z": 5.0 } }关键注释:
[JsonIgnore]:标记不参与序列化的字段[JsonProperty]:自定义字段映射名称Vector3Serializable:需要手动处理Unity引擎特有类型 2、CSV序列化(CsvHelper库)CsvHelper核心功能
自动映射类属性到CSV列处理标题行复杂数据类型安装方法:通过NuGet安装CsvHelper using CsvHelper; using System.Collections.Generic; using System.Globalization; using System.IO; using UnityEngine; // CSV数据模型类 public class EnemyData { public int Id { get; set; } public string Name { get; set; } public float Speed { get; set; } public string PrefabPath { get; set; } } public class CsvExample : MonoBehaviour { private string csvPath = "Enemies.csv"; void Start() { WriteCSV(); ReadCSV(); } //序列化 将数据写入CSV文件中 void WriteCSV() { List<EnemyData> enemies = new List<EnemyData> { new EnemyData { Id = 1, Name = "Goblin", Speed = 3.5f, PrefabPath = "Prefabs/Enemies/Goblin" }, new EnemyData { Id = 2, Name = "Dragon", Speed = 8.0f, PrefabPath = "Prefabs/Enemies/Dragon" } }; using (var writer = new StreamWriter(csvPath)) using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.WriteRecords(enemies); } Debug.Log("CSV写入完成!"); } //反序列化 将读取到的数据转换成类 void ReadCSV() { using (var reader = new StreamReader(csvPath)) using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) { var records = csv.GetRecords<EnemyData>(); foreach (var enemy in records) { Debug.Log($"ID: {enemy.Id}, Name: {enemy.Name}, Speed: {enemy.Speed}"); } } } }生成CSV内容:
Id,Name,Speed,PrefabPath 1,Goblin,3.5,Prefabs/Enemies/Goblin 2,Dragon,8,Prefabs/Enemies/Dragon 3、Excel序列化 (EPPlus库)EPPlus库核心能力
创建、修改.xlsx文件支持公式、样式、图表安装:从NuGet安装EPPlus(注意Unity需使用兼容版本) using OfficeOpenXml; using System; using System.IO; using UnityEngine; public class ExcelExample : MonoBehaviour { private string excelPath = "GameData.xlsx"; void Start() { WriteExcel(); ReadExcel(); } private void WriteExcel() { ExcelPackage.LicenseContext = LicenseContext.NonCommercial; // 序列化 写入数据 创建新Excel文件 using (ExcelPackage package = new ExcelPackage()) { // 添加工作表 ExcelWorksheet sheet = package.Workbook.Worksheets.Add("玩家数据"); // 填充标题行 sheet.Cells[1, 1].Value = "ID"; sheet.Cells[1, 2].Value = "Name"; sheet.Cells[1, 3].Value = "Level"; sheet.Cells[1, 4].Value = "LastLogin"; // 填充数据 sheet.Cells[2, 1].Value = 101; sheet.Cells[2, 2].Value = "Player1"; sheet.Cells[2, 3].Value = 15; sheet.Cells[2, 4].Value = DateTime.Now; // 保存文件 FileStream fileStream = new FileStream(excelPath, FileMode.Create); package.SaveAs(fileStream); fileStream.Close(); } Debug.Log("Excel写入完成!"); } //反序列化 读取数据 private void ReadExcel() { using (ExcelPackage package = new ExcelPackage(new FileInfo(excelPath))) { ExcelWorksheet sheet = package.Workbook.Worksheets["玩家数据"]; int rowCount = sheet.Dimension.Rows; for (int row = 2; row <= rowCount; row++) { //跳过标题行 int id = int.Parse(sheet.Cells[row, 1].Text); string name = sheet.Cells[row, 2].Text; int level = int.Parse(sheet.Cells[row,3].Text); DateTime lastLogin = DateTime.Parse(sheet.Cells[row,4].Text); Debug.Log($"读取到玩家:{name} (等级{level}), 最后登录时间:{lastLogin}"); } } } } 4、XML序列化核心优势:
严格的树状数据格式,支持复杂嵌套结构内置Schema验证(XSD)保证数据完整性支持注释和CDATA块处理特殊字符 using System.Collections.Generic; using System.IO; using System.Xml.Serialization; using UnityEngine; // 装备品类(嵌套定义) [System.Serializable] public class EquipmentItem { public enum ItemRarity { Common, Rare, Epic } [XmlAttribute("id")] // 设置为XML属性 public string itemID; [XmlElement("displayName")] // 设置为XML元素 public string itemName; [XmlIgnore] // 不参与XML序列化 public ItemRarity rarity; [XmlElement("durability")] public int currentDurability = 100; // 自定义字段序列化转换器 [XmlElement("rarity")] public string RarityString { get => rarity.ToString(); set => rarity = (ItemRarity)System.Enum.Parse(typeof(ItemRarity), value); } } // 角色装备数据容器 [System.Serializable] public class CharacterEquipment { [XmlArray("WeaponSlots")] // 定义数组包裹元素 [XmlArrayItem("Weapon")] public List<EquipmentItem> weapons = new List<EquipmentItem>(); [XmlArray("ArmorSlots")] [XmlArrayItem("Armor")] public EquipmentItem[] armors = new EquipmentItem[4]; // 固定长度数组 } public class XMLSerializeExample : MonoBehaviour { private string xmlPath = "CharacterData.xml"; void Start() { // 创建测试数据 CharacterEquipment equipment = new CharacterEquipment { weapons = { new EquipmentItem { itemID = "w_001", itemName = "Steel Sword", rarity = EquipmentItem.ItemRarity.Epic }, new EquipmentItem { itemID = "w_002", itemName = "Wooden Bow", rarity = EquipmentItem.ItemRarity.Common } }, armors = { new EquipmentItem { itemID = "a_001", itemName = "Iron Helmet" }, null, // 演示空元素处理方法 new EquipmentItem { itemID = "a_003", itemName = "Chainmail" } } }; // 序列化到文件 SerializeToXML(equipment, xmlPath); // 从文件反序列化 CharacterEquipment loadedData = DeserializeFromXML<CharacterEquipment>(xmlPath); Debug.Log($"加载的武器数量: {loadedData.weapons.Count}"); } void SerializeToXML<T>(T data, string path) { XmlSerializer serializer = new XmlSerializer(typeof(T)); using (StreamWriter stream = new StreamWriter(path)) { serializer.Serialize(stream, data); } Debug.Log($"XML序列化完成,文件尺寸:{new FileInfo(path).Length} bytes"); } T DeserializeFromXML<T>(string path) { XmlSerializer serializer = new XmlSerializer(typeof(T)); using (StreamReader stream = new StreamReader(path)) { return (T)serializer.Deserialize(stream); } } }XML输出结构:
<CharacterEquipment> <WeaponSlots> <Weapon id="w_001"> <displayName>Steel Sword</displayName> <rarity>Epic</rarity> <durability>100</durability> </Weapon> <Weapon id="w_002"> <displayName>Wooden Bow</displayName> <rarity>Common</rarity> <durability>100</durability> </Weapon> </WeaponSlots> <ArmorSlots> <Armor id="a_001"> <displayName>Iron Helmet</displayName> <rarity>Common</rarity> <durability>100</durability> </Armor> <Armor /> <Armor id="a_003"> <displayName>Chainmail</displayName> <rarity>Common</rarity> <durability>100</durability> </Armor> </ArmorSlots> </CharacterEquipment>关键注解:
[XmlAttribute]:将字段序列化为XML属性(紧凑格式)[XmlElement]:自定义元素名称(默认使用字段名)[XmlArray]+[XmlArrayItem]:控制集合的嵌套结构自定义属性转换器:处理枚举类型与字符串的转换TechX —— 心探索、心进取! 每一次跌倒都是一次成长 每一次努力都是一次进步
END 感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何问题或意见,或者想要了解更多关于本主题的信息,欢迎在评论区留言与我交流。我会非常乐意与大家讨论和分享更多有趣的内容。 如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。 在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!
Unity之Serialized序列化:从原理到实践由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Unity之Serialized序列化:从原理到实践”