APP下载

Unity 3D中单例模式的设计与应用

2021-01-28

喀什大学学报 2020年6期
关键词:脚本调用实例

邓 伟

(喀什大学 计算机科学与技术学院,新疆 喀什 844000)

0 引言

在系统应用中,某些对象要求具有唯一性.例如在Windows 中就只能打开一个任务管理器窗口,如果不采用某种机制对窗口对象进行唯一化,那么将可能打开多个窗口,若窗口显示内容一致,则是对象重复,浪费内存资源;若窗口显示内容不一致,则意味着在某一瞬间系统有多个状态,不知道哪一个才是真实的状态.这样既不符合实际,也会给用户带来混乱.因此,确保系统中某个对象的唯一性即一个类只能有一个实例就非常重要[1].这时常采取单例模式来保证对象的唯一性.Unity 3D 是一款专业的3D 游戏引擎,广泛用于开发各种高质量2D/3D 游戏和VR/AR 应用.游戏开发中也会遇到类似的要求,比如游戏设计中,可能需要设计一个管理玩家信息的类脚本,要求在整个游戏中确保该类只有一个实例,且便于随时访问;也可能需要设计一个管理场景切换的类脚本,要求保存之前场景的状态或者获取之前场景的数据,并能在整个游戏范围内访问这个数据.这时采用单例模式设计类脚本就是一个比较好的选择方案,可确保一个类在系统中只有一个实例,并负责自行实例化和向整个系统提供对该实例的访问[2].单例模式在Unity 3D 虚拟现实和游戏设计开发中,一般常用于管理器类的设计,实现场景切换中对象的唯一性及持久化,是一种重要的设计模式.

1 单例模式设计原理

单例模式(Singleton Pattern) 是一种创建型模式,要求在设计一个类时保证整个程序在运行期间只存在一个实例对象且易于被访问,是对实例的生成过程进行管理的一种模式.它提供了全局唯一的访问入口,易于控制可能发生的冲突,可以严格地控制客户怎样访问它以及何时访问它[3].特殊场合下,如何保证一个类只有一个实例对象且这个实例对象易于被访问,是一个必须要的考虑因素.定义一个全局性的变量虽可实现实例对象随时被访问,却也可能实例化出多个对象,不能保证实例的唯一性.最简明可靠的方法是,在类的设计中,让类自身保存它的唯一实例,保证没有其他实例被创建,且可提供一个访问该实例的方法.单例模式中的这个类称为单例类,其有三个特点:单例类只能有一个实例;单例类必须自行创建这个实例;单例类必须自行向整个系统提供这个实例.可以说,当一个系统中,只允许某个类的一个实例存在,那么就应采用单例模式[4].设置类为单例,需要考虑三个问题:一是不让类的实例被随意构造出来;二是如果不能随意构造类的实例,那么要使用这个类该如何解决;三是保证只能构造一个唯一实例.普通类的实例是用new 关键字来创建的,调用的是类的默认公有无参构造器.所以,如果不让类的实例被随意构造出来,可以将构造器私有化,这样就不能随意使用构造器来创建类的实例.针对第二个问题,可以在类中调用自身私有构造方法,然后对外提供一个公有的静态方法来获取即可.解决第三个问题,可以声明一个私有的静态的类的变量,在公有的静态方法中,根据判断这个变量的引用是否为null 来决定是否调用自身私有构造方法创建类的实例,从而保证实例的唯一性.

2 Unity 3D 中的两类单例模式

在Unity 3D 中,MonoBehaviour 类是十分重要的类,它定义了基本的脚本行为,提供了许多属性和方法,不同的方法在特定的情况下会被调用,实现特定的功能.[5]根据设计的类是否继承自MonoBehaviour 类,可以分为两类单例模式,一种是非继承MonoBehaviour 类普通单例模式,另一种是继承MonoBehaviour 类的单例模式.

2.1 非继承MonoBehaviour 类的普通单例模式

设计了SingletonClass 类样例,类图如图1所示.

图1 SingletonClass 类图

设计如下:

代码中,声明一个静态SingletonClass 类的变量instance 来引用唯一的对象,私有的无参构造方法SingletonClass()让外部类无法调用,只能在单例类的内部被调用,静态方法GetSingletonInstance () 内通过判断instance 是否为null,保证创建此类的唯一对象.上述代码可以说是一个纯C# 普通单例,在Unity 3D 中,如果自定义一个类,只是想调用其中的方法和属性,不挂载到场景的物体中使用,就可以不继承MonoBehaviour 类,可以使用以上方式来构建单例,此类方式在Unity 3D 开发中一般比较少见.

