当前位置: 首页 > news >正文

Unity序列化字段、单例模式(Singleton Pattern)

一、序列化字段

在Unity中,序列化字段是一个非常重要的概念,主要用于在Unity编辑器中显示和编辑类的成员变量,或者在运行时将对象的状态保存到文件或网络中。

 1.Unity序列化字段的作用

  • 在编辑器中显示和编辑字段:默认情况下,只有公共字段(  public  )才会在Unity编辑器的Inspector窗口中显示。如果希望私有字段(  private  )或受保护字段(  protected  )也能在编辑器中显示并被编辑,可以通过序列化字段来实现。
  • 保存和加载对象状态:序列化字段还可以用于将对象的状态保存到文件或网络中,并在需要时重新加载这些状态。

2. 使用  [SerializeField]  属性

Unity提供了  [SerializeField]  属性,用于将私有或受保护的字段标记为可序列化。这样,这些字段就可以在Unity编辑器的Inspector窗口中显示和编辑。

using UnityEngine;public class ExampleScript : MonoBehaviour
{// 这是一个公共字段,会在Inspector中显示public int publicField;// 这是一个私有字段,不会在Inspector中显示private string privateField;// 这是一个私有字段,但使用[Serialize]标记后会在Inspector中显示[SerializeField] private float serializedField;void Start(){Debug.Log("Public Field: " + publicField);Debug.Log("Serialized Field: " + serializedField);}
}
//publicField和serializedField都会在Inspector中显示,而privateField不会显示。
//使用[SerializeField]属性可以让开发者更好地封装类的实现细节,同时仍然允许在编辑器中对字段进行编辑。

 3. 默认序列化规则

  • 公共字段:默认情况下,所有公共字段(  public  )都会被序列化,无论是否使用了  [SerializeField]  属性。
  • 私有字段:默认情况下,私有字段(  private  )不会被序列化,除非使用了  [SerializeField]  属性。
  • 静态字段:静态字段(  static  )永远不会被序列化,因为它们不属于对象的实例状态。
  • 只读字段:只读字段(  readonly  )不会被序列化,因为它们在构造函数之后不能被修改。
  • 特殊类型字段:某些特殊类型(如  Dictionary  、  Action  、  Func  等)不会被序列化,因为它们的序列化逻辑比较复杂。

4. 使用  [SerializeField]  的注意事项

  • 不要滥用:虽然  [SerializeField]  可以让私有字段在编辑器中显示,但不要过度使用它。过多的字段暴露在Inspector中可能会导致编辑器界面变得混乱。
  • 性能影响:序列化字段可能会对性能产生一定影响,尤其是在大量对象需要序列化时。因此,在性能敏感的场景中,需要谨慎使用序列化字段。
  • 与  [HideInInspector]  的区别:  [SerializeField]  用于将私有字段标记为可序列化,使其在Inspector中显示;而  [HideInInspector]  用于隐藏公共字段,使其不在Inspector中显示。

 5. 序列化字段的应用场景

  • 组件属性编辑:在开发自定义组件时,使用  [SerializeField]  可以让开发者更好地封装组件的实现细节,同时仍然允许用户在编辑器中配置组件的属性。
  • 保存和加载游戏状态:通过序列化字段,可以将游戏对象的状态保存到文件中,并在需要时重新加载这些状态,实现游戏的存档和读档功能。
  • 网络同步:在多人游戏中,可以将需要同步的字段标记为序列化字段,然后通过网络协议将这些字段的状态发送到其他客户端。

6. 扩展:自定义序列化

如果默认的序列化机制不能满足需求,Unity还提供了自定义序列化的方法。例如,可以通过实现  ISerializationCallbackReceiver  接口,在序列化和反序列化时执行自定义逻辑。

using UnityEngine;public class CustomSerializationExample : MonoBehaviour, ISerializationCallbackReceiver
{// 这是一个自定义字段,不会直接被序列化private string customField;// 用于存储自定义字段的序列化数据[SerializeField] private string serializedData;public void OnBeforeSerialize(){// 在序列化之前,将自定义字段的值转换为可序列化的格式serializedData = customField;}public void OnAfterDeserialize(){// 在反序列化之后,将序列化的数据还原为自定义字段的值customField = serializedData;}
}

