设计模式与游戏完美开发(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.cs

public class PBaseDefenseGame {
    private static PBaseDefenseGame _instance;

    public static PBaseDefenseGame Instance {
        get {
            if (_instance == null) {
                _instance = new PBaseDefenseGame();
            }
            return _instance;
        }
    }
    ...
    private PBaseDefenseGame() { }
}

BattleState.cs

public class BattleScene: ISceneState {
    ...
    pubic override void StateBegin() {
        PBaseDefenseGame.Instance.Initinal();//通过Instance直接访问PBaseDefenseGame对象
    }
    ...
}

SoldierClickScript.cs

public 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.cs

public class PBaseDefenseGame {
    public void Initinal() {
        m_StageSystem = new StageSystem(this);
        // 注入其他系统
        EnemyAI.SetStageSystem(m_StageSystem);
    }
}
//举例来说,敌方单位AI类(EnemyAI), 在运行时需要使用关卡系统(StageSystem)的信息,但EnemyAI对象产生的位置是在敌方单位建造者(EnemyBuilder)之下:
EnemyBuilder.cs

public 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;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VSCode 在Windows下开发时使用Cmake Tools时输出Log乱码以及CPP文件乱码的终极解决方案

在Windows11上使用VSCode开发C程序的时候,由于使用到了Cmake Tools插件,在编译运行的时候,会出现输出日志乱码的情况,那么如何解决呢? 这里提供了解决方案: 当Settings里的Cmake: Output Log Encoding里设…

Solidity入门: 函数

函数 Solidity语言的函数非常灵活&#xff0c;可以进行各种复杂操作。在本教程中&#xff0c;我们将会概述函数的基础概念&#xff0c;并通过一些示例演示如何使用函数。 我们先看一下 Solidity 中函数的形式: function <function name>(<parameter types>) {in…

基于 Python 自动化接口测试(踩坑与实践)

文档&#xff1a;基于 Python 的自动化接口测试 目录 背景问题描述与解决思路核心代码修改点及其详细解释最终测试结果后续优化建议 1. 问题背景 本项目旨在使用 Python 模拟浏览器的请求行为&#xff0c;测试文章分页接口的可用性。测试目标接口如下&#xff1a; bashcoder…

Spring Boot教程之五十一:Spring Boot – CrudRepository 示例

Spring Boot – CrudRepository 示例 Spring Boot 建立在 Spring 之上&#xff0c;包含 Spring 的所有功能。由于其快速的生产就绪环境&#xff0c;使开发人员能够直接专注于逻辑&#xff0c;而不必费力配置和设置&#xff0c;因此如今它正成为开发人员的最爱。Spring Boot 是…

web-app uniapp监测屏幕大小的变化对数组一行展示数据作相应处理

web-app uniapp监测屏幕大小的变化对数组一行展示数据作相应处理 1.uni.getSystemInfoSync().screenWidth; 获取屏幕宽度 2.uni.onWindowResize&#xff08;&#xff09; 实时监测屏幕宽度变化 3.根据宽度的大小拿到每行要展示的数量itemsPerRow 4.为了确保样式能够根据 items…

使用强化学习训练神经网络玩俄罗斯方块

一、说明 在 2024 年暑假假期期间&#xff0c;Tim学习并应用了Q-Learning &#xff08;一种强化学习形式&#xff09;来训练神经网络玩简化版的俄罗斯方块游戏。在本文中&#xff0c;我将详细介绍我是如何做到这一点的。我希望这对任何有兴趣将强化学习应用于新领域的人有所帮助…

计算机网络 (32)用户数据报协议UDP

前言 用户数据报协议&#xff08;UDP&#xff0c;User Datagram Protocol&#xff09;是计算机网络中的一种重要传输层协议&#xff0c;它提供了无连接的、不可靠的、面向报文的通信服务。 一、基本概念 UDP协议位于传输层&#xff0c;介于应用层和网络层之间。它不像TCP那样提…

如何将 DotNetFramework 项目打包成 NuGet 包并发布

如何将 DotNetFramework 项目打包成 NuGet 包并发布 在软件开发过程中&#xff0c;将项目打包成 NuGet 包并发布到 NuGet 库&#xff0c;可以让其他开发者方便地引用和使用你的项目成果。以下是将 WixWPFWizardBA 项目打包成 NuGet 包并发布的详细步骤&#xff1a; 1. 创建 .n…

解决GitHub上的README.md文件的图片内容不能正常显示问题

一、问题描述 我们将项目推送到GitHub上后&#xff0c;原本在本地编写配置好可展现的相对路径图片内容&#xff0c;到了GitHub上却不能够正常显示图片内容&#xff0c;我们希望能够在GitHub上正常显示图片&#xff0c;如下图所示&#xff1a; 二、问题分析 现状&#xff1a;REA…

如何解决 VS Code 调试时无法查看 std 中变量的问题

在使用 VS Code 调试 C 程序时&#xff0c;我们经常遇到查看 std 容器或字符串变量时只显示一串数字而看不到实际值的情况。这是由于调试器未启用 pretty-printing 功能导致的。为了解决这个问题&#xff0c;可以在 launch.json 中进行配置。 问题描述 在调试 C 程序时&…

安装MySQL的五种方法(Linux系统和Windows系统)

一.在Linux系统中安装MySQL 第一种方法:在线YUM仓库 首先打开MySQL官网首页 www.mysql.com 找到【DOWNLOADS】选项&#xff0c;点击 下拉&#xff0c;找到 【MySQL Community(GPL) Downloads】 在社区版下载页面中&#xff0c;【 MySQL Yum Repository 】链接为在线仓库安装…

基于mybatis-plus历史背景下的多租户平台改造

前言 别误会&#xff0c;本篇【并不是】 要用mybatis-plus自身的多租户方案&#xff1a;在表中加一个tenant_id字段来区分不同的租户数据。并不是的&#xff01; 而是在假设业务系统已经使用mybatis-plus多数据源的前提下&#xff0c;如何实现业务数据库隔开的多租户系统。 这…

RabbitMQ高级篇之MQ可靠性 数据持久化

文章目录 消息丢失的原因分析内存存储的缺陷如何确保 RabbitMQ 的消息可靠性&#xff1f;数据持久化的三个方面持久化对性能的影响持久化实验验证性能对比Spring AMQP 默认持久化总结 消息丢失的原因分析 RabbitMQ 默认使用内存存储消息&#xff0c;但这种方式带来了两个主要问…

Openssl1.1.1s rpm包构建与升级

rpmbuild入门知识 openssh/ssl二进制升级 文章目录 前言一、资源准备1.下载openssh、openssl二进制包2.安装rpmbuild工具3.拷贝源码包到SOURCES目录下4.系统开启telnet&#xff0c;防止意外导致shh无法连接5.编译工具安装6.补充说明 二、制作 OpenSSL RPM 包1.编写 SPEC 文件2.…

【Unity3D】apk加密(global-metadata.dat加密)

涉及&#xff1a;apk、aab、global-metadata.dat、jks密钥文件、APKTool、zipalign 使用7z打开apk文件观察发现有如下3个针对加密的文件。 xxx.apk\assets\bin\Data\Managed\Metadata\global-metadata.dat xxx.apk\lib\armeabi-v7a\libil2cpp.so xxx.apk\lib\arm64-v8a\libil…

[免费]微信小程序(高校就业)招聘系统(Springboot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序(高校就业)招聘系统(Springboot后端Vue管理端)&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序(高校就业)招聘系统(Springboot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项目介绍…

RNN心脏病预测-Pytorch版本

本文为为&#x1f517;365天深度学习训练营内部文章 原作者&#xff1a;K同学啊 一 导入数据 import numpy as np import pandas as pd import torch from torch import nn import torch.nn.functional as F import seaborn as sns from sklearn.preprocessing import Standard…

科普CMOS传感器的工作原理及特点

在当今数字化成像的时代&#xff0c;图像传感器无疑是幕后的关键 “功臣”&#xff0c;它宛如一位神奇的 “光影魔法师”&#xff0c;通过光电效应这一奇妙的物理现象&#xff0c;将光子巧妙地转换成电荷&#xff0c;为图像的诞生奠定基础。而在众多类型的图像传感器中&#xf…

Ubuntu 18.04 解决screen无法滚屏的问题

Ubuntu 18.04 解决screen无法滚屏的问题_ubuntu screen 无法上滑-CSDN博客文章浏览阅读2.7k次&#xff0c;点赞2次&#xff0c;收藏3次。在etc/screenrc中加入termcapinfo xterm* ti:te重新进入screen的sessionscreen -d -r XXX_ubuntu screen 无法上滑https://blog.csdn.net/w…

2025年01月09日Github流行趋势

1. 项目名称&#xff1a;khoj 项目地址url&#xff1a;https://github.com/khoj-ai/khoj项目语言&#xff1a;Python历史star数&#xff1a;22750今日star数&#xff1a;1272项目维护者&#xff1a;debanjum, sabaimran, MythicalCow, aam-at, eltociear项目简介&#xff1a;你…