算法、数据结构、与设计模式等在游戏开发中的运用 (一):单例设计(Singleton Design)

作者: Compasslg 李涵威

1. 什么是单例设计(Singleton Design)

在学校学习面向对象编程中的一些常用的设计模式时,我第一次系统的接触到了单例设计(Singleton Design),或者说单例设计模式。所谓设计模式(Design Pattern),指的是在软件开发中针对一些常见问题提出的可复用的解决方式;而单例设计便是针对在面向对象编程中一些只会被实例化一次、或只允许一个实例(instance)存在的类(class)而出现的设计模式。这种对象/实例通常是作为工程中一个全局管理的存在,因此他们会在整个工程的各个角落被调用。由于在面向对象编程的设计中你往往需要通过储存一个对象的地址来随时调用其中的方法和数据,这便可能会造成同一个对象的地址被储存很多次的情况。

在单例设计模式中,你可以通过将单例目标类的构造器(Constructor)设置为private类型使该类无法在外部被实例化,然后在这个类的内部实例一个自己的对象并将其储存在一个静态变量中。同时,你也可以写一个公开的静态getter方法来作为获取和调用这个单例的方式。如此这般,通过使用单例设计模式,你便可以实现一个全局有且只有一个实例的类。具体实现方式我会在下一个部分举例说明。

2. 如何使用单例设计 (Java 范例)

Java是单例设计最常被应用的语言。一个最典型的例子便是java.lang.Runtime中的Runtime class. 该类无法被实例,你可以通过其中的静态方法Runtime.getRuntime()来调用他的单例 object。

除此之外,在软件和游戏开发中还有很多的功能可以利用单例设计实现,以下便是一个用Java通过单例设计实现的可能被应用在游戏和软件中的音频管理器(AudioManager)的简单模型:

/**===========================================
这是一个在游戏中管理音频文件的类,有且只有存在一个。
因此使用单例设计模式。
*===========================================*/
class AudioManager {
// 设置为private,外部便不能再修改这个单例。
private static instance; // 单例中实际储存的内容。这里使用的数据类型只是作为该单例的应用背景(context),仅供参考,在AudioManager中具体要用什么数据类型来储存音频资料应视情况而定
private HashMap<String, AudioClip> clips; // ... 此处略过其他AudioManager可能需要的变量 ... // private 构造器,无法被外部调用
private AudioManager(){
clips = new HashMap<String, AudioClip>();
// ... 此处略过其他可能需要初始化的东西
} // 单例的 Getter。会且只会实例一个该类的实例。
// 如果担心第一次调用时的速度影响,可以在loading的时候统一将单例设计的类的getInstance方法调用一次
public static AudioManager getInstance(){
// 实例化如果此前没有实例
if (instance == null){
instance = new AudioManager();
}
return instance;
} // 播放一段音频的方法
public void playAudioClip(String clipName){
clips.get(clipName).play();
}
}

以下是调用方法:

public static void main(String[] args){
AudioManager.getInstance().playAudioClip("BGM");
}

3. 游戏开发中的运用 (Unity)

在游戏开发过程中,我们常常会需要应用到一些负责全局管理的并且只会同时存在一个实例的类,例如上面提到的AudioManager,还有负责管理游戏状态或界面的StateManager和SceneManager,我自己写游戏也经常会写一个负责数据管理的类DataManager。这些类都是有且只有一个实例存在,都可以通过 Part 2 中的方法依样画葫芦实现和调用,这里就不复述了。

在使用Unity开发游戏时,我们往往会要用到一个GameController。在我接触单例设计之前,我都会选择在要用到他的地方存一个变量,然后通过在编辑器中把它拖到inspector,或者使用

gameController = GameObject.FindWithTag("GameController");

来找到它。久而久之,这样不但使代码变得混乱且重复,在速度上和内存空间上也给我一种很浪费的感觉。这个时候,我们可以利用Singleton Design思想,在GameController中加一个静态变量

public class GameController : MonoBehaviour {

	private static GameController instance;
void Awake(){
// ... 省略其他初始化相关代码 ...
instance = this;
} public static GameController GetInstance(){
return instance;
}
}

这样,虽然这个类是绑定Unity中的GameObject被实例的而并非按照此前提到的通过private constructor的方法生成的单例,我们依然可以利用单例设计中的部分思想来使他调用起来更方便。此后,当我们需要调用GameController中的方法时,只需要调用他的单例即可

GameController.GetInstance().MethodName();

4. 总结

在我学习OOP中常用的设计模式的过程中,我的教授表达了并不推荐学生使用单例设计的看法。他认为单例设计 “破例的使用了全局变量,破坏了模块化设计的思想 ”。但此后,我在学习过程中多次看到其他教授使用单例设计,Github上也有一些不小的项目用到过单例设计;同时,我自己在游戏开发过程中也常常感受到这种设计模式带来的便利。所以,我觉得只要合理运用,单例设计也不失为一种好的设计模式。如有不同意见或者高手有什么指教,欢迎评论。