二、单例模式(Singleton Pattern) 

在Unity中,单例模式是一种常见的创建型设计模式,用于确保某个类在程序运行期间只有一个实例存在,并提供一个全局访问点来获取这个实例。

在单例模式中,类的构造函数通常是私有的,防止外部通过 new 来创建对象,类内部维护一个静态实例,通过公共的静态方法提供访问。

单例模式的实现分为饿汉模式懒汉模式

1.饿汉式(Eager Initialization)单例模式:

是一种在类加载时就立即创建单例对象的实现方式。这种模式确保了单例对象在程序启动时就存在,并且可以立即使用。

适用场景:

当对象需要在程序启动时立即可用,且创建成本较低时使用,适合常用于必要的核心功能。例子:游戏设置、玩家控制、主菜单管理器。

实现方式:

1)属性实现单例模式(饿汉模式

通过定义一个静态属性来实现单例模式,并且通过私有设置器(  private set  )确保实例在第一次创建时被设置,并且之后不能被更改。

public class Player : MonoBehaviour
{//单例模式,确保了Player类只有一个实例,并且提供了一个全局访问点来获取这个实例//通过私有设置访问器,确保了Instance属性的值不会被外部代码意外修改public static Player Instance{ get; private set; }// ... 其他代码 ...//Awake在游戏对象被创建时自动调用,通常用于初始化操作。private void Awake(){//检查Instance是否已经为null。如果Instance不为null,说明已经有一个实例被创建了if (Instance != null){Debug.Log("已经有一个实例被创建了");}//将当前对象实例赋值给Instance,使其成为该类的唯一实例Instance = this;}
}
//这种实现方式确保了Player类在整个游戏会话中只有一个实例,并且可以通过Player.Instance访问这个实例。

注意点: 

1.立即初始化:在类被加载时立即创建单例对象,而不是在首次使用时才创建。

2. 简单实现:实现简单,不需要同步控制,因为对象在第一次加载时就被创建。

3. 资源占用:可能会在不需要时就占用资源,因为对象在类加载时就被创建了。

2)线程安全实现单例模式(饿汉模式

确保即使在多线程环境中,Player 类也只有一个实例,并且这个实例是在第一次被访问时创建的(懒加载)。

public class Player : MonoBehaviour
{// 私有静态字段,用于存储单例实例private static Player instance;// 公共静态属性,提供对单例的全局访问点public static Player Instance{// 属性的get访问器,用于获取单例实例get{// 如果实例为null,则进入锁定代码块if (instance == null){// 使用锁确保线程安全,防止多线程同时创建实例lock (typeof(Player)){// 再次检查实例是否为null,防止多线程创建多个实例if (instance == null){// 创建Player类的新实例instance = new Player();}}}// 返回单例实例return instance;}}// Unity生命周期方法,在对象被创建时调用private void Awake(){// 如果已经存在一个实例,并且当前实例不是那个实例,则销毁当前实例if (Instance != this){Destroy(gameObject);}// 如果当前实例是单例,则防止它在加载新场景时被销毁else{DontDestroyOnLoad(gameObject);}}
}

2.懒汉式(Lazy Initialization)单例模式:

是一种常见的单例实现方式,它确保单例对象在首次被访问时才创建。这种方式可以节省资源,因为它避免了在程序启动时立即创建对象,而是在真正需要时才创建。

适用场景:

当对象可能不被使用,或创建成本较高时使用,适合于可选或资源密集型功能。例子:高级图形选项、调试工具、可选模块。

实现方式:

1)属性实现单例模式(懒汉模式

确保单例对象在首次被访问时才创建,从而节省了资源。同时,由于属性的访问器是公共的,所以可以在任何地方通过 Singleton.Instance 访问单例。

using UnityEngine;public class Player : MonoBehaviour
{// 私有静态字段,用于存储单例实例private static Player instance;// 公共静态属性,提供全局访问点来获取单例实例public static Player Instance{get{// 如果实例为null,则创建实例if (instance == null){instance = new GameObject("Player").AddComponent<Player>();}return instance;}}// 私有构造函数,防止外部实例化private Player() { }// Unity生命周期方法,在游戏对象被创建时自动调用private void Awake(){// 检查是否已经存在实例if (instance != null && instance != this){// 如果存在另一个实例,则销毁当前实例Destroy(gameObject);}else{// 标记为DontDestroyOnLoad,防止在加载新场景时销毁DontDestroyOnLoad(gameObject);instance = this;}}
}

注意点:

1. 延迟初始化:单例对象在首次被访问时创建,而不是在类加载时立即创建。

2. 访问延迟:第一次访问单例属性时可能会有轻微延迟,因为需要创建对象。

3. 资源节省:只有在实际需要时才创建对象,节省资源。

2)线程安全实现单例模式(懒汉模式

为了提高线程安全性,可以使用双重检查锁定(Double-Checked Locking)模式

using UnityEngine;public class Player : MonoBehaviour
{// 私有静态字段,用于存储单例实例private static Player instance;// 用于同步的对象,确保线程安全private static readonly object lockObject = new object();// 公共静态属性,提供全局访问点来获取单例实例public static Player Instance{get{// 如果实例为null,则进入锁定代码块if (instance == null){lock (lockObject){// 再次检查实例是否为null,防止多线程创建多个实例if (instance == null){// 创建Player类的新实例instance = new GameObject("Player").AddComponent<Player>();}}}// 返回单例实例return instance;}}// 私有构造函数,防止外部实例化private Player() { }// Unity生命周期方法,在游戏对象被创建时自动调用private void Awake(){// 检查是否已经存在实例if (instance != null && instance != this){// 如果存在另一个实例,则销毁当前实例Destroy(gameObject);}else{// 如果不存在实例,则调用DontDestroyOnLoad,防止在加载新场景时销毁DontDestroyOnLoad(gameObject);// 将当前实例赋值给单例instance = this;}}
}

相关文章:

  • Redis 发布订阅模式深度解析:原理、应用与实践
  • 制作大风车动画
  • Mipsel固件Fuzzing小记
  • 永磁同步电机公式总结——反电动势、磁链、转矩公式;三项、两项电压方程;坐标表换方程
  • Node.js 源码架构详解
  • 【磁盘清理】C盘告急,给JetBrains缓存文件搬个家
  • <input type=“number“>不显示的原因
  • NBA足球赛事直播源码体育直播M33模板赛事源码
  • CSS- 4.1 浮动(Float)
  • 【Linux笔记】——线程互斥与互斥锁的封装
  • 本地缓存更新方案探索
  • 产品经理入门(2)产品体验报告
  • Ubuntu 环境下搭建 Docker 及常见问题解决方案
  • 大数据会被AI取代?不!大数据才是AI的“智慧燃料”引擎
  • mcp学习笔记
  • JSP链接MySQL8.0(Eclipse+Tomcat9.0+MySQL8.0)
  • 西门子 Teamcenter13 Eclipse RCP 开发 1.2 工具栏 开关按钮
  • 在线教育本地化分发:代理IP实现区域访问控制与内容适配
  • MySQL表的约束(上)
  • 嵌入式学习笔记 - STM32定时器的输入通道与时钟源
  • 上海这个咖啡文化节首次“走出去”,率本土品牌亮相英国伦敦
  • 北京韩美林艺术馆党支部书记郭莹病逝,终年40岁
  • 探秘多维魅力,长江经济带、珠三角媒体总编辑岳阳行启动
  • 自强!助残!全国200个集体和260名个人受到表彰
  • 国家卫生健康委通报关于肖某引发舆情事件调查处置进展情况
  • 宜昌谱写新叙事:长江大保护与高质量发展如何相互成就