马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
内容将会连续更新,有错误的地方欢迎指正,谢谢!
Unity之Serialized序列化:从原理到实践 TechX 对峙将创新的科技带给世界!
拥有更好的学习体验 —— 不停积极,不停进步,不停探索 | TechX —— 心探索、心进取!
助力快速把握 Serialized序列化 学习
为初学者节流名贵的学习时间,克制困惑! |
一、 引言
在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 感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何题目或意见,或者想要相识更多关于本主题的信息,欢迎在批评区留言与我交换。我会非常乐意与大家讨论和分享更多风趣的内容。
如果您喜欢本博客,请点赞和分享给更多的朋侪,让更多人受益。同时,您也可以关注我的博客,以便实时获取最新的更新和文章。
在将来的写作中,我将继承积极,分享更多风趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |