设计模式与游戏完美开发(3)

更多内容可以浏览本人博客:https://azureblog.cn/ 😊
该文章主体内容来自《设计模式与游戏完美开发》—蔡升达

第二篇 基础系统

第五章 获取游戏服务的唯一对象——单例模式(Singleton)

游戏实现中的唯一对象

在游戏开发过程中,我们常常希望一些游戏对象(比如某游戏功能管理器)具有两项特性:

  • 唯一性:同时间只存在一个对象。
  • 便捷性:提供一个快速获取这个对象的方法。

比较直接的想法是使用全局静态对象,但是全局静态对象很难避免产生多个对象,也容易产生全局变量名重复的问题。

所以最好让这个类只产生一个对象,并提供便利的方法来获取这唯一的对象,这就是单例模式

单例模式

确认类只有一个对象,并提供一个全局的方法来获取这个对象。——GoF

结构:

img

参与者如下:

  • 能产生唯一对象的类,并且提供"全局方法"让外界可以方便获取唯一的对象

  • 通常会把唯一的类对象设置为"静态类属性"

  • 习惯上会使用Instance作为全局静态方法的名称,通过这个静态函数可能获取"静态类属性"

C#实现范例:

public class Singleton {public string Name { get; set; }private static Singleton _instance;public static Singleton Instance {get {if (_instance == null) {//保证唯一性,并提供遍历方法供使用Debug.Log("产生Singleton");_instance = new Singleton();}return _instance;}}private Single() { }//让外部不能new对象,保证唯一性
}void UnitTest() {Singleton.Instance.Name = "Hello";Singleton.Instance.Name = "World";Debug.Log(Singleton.Instance.Name);
}

使用单例模式获取唯一的游戏服务对象

一般来说,游戏需要一个GameManger类来管理一些功能和全局数据,在《P级阵地》中,PBaseDefenseGame就代表这样一个类,并且应用单例模式设计这个类:

img

参与者说明:

  • PBaseDefenseGame

    • 游戏主程序,内部包含了类型为PBaseDefenseGame的静态成员属性_instance,作为该类唯一的对象。
    • 提供使用C# getter实现的静态成员方法Instance,用它来获取唯一的静态成员属性_instance
  • BattleScene