算法、数据结构、与设计模式等在游戏开发中的运用 (一):单例设计(Singleton Design)的更多相关文章

  1. JAVA设计模式:单例设计

    1.单例设计Singleton的引出 单例设计,从名字上首先可以看出单---即只有一个,例---只的是实例化对象:那么单例也就是说一个类,只产生了一个实例化对象.但是我们都知道,一个类要产生实例化对象 ...

  2. [Unity游戏开发]向量在游戏开发中的应用(三)

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51088236 在上一篇博客中讲了利用向量点乘在游戏开发中应用的几种情景.本 ...

  3. 【Cocos2d-X游戏实战开发】捕鱼达人之单例对象的设计(二)

    本系列学习教程使用的是cocos2d-x-2.1.4(最新版为cocos2d-x-2.1.5)    博主发现前两个系列的学习教程被严重抄袭,在这里呼吁大家请尊重开发者的劳动成果, 转载的时候请务必注 ...

  4. 借助AMD来解决HTML5游戏开发中的痛点

    借助AMD来解决HTML5游戏开发中的痛点 游戏开发的痛点 现在,基于国内流行引擎(LayaAir和Egret)和TypeScript的HTML5游戏开发有诸多痛点: 未采用TypeScript编译器 ...

  5. [Unity游戏开发]向量在游戏开发中的应用(二)

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/50972976 在上一篇博客中讲了利用向量方向的性质来解决问题.这篇博客将继 ...

  6. [Unity游戏开发]向量在游戏开发中的应用(一)

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/50810102 向量在游戏开发中是非常实用的,我们在学校学完向量的知识后,只 ...

  7. Cocos2d-x游戏开发中的消息机制:CCNotificationCenter的使用

    在HTML5游戏开发中,js可以使用Event对象的addEventListener(添加事件监听).dispatchEvent(触发事件)实现监听机制,如果在coocos2d-x中,去实现这种机制该 ...

  8. 二、Cocos2dx概念介绍(游戏开发中不同的坐标系,cocos2dx锚点)

    注:ccp是cocos2dx中的一个宏定义,#define ccp(__X__,__Y__)CCPointMake((float)__X__, (float)__Y__),在此文章中表示坐标信息 1. ...

  9. [C++基金会]位计算 游戏开发中的应用

    定义的位操作:通俗点说,,位计算是计算机操作二进制整数. 无论整数可以用二的方式来表示进度,不同类型的其长度的整数位的是不一样的.INT8要么char靠8个月2 位表示,INT16或者short是由1 ...

随机推荐

  1. [信号与系统]傅里叶变换、DFT、FFT分析与理解

    目录 一.前言 二.傅里叶变换 1.傅里叶级数 2.傅里叶级数系数求解 2.1.求解方法 2.2.三角函数的正交性 2.3.系数求解过程 2.4.关于傅里叶级数的个人感悟 3.引入复指数 4.总结 三 ...

  2. Shiro反序列化漏洞检测、dnslog

    目录 信息收集 poc 参考 信息收集 poc # pip install pycrypto import sys import base64 import uuid from random impo ...

  3. nessus 故障处理

    0x00 问题描述 打开Nessues Web Client时,界面循环在Initializing Please wait while Nessus prepares files needed...和 ...

  4. linux 设置系统时间

    第一种: 服务器date时间不准: root@mdy-zabbix2:~# date Fri Sep 28 09:58:56 UTC 2018 实际是下午6点 第一步:执行tzselect 第二步: ...

  5. .NET 5下的Blazor是否可以大规模正式使用?

    今天在微信群讨论了很多Blazor是否可以正常用的问题.大家争的面红耳赤的. 于是趁着无聊,就水了这么一篇文. 还记得Blazor还在预览版的时候,我就开始关注Blazor了. 那会儿调试Blazor ...

  6. 基于CefSharp开发浏览器(九)浏览器历史记录弹窗面板

    一.前言 前两篇文章写的是关于浏览器收藏夹的内容,因为收藏夹的内容不会太多,故采用json格式的文本文件作为收藏夹的存储方式. 关于浏览器历史记录,我个人每天大概会打开百来次网页甚至更多,时间越长历史 ...

  7. AbstractQueuedSynchronizer之AQS

    一.可重入锁 可参考:可重入锁和递归锁 1,定义 指的是同一线程外层函数获得锁后,再进入该线程的内层方法会自动获取锁(前提:锁对象是同一个对象). Java中的ReentranLock(显示锁)和Sy ...

  8. Distributed Cache(分布式缓存)-SqlServer

    分布式缓存是由多个应用服务器共享的缓存,通常作为外部服务存储在单个应用服务器上,常用的有SqlServer,Redis,NCache. 分布式缓存可以提高ASP.NET Core应用程序的性能和可伸缩 ...

  9. python基础学习之简化占位符和格式化的概念

    简化版占位符有哪些? %s   字符串占位符,代表该位置有一个字符串待替换(万能) %d   数字占位符,代表该位置有一个数字待替换 %f    同上,这里是浮点数,float[默认保留6位小数] % ...

  10. android分析之Parcel

    将数据打包,跨进程传输(通过Binder).看看这货究竟是啥玩意: Parcel.java : public final class Parcel { private static final boo ...