算法、数据结构、与设计模式等在游戏开发中的运用 (一):单例设计(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. java自学第3期——继承、多态、接口、抽象类、final关键字、权限修饰符、内部类

    一.继承: 关键字extends /* 定义一个父类:人类 定义父类格式:public class 父类名称{ } 定义子类格式:public class 子类名称 extends 父类名称{ } * ...

  2. io流+网络+线程池 实现简单的多客户端与服务器端通信

    1 import java.io.IOException; 2 import java.io.InputStream; 3 import java.io.OutputStream; 4 import ...

  3. 玩遍博客网站,我整理了 Hugo 及其流行的风格主题

    搭建博客网站是个人进入互联网世界的最常见方式之一.伴随着网站技术的发展,如何搭建博客网站已经变得非常容易了.当然,你可以选择诸如 新浪博客.CSDN.博客园 之类的大型网站,快速创建依赖于大平台的个人 ...

  4. OLAP分析

    OLAP分析 1 视频教程 视频教程 如果对资源下载.分析操作有疑问,直接跟着视频做一遍即可. 2 数据集合说明 FoodMart,其为一家食品连锁店经营产生的数据存放的数据库,包括销售数据.库存数据 ...

  5. Innodb的存储及缓存

    参考[mysql技术内幕] 一.mysql体系结构和存储引擎 1.数据库与数据库实例 数据库:物理操作系统文件或者其他文件组成的集合: 数据库实例:有数据库后台进程/线程和一个共享内存区域组成. 数据 ...

  6. go 动态数组 二维动态数组

    go使用动态数组还有点麻烦,比python麻烦一点,需要先定义. 动态数组申明 var dynaArr []string 动态数组添加成员 dynaArr = append(dynaArr, &quo ...

  7. 痞子衡嵌入式:FlexSPI复位方式不当会导致i.MXRT系列下OTFAD加密启动失败

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是FlexSPI复位方式不当会导致i.MXRT系列下OTFAD加密启动失败问题. 本篇是<系统时钟配置不当会导致i.MXRT1xxx ...

  8. JVM笔记 -- JVM的发展以及基于栈的指令集架构

    2011年,JDK7发布,1.7u4中,开始启用新的垃圾回收器G1(但是不是默认). 2017年,发布JDK9,G1成为默认GC,代替CMS.(一般公司使用jdk8的时候,会通过参数,指定GC为G1) ...

  9. Java流程控制:用户交互Scanner

    java.util.Scanner工具类获取用户输入语法:Scanner scanner = new Scanner(System.in);通过Scanner类的next()与nextLine()方法 ...

  10. 聊一聊桥接(JSBridge)的原理

    一.前言 如今的互联网时代也称移动互联网时代,基本上每个人每天都会花费大量时间在移动设备上,早期的移动端应用大都使用原生开发(android,ios),而现在的移动开发技术选型上基本都是混合开发(Hy ...