2.2 继承MonoBehaviour 类的单例模式

在Unity 3D 中,任何要挂载到游戏对象上的脚本都需要继承MonoBehaviour 类,因此在Unity 3D 开发中此类单例模式比较常见.和普通单例不同的是,所有继承MonoBehaviour 的类不可以实例化,Unity 3D 都会自动创建实例,继承自MonoBeHaviour 的脚本在挂上GameObject的时候相当于已经实例化一次.

设计如下:

上述代码中,单例类SingletonClass 继承自MonoBehaviour 类,内部有一个instance 属性,get设置外部可以读取,私有的set 保证只能在SingletonClass 类内部赋值,Awake()方法在脚本对象实例化时被调用.

3 Unity 3D 特定场景中单例模式的应用实现

在使用Unity 3D 开发游戏的时候,经常需要设计类用于管理一些全局的变量和方法,记录各种需要在整个游戏过程中持久化的数据,特别是在场景切换时,要保证对象的唯一,且可以被访问.设有一个特定环境:模仿闯关游戏中的分值奖励.有两个游戏场景Scene1 和Scene2,每个场景中各有一个按钮(分别命名为Button1和Button2)和文本UI(命名为Text_Score),点击一个场景的按钮(先启动Scene1 场景),会跳转到另一个场景,并且每点击一次按钮,跳转到的场景中的文本UI 会执行加1 操作并显示.

从单例模式角度分析,设计一个Manager类,整个游戏过程中必须有且只有一个Manager类的实例;在Manager 中记录一个名为Score 的int 型属性变量;场景切换时,Manager 不被销毁,并对Score 值进行加1 操作;每个场景中的文本UI 会显示当前Score 值.

3.1 定义Manager 单例类

在Unity 3D 中的Scene1 场景下创建一个空物体对象,命名为Player.创建C# 脚本,命名为Manager,将Manage 类脚本挂载在Player 对象上.

上述代码中,Manager类继承了MonoBehaviour 类.静态Manager 类型的属性成员Instance 保证它可以通过类访问,而不是通过实例访问,Instance 属性中,get 保证外部可读取,同时私有的set 保证只能在Manager 类内部赋值;Score 属性实现对私有_Score 字段的读取;Awake()方法在脚本实例被创建时调用,脚本生命周期中只执行一次,方法中对Instance 进行赋值初始化.

3.2 定义ChangeScene 类脚本

创建ChangeScene 类脚本,分别挂载到两个场景的按钮上,用于实现场景切换及计分显示功能.

代码中,Start () 方法中实现单例对象中的Score 属性的读操作,将分值显示在文本UI 上.Change()是按钮事件的处理方法,单击按钮时实现单例对象中的Score 属性进行分值加1 的写操作,分值加1,并跳转到另一场景.

3.3 存在的问题及解决方法

在Unity 3D 中,切换场景时默认会销毁上一个游戏场景中所有的游戏对象,所以场景中所创建的Player 对象会被销毁,这时可以使用DontDestroyOnLoad()方法让Player 对象在切换游戏场景时不会被销毁,该方法添加在Manager类的Awake () 方法内即可.另外,当从场景Scene2 切换回场景Scene1 时,并不是恢复场景Scene1,而是重新创建出一个新场景,导致一个新的Player 对象被创建,不能保证Player 对象的唯一性.所以需要在Awake()方法内添加逻辑判断,检测到已经存在一个Player 对象时,销毁当前Player 对象.Awake()方法代码修改如下:

4 结语

单例模式是一种常用的设计模式,系统中对应类只有一个实例对象,可以避免多次创建对象带来的资源消耗,或是方便数据的统一处理和保存.Unity 3D 是单线程操作,通过协程机制来实现一些类似于多线程的功能.所以在上述单例模式设计中,没有涉及多线程而去增加“锁”.此外,如果单例不涉及到Unity 3D 相关组件操作,就不用继承MonoBehaviour 类,可以用纯C# 的单例语法来处理.

猜你喜欢

脚本调用实例
酒驾
安奇奇与小cool 龙(第二回)
核电项目物项调用管理的应用研究
LabWindows/CVI下基于ActiveX技术的Excel调用
数据库系统shell脚本应用
快乐假期
基于系统调用的恶意软件检测技术研究
完形填空Ⅱ
完形填空Ⅰ
利用RFC技术实现SAP系统接口通信