    • PBaseDefenseGame类的客户端,使用PBaseDefenseGame.Instance来获取唯一的对象
PBaseDefenseGame.cspublic class PBaseDefenseGame {private static PBaseDefenseGame _instance;public static PBaseDefenseGame Instance {get {if (_instance == null) {_instance = new PBaseDefenseGame();}return _instance;}}...private PBaseDefenseGame() { }
}BattleState.cspublic class BattleScene: ISceneState {...pubic override void StateBegin() {PBaseDefenseGame.Instance.Initinal();//通过Instance直接访问PBaseDefenseGame对象}...
}SoldierClickScript.cspublic class SoldierOnClick: MonoBehavior {...public void OnClick() {PBaseDefenseGame.Instance.ShowSoldierInfo(Solder);}
}

反对使用单例模式的原因

单例模式似乎看起来非常方便,不必为了“安排参数传递”和“设置引用”伤脑筋,你可以在项目的任何地方通过Instance方法使用该类对象。然而,大多数资深设计者都反对滥用单例模式,有以下几个原因:

  • 全局变量的过度滥用:实质上,单例模式类也是一种全局变量,然而绝大多数类从设计角度需要保有“适当可视性”,很多类我们并不需要甚至并不愿意它被全局共享,单例模式在这种情况下可以被认为是一种“放弃思考”的暴力求解手段。
  • 违反“开-闭原则”:让一个类成为单例类,理论上它就失去了继承能力,就无法对修改关闭。

但是,也有两种方法可以让单例模式返回接口类——即父类为单例模式,并让子类继承实现:

  • 子类向父类注册实体对象,让父类的Instance方法返回对象时,按条件查表发挥对应的子类对象。
  • 每个子类都实现单例模式,再由父类的Instance去获取这些子类。(《P级阵地》采用类似的方法)。

少用单例模式如何方便地引用到单一对象

让类具有计数功能,即通过一个静态类属性计数器来限制对象的数量。

**将A类对象设置为B类的成员,B类的方法便可以方便的使用A类对象。这也是“依赖注入”的方式之一。**可以让被引用的对象不必通过参数传递的方式,就能被类的其他方法引用.按照设置的方式又可以分为"分别设置"和"指定类静态成员"两种。

  1. 分别设置

    在初始化的时候给用到的每个B类将A类对象传入

    public class PBaseDefenseGame {public void Initinal() {m_GameEventSystem = new GameEventSystem(this);//将PBaseDefenseGame类对象传入...}
    }public abstract class IGameSystem {protected PBaseDefenseGame m_PBDGame = null;public IGameSystem(PBaseDefenseGame PBDGame) {m_PBDGame = PBDGame;}
    }public class CampSystem: IGameSystem {public CampSystem(PBaseDefenseGame PBDGame): base(PBDGame) {Initialize();}public void ShowCaptiveCamp() {m_PBDGame.ShowGameMsg("获得俘兵营");//每个系统都可以通过成员m_PBDGame使用PBaseDefenseGame类对象的方法和属性}
    }
    
  2. 指定类的静态成员

    A类的功能若需要使用到B类的方法,并且A类在产生其对象时具有下列几种情况:

    • 产生对象的位置不确定

    • 有多个地方可以产生对象

    • 生成的位置无法引用到

    • 有众多子类

      (实际上与上面相对,1.分别设置中的产生对象位置就是确定的)

​ 当满足上述情况之一时,可以直接将B类对象设置为A类中的"静态成员属性", 让该类的对象都可以直接使用。

// PBaseDefenseGame.cspublic class PBaseDefenseGame {public void Initinal() {m_StageSystem = new StageSystem(this);// 注入其他系统EnemyAI.SetStageSystem(m_StageSystem);}
}
//举例来说,敌方单位AI类(EnemyAI), 在运行时需要使用关卡系统(StageSystem)的信息,但EnemyAI对象产生的位置是在敌方单位建造者(EnemyBuilder)之下:
EnemyBuilder.cspublic class EnemyBuilder: ICharacterBuilder {public override void AddAI() {EnemyAI theAI = new EnemyAI(m_BuildParam.NewCharacter, m_BuildParam.AttackPosition);m_BuildParam.NewCharacter.SetAI(theAI);}
}
//按照"最少知识原则(LKP)",会希望敌方单位的建造者(EnemyBuilder)减少对其他无关类的引用.因此,在产生敌方单位AI(EnemyAI)对象时,敌方单位建造者(EnemyBuilder)无法将关卡系统(StageSystem)对象设置给敌方单位AI,这是属于上述"生成的位置无法引用到"的情况.所以,可以在敌方单位AI(EnemyAI)类中,提供一个静态成员属性和静态方法,让关卡系统(StageSystem)对象产生的当下,就设置给敌方单位AI(EnemyAI)类:
public class EnemyAI: ICharacterAI {private static StageSystem m_StageSystem = null;public static void SetStageSystem(StageSystem StageSystem) {m_StageSystem = StageSystem;}public ovrride bool CanAttackHeart() {m_StageSystem.LoseHeart();return true;}
}
  1. 直接使用静态类
public static class PBDFactory {private static IAssetFactory m_AssetFactory = null;public static IAssetFactory GetAssetFactory() {if (m_AssetFactory == null) {if (m_bLoadFromResource) {m_AssetFactory = new ResourceAssetFactory();} else {m_AssetFactory = new RemoteAssetFactory();}}return m_AssetFactory;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/65414.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pygame飞机大战

飞机大战 1.main类2.配置类3.游戏主类4.游戏资源类5.资源下载6.游戏效果 1.main类 启动游戏。 from MainWindow import MainWindow if __name__ __main__:appMainWindow()app.run()2.配置类 该类主要存放游戏的各种设置参数。 #窗口尺寸 #窗口尺寸 import random import p…

如何让用户在网页中填写PDF表格?

在网页中让用户直接填写PDF表格,可以大大简化填写、打印、扫描和提交表单的流程。通过使用复选框、按钮和列表等交互元素,PDF表格不仅让填写过程更高效,还能方便地在电脑或移动设备上访问和提交数据。 以下是在浏览器中显示可填写PDF表单的四…

ThinkPHP 8高效构建Web应用-获取请求对象

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《2025新书 ThinkPHP 8高效构建Web应用 编程与应用开发丛书 夏磊 清华大学出版社教材书籍 9787302678236 ThinkPHP 8高效构建Web应用》【摘要 书评 试读】- 京东图书 使用VS Code开发ThinkPHP项目-CSDN博客 编程与应用开…

23.行号没有了怎么办 滚动条没有了怎么办 C#例子

新建了一个C#项目,发现行号没有了。 想把行号调出来,打开项目,选择工具>选项> 如下图,在文本编辑器的C#里有一个行号,打开就可以了 滚动条在这里:

30天开发操作系统 第 12 天 -- 定时器

前言 定时器(Timer)对于操作系统非常重要。它在原理上却很简单,只是每隔一段时间(比如0.01秒)就发送一个中断信号给CPU。幸亏有了定时器,CPU才不用辛苦地去计量时间。……如果没有定时器会怎么样呢?让我们想象一下吧。 假如CPU看不到定时器而仍想计量时…

el-table 实现纵向多级表头

为了实现上图效果,最开始打算用el-row、el-col去实现,但发现把表头和数据分成两大列时,数据太多时会导致所在格高度变高。但由于每一格数据肯定不一样,为保持高度样式一致,就需要我们手动去获取最高格的高度之后再设置…

uni-app深度解码:跨平台APP开发的核心引擎与创新实践

在当今数字化浪潮中,移动应用市场呈现出爆炸式增长。为了满足不同用户群体在不同操作系统上的需求,跨平台 APP 开发成为众多开发者的首选策略。uni-app 作为一款领先的跨平台开发框架,以其独特的优势和创新的实践在众多同类产品中脱颖而出。它…

oxml中创建CT_Document类

概述 本文基于python-docx源码,详细记录CT_Document类创建的过程,以此来加深对Python中元类、以及CT_Document元素类的认识。 元类简介 元类(MetaClass)是Python中的高级特性。元类是什么呢?Python是面向对象编程…

jenkins入门6 --拉取代码

Jenkins代码拉取 需要的插件,缺少的安装下 新建一个item,选择freestyle project 源码管理配置如下:需要添加git库地址,和登录git的用户密码 配置好后执行编译,成功后拉取的代码在工作空间里

在 ASP.NET CORE 中上传、下载文件

创建 Web API 来提供跨客户端和服务器的文件上传和下载是常有的事。本文将介绍如何通过 ASP.NET CORE 来实现。 首先在 Visual Studio 中创建空的 Web API 项目,然后选择目标框架 .Net Core 3.1。 创建名为 FileController 的控制器,提供操作文件的接口…

vue2迁移至rsbuild

背景 由于远程机器配置较低,每次运行vue2项目都会非常卡。后期项目文件、路由更多的时候,启动到一半直接会跳出open too many files类似的错误,尝试将路由屏蔽掉只剩下开发所需的一个路由也不行(不是说webpack的打包是全部打包&am…

智能手机租赁系统全新模式改变消费习惯与商家盈利路径

内容概要 智能手机租赁系统的崛起,让我们瞄到了一个消费市场的新风向标。想象一下,传统上人们总是为了最新款手机奋不顾身地排队、借钱甚至是透支信用卡。现在,通过灵活的租赁选项,消费者可以更加随意地体验高科技产品&#xff0…

浏览器报错:您的连接不是私密连接,Kubernetes Dashboard无法打开

问题描述 部署完成Kubernetes Dashboard后,打开HTTPS的web页面,Chrome和Edge浏览器都无法正常加载页面,会提示您的连接不是私密连接的报错。 ​​​​​​​​​​​​ 原因: 浏览器不信任这些自签名的ssl证书,为了…

docker pull(拉取镜像)的时候,无法下载或者卡在Waiting的解决方法

docker pull的时候,卡在Waiting的解决方法 一般情况(大部分镜像都可以拉取)更换镜像源 进一步(如es等拉取不到)在镜像同步站搜索详细步骤 还可以在挂载的时候,让其下载对应的版本 一般情况(大部…

注册中心如何选型?Eureka、Zookeeper、Nacos怎么选

这是小卷对分布式系统架构学习的第9篇文章,第8篇时只回答了注册中心的工作原理的内容,面试官的第二个问题还没回答,今天再来讲讲各个注册中心的原理,以及区别,最后如何进行选型 上一篇文章:如何设计一个注册…

恒压恒流原边反馈控制芯片 CRE6289F

CRE6289F 系列产品是一款内置高压 MOS 功率开关管的高性能多模式原边控制的开关电源芯片。较少的外围元器件、较低的系统成本设计出高性能的交直流转换开关电源。CRE6289F 系列产品提供了极为全面和性能优异的智能化保护功能,包括逐周期过流保护、软启动、芯片过温保…

开源 AI 智能名片 2+1 链动模式商城小程序在商业营销中的心理博弈与策略应用

摘要:在当今竞争激烈的商业环境中,理解消费者心理对营销成败起着关键作用。本文聚焦于消费者 “占便宜” 心理,深入探讨开源 AI 智能名片 21 链动模式商城小程序如何利用这一心理,在 “双十一”“双十二” 等购物热潮背景下&#…

01 数据分析介绍及工具准备

数据分析介绍及工具准备 一、工具准备二、下载和使用Anaconda三、jupyter notebook常用快捷键 一、工具准备 数据科学库 NumPy,SciPy,Pandas,Scikit-Learn 数据可视化库 Matplotlib,Seaborn 编译器 Jupyter Notebook 数据科…

opencv摄像头标定程序实现

摄像头标定是计算机视觉中的一个重要步骤,用于确定摄像头的内参(如焦距、主点、畸变系数等)和外参(如旋转矩阵和平移向量)。OpenCV 提供了方便的工具来进行摄像头标定。下面分别给出 C 和 Python 的实现。 1. C 实现…

java项目之网上租贸系统源码(springboot+mysql+vue)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的网上租贸系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 基于Spring Boot的网上租贸…