[读书笔记] 设计模式与游戏完美开发

最近在看《设计模式与游戏完美开发》,文章将记录一些要点和一些设计模式实现

GoF定义的23种设计模式及应用场景

  • 系统设计可以采用的设计模式:单例、状态(场景切换)、外观(保证高内聚)、中介者(保证低解耦)
  • 角色设计可以采用的设计模式:桥接(武器、技能)、策略(属性计算)、模板方法(重复流程)、状态(AI)
  • 对象管理可以采用的设计模式:工厂(角色产生)、建造者(生产流程与功能表现分离)、享元(子弹、特效)
  • 逻辑设计可以采用的设计模式:命令(撤销回退)、责任链(关卡)
  • 辅助功能可以采用的设计模式:组合(UI、红点)、观察者(好友、成就)、备忘录(存档)、访问者(管理某一类对象)
  • 游戏优化可以采用的设计模式:装饰、适配器、代理
  • 其他模式:迭代器、原型、解释器、抽象工厂

中介者模式

在这里插入图片描述
关于中介者模式,我们想象在一家互联网公司,有多个Colleague(同事),Colleague(同事)之间需要合作开发,不管是采用哪种版本控制软件,都可以抽象为一个Mediator(中介者)。而每个Colleague(同事)需要有版本控制的权限,即Mediator引用,而ConcreteMediator(具体中介者)则为ConcreteColleague(具体同事)提供服务。

Mediator.cs

public abstract class Mediator
{public abstract void SendMessage(Colleague theColleage, string message);
}

ConcreateMediator.cs

public class ConcreateMediator : Mediator
{ConcreateColleague1 m_Colleague1 = null;ConcreateColleague2 m_Colleague2 = null;public void SetColleage1(ConcreateColleague1 theColleague){m_Colleague1 = theColleague;}public void SetColleage2(ConcreateColleague2 theColleague){m_Colleague2 = theColleague;}public override void SendMessage(Colleague theColleague, string message){if (theColleague == m_Colleague1){m_Colleague2.Request(message);} else if (theColleague == m_Colleague2){m_Colleague1.Request(message);}}
}

Colleage.cs

public abstract class Colleague
{protected Mediator m_Mediator = null;public Colleague(Mediator mediator){m_Mediator = mediator;}public abstract void Request(string message);
}

ConcreateColleage1.cs、ConcreateColleage2.cs

using UnityEngine;
public class ConcreateColleague1 : Colleague
{public ConcreateColleague1(Mediator mediator) : base(mediator){}public void Action(){m_Mediator.SendMessage(this, "Concreate1执行");}public override void Request(string message){Debug.Log("C2已接收:" + message);}
}
using UnityEngine;
public class ConcreateColleague2 : Colleague
{public ConcreateColleague2(Mediator mediator) : base(mediator){}public void Action(){m_Mediator.SendMessage(this, "Concreate2执行");}public override void Request(string message){Debug.Log("C2已接收:" + message);}
}

Test.cs

using UnityEngine;public class Test : MonoBehaviour
{void Start(){// 定义具体中介者、具体同事ConcreateMediator pMediator = new ConcreateMediator();ConcreateColleague1 pColleage1 = new ConcreateColleague1(pMediator);ConcreateColleague2 pColleage2 = new ConcreateColleague2(pMediator);// 添加同事pMediator.SetColleage1(pColleage1);pMediator.SetColleage2(pColleage2);// 同事执行行为,中介者负责派发消息pColleage1.Action();pColleage2.Action();}void Update(){}
}

tips:有些情况下,Mediator会同时采用单例模式实现,在这种情况下,尽量避免从Mediator派生ConcreateMediator,这是因为当ConcreateMediator作为单例使用时,会因为单例只会存在唯一示例,当父类有多个派生类时会有“白马非马”的逻辑诡辩。同时为了遵循开闭原则,也要尽量依赖“接口”,而非依赖“实现”。

里氏替换原则有至少以下两种含义:
里氏替换原则是针对继承而言的,如果继承是为了实现代码重用,那么共享的父类方法就应该保持不变,不能被子类重新定义。 如果继承的目的是为了多态,而多态的前提就是子类覆盖并重新定义父类的方法,应该将父类定义为抽象类。不符合LSP的最常见的情况是,父类和子类都是可实例化的非抽象类,且父类的方法被子类重新定义,这一类的实现继承会造成父类和子类间的强耦合,也就是实际上并不相关的属性和方法牵强附会在一起,不利于程序扩展和维护。


工厂模式

在这里插入图片描述
工厂模式是最简单的一种设计模式,由ConcreteCreate(具体工厂)产生ConcreateProduct(具体产品)
Creator.cs

public abstract class Creator
{public abstract Product FactoryMethod();
}

ConcreateCreatorProductA.cs

using UnityEngine;public class ConcreateCreatorProductA : Creator
{public ConcreateCreatorProductA(){Debug.Log("产生工厂A");}public override Product FactoryMethod(){return new ConcreateProductA();}
}

Product.cs

public abstract class Product { }

ConcreateProductA

using UnityEngine;public class ConcreateProductA : Product
{public ConcreateProductA(){Debug.Log("生产A");}
}

tips:通过不同子类工厂产生不同品类的产品,当产品种类过多时,会导致“工厂子类暴增”。这时候可以配合FactoryMethod传参实现工厂模式


建造者模式

在这里插入图片描述
对于一个工厂来说,一条流水线应该可以生产多种产品吗,即生产流程与产品装配是可以解耦的。由Director(建造者指示者)对产品生产流程进行管控,而Builder(建造者)有很多ConcerteBuilder(具体建造者)则负责不同功能的装配。

Director.cs

public class Director
{public Product m_Product;public Director() { }public void Construct(Builder theBuilder){m_Product = new Product();theBuilder.BuilderPart1(m_Product);theBuilder.BuilderPart2(m_Product);}public Product GetResult(){return m_Product;}
}

Builder.cs

public abstract class Builder
{public abstract void BuilderPart1(Product theProduct);public abstract void BuilderPart2(Product theProduct);
}

ConcreateBuilderA.cs

public class ConcreateBuilderA : Builder
{public override void BuilderPart1(Product theProduct){theProduct.AddPart("产品A工人:生产部件1");}public override void BuilderPart2(Product theProduct){theProduct.AddPart("产品A工人:生产部件2");}
}
using UnityEngine;
using System.Collections;public class ConcreateBuilderB : Builder
{public override void BuilderPart1(Product theProduct){theProduct.AddPart("产品B工人:生产部件1");}public override void BuilderPart2(Product theProduct){theProduct.AddPart("产品B工人:生产部件2");}
}

Product.cs

using UnityEngine;
using System.Collections.Generic;public class Product
{private List<string> m_Part = new List<string>();public Product() { }public void AddPart(string v){m_Part.Add(v);}public void ShowProduct(){foreach(var part in m_Part){Debug.Log(part);}}
}

Test.cs

using UnityEngine;public class Test : MonoBehaviour
{Director director = new Director();Product theProduct = null;void Start(){director.Construct(new ConcreateBuilderA());theProduct = director.GetResult();theProduct.ShowProduct();director.Construct(new ConcreateBuilderB());theProduct = director.GetResult();theProduct.ShowProduct();}
}

享元模式

在这里插入图片描述
书中描述的案例,个人以为不适合理解,故此处暂留


组合模式

“将对象以树状结构进行组合,用以表现部分与整体的层次关系”,UI的层次结构正好适合使用这种设计模式。

在这里插入图片描述
Component(组件界面)用来定义树形结构,以及每个结点都能使用的操作,Composite(组合结点,即根结点),实现Component定义的各个方法,Leaf(叶结点,终端结点)
IComponent.cs

using UnityEngine;public abstract class IComponent
{protected string m_Value;public abstract void Operation();public virtual void Add(IComponent theComponent){Debug.Log("子类未实现");}public virtual void Remove(IComponent theComponent){Debug.Log("子类未实现");}public virtual IComponent GetChild(int index){Debug.Log("子类未实现");return null;}
}

Composite.cs

using UnityEngine;
using System.Collections.Generic;public class Composite : IComponent
{List<IComponent> m_Childs = new List<IComponent>();public Composite(string value){m_Value = value;}public override void Add(IComponent theComponent){m_Childs.Add(theComponent);}public override void Remove(IComponent theComponent){m_Childs.Remove(theComponent);}public override IComponent GetChild(int index){return m_Childs[index];}public override void Operation(){Debug.Log("根节点执行" + m_Value);foreach (IComponent theComponent in m_Childs){theComponent.Operation();}}
}

Leaf.cs

using UnityEngine;public class Leaf : IComponent
{public Leaf(string value){m_Value = value;}public override void Operation(){Debug.Log("叶节点执行:" + m_Value);}
}

test.cs

using UnityEngine;public class Test2 : MonoBehaviour
{IComponent theRoot;void Start(){theRoot = new Composite("Root");theRoot.Add(new Leaf("Leaf1"));theRoot.Add(new Leaf("Leaf2"));IComponent theChild1 = new Composite("Child1");theChild1.Add(new Leaf("child1.Leaf1"));theChild1.Add(new Leaf("child1.Leaf2"));theRoot.Add(theChild1);IComponent theChild2 = new Composite("Child1");theChild2.Add(new Leaf("child2.Leaf1"));theChild2.Add(new Leaf("child2.Leaf2"));theRoot.Add(theChild2);theRoot.Operation();}
}

tips:GameObject.Find()会遍历所有场景中的对象,而且在Unity中存在重名的问题,会产生性能问题。可以通过自己实现Find,保证只在指定的Canvas下查找
UI的功能在场景初始化时决定,保证UI逻辑与表现分离


责任链模式

在这里插入图片描述
责任链可以理解为数据结构的链表,每个Handler都有下一个Handler的引用。在关卡系统的设计上,很多项目会采用CreateStage(创建关卡)、CheckNextStage(检查进入下一关)方法来对场景进行操作,而采用责任链模式,则可以使用Handler(关卡对象)替代,通过类化关卡数据、通关条件能够提高关卡系统的灵活度。
Handler.cs

public abstract class Handler
{protected Handler m_NextHandle = null;public Handler(Handler theNextHandle){m_NextHandle = theNextHandle;}public virtual void HandleRequest(int cost){if (m_NextHandle != null){m_NextHandle.HandleRequest(cost);}}
}

ConcreateHandle1.cs

using UnityEngine;public class ConcreateHandle1 : Handler
{public int m_CostCheck = 10;public ConcreateHandle1(Handler theNextHandle) : base(theNextHandle) { }public override void HandleRequest(int cost){if (cost < m_CostCheck){Debug.Log("ConcreateHandle2核准");} else{base.HandleRequest(cost);}}
}

ConcreateHandle2.cs

using UnityEngine;public class ConcreateHandle2 : Handler
{public int m_CostCheck = 20;public ConcreateHandle2(Handler theNextHandle) : base(theNextHandle) { }public override void HandleRequest(int cost){if (cost <= m_CostCheck){Debug.Log("ConcreateHandle2核准");}else{base.HandleRequest(cost);}}
}

Test.cs

using UnityEngine;
using System.Collections;public class Test : MonoBehaviour
{void Start(){ConcreateHandle2 theHandle2 = new ConcreateHandle2(null);ConcreateHandle1 theHandle1 = new ConcreateHandle1(theHandle2);theHandle1.HandleRequest(5);theHandle1.HandleRequest(20);}
}

观察者模式

在这里插入图片描述
对于发布-订阅关系来说,一个Subject可以对应多个Observer,Subject维护观察者列表,并提供Notify()通知功能;Observer提供Update()方法,在Subject通知更新时调用。
Subject.cs

using System.Collections.Generic;public abstract class Subject
{List<Observer> m_Observer = new List<Observer>();public void Attach(Observer theObserver){m_Observer.Add(theObserver);}public void Detach(Observer theObserver){m_Observer.Remove(theObserver);}public void Notify(){// 通知所有观察者foreach (Observer observer in m_Observer){observer.Update();}}
}

ConcreateSubject.cs

using UnityEngine;
using System.Collections;public class ConcreateSubject : Subject
{string m_SubjectState;public void SetState(string state){m_SubjectState = state;Notify();}public string GetState(){return m_SubjectState;}
}

Observer.cs

using UnityEngine;
using System.Collections;public abstract class Observer
{public abstract void Update();
}

ConcreateObserver.cs

using UnityEngine;public class ConcreateObserver : Observer
{string m_ObjectState;ConcreateSubject m_Subject = null;public ConcreateObserver(ConcreateSubject subject){m_Subject = subject;}public override void Update(){Debug.Log("ConcreteObserver.Update");Debug.Log("ConcreateObserver1:Subject" + m_ObjectState);}
}

Test.cs

using UnityEngine;
using System.Collections;public class Test : MonoBehaviour
{ConcreateSubject theSubject = null;ConcreateObserver theObserver = null;// Use this for initializationvoid Start(){theSubject = new ConcreateSubject();theObserver = new ConcreateObserver(theSubject);theSubject.Attach(theObserver1); // 观察者参与订阅theSubject.SetState("Subject状态1"); // 主题修改状态,并通知theObserver1.ShowState(); // 观察者接收通知并执行Update()操作}
}

备忘录模式

在这里插入图片描述
在不违反封装的原则下,获取一个对象的内部状态并保留在外部。当我们想办法去获取游戏系统的数据时,现有系统难免被改动,而如果由游戏系统主动提供数据,则可以保证系统的封装性,这也是备忘录模式的精髓。Originator(记录拥有者)会自动产出需要保存的记录Memento(记录保存者,我称之为记忆体),由Caretaker(记录看守者)维护多个Memento。

Originator.cs

using UnityEngine;public class Originator
{string m_State;public void SetInfo(string state){m_State = state;}public void ShowInfo(){Debug.Log("Originator State" + m_State);}public Memento CreateMemento(){Memento newMemento = new Memento();newMemento.SetState(m_State);return newMemento;}public void SetMemento(Memento m){m_State = m.GetState();}
}

Memento.cs

public class Memento
{string m_State;public string GetState(){return m_State;}public void SetState(string state){m_State = state;}
}

Caretaker.cs

using System.Collections.Generic;public class Caretaker
{Dictionary<string, Memento> m_Mementos = new Dictionary<string, Memento>();public void AddMemento(string version, Memento theMemento){if (m_Mementos.ContainsKey(version) == false)m_Mementos.Add(version, theMemento);elsem_Mementos[version] = theMemento;}public Memento GetMemento(string version){if (m_Mementos.ContainsKey(version) == false)return null;return m_Mementos[version];}
}

Test.cs

using System.Collections;public class Test : MonoBehaviour
{void Start(){Originator theOriginator = new Originator();Caretaker theCaretaker = new Caretaker();theOriginator.SetInfo("version1");theOriginator.ShowInfo();theCaretaker.AddMemento("1", theOriginator.CreateMemento());theOriginator.SetInfo("version2");theOriginator.ShowInfo();theCaretaker.AddMemento("2", theOriginator.CreateMemento());theOriginator.SetInfo("version3");theOriginator.ShowInfo();theOriginator.SetMemento(theCaretaker.GetMemento("2"));theOriginator.ShowInfo();theOriginator.SetMemento(theCaretaker.GetMemento("1"));theOriginator.ShowInfo();}
}

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

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

相关文章

iOS开发——GPUImage源码解析

一、基本概念 GPUImage&#xff1a;一个开源的、基于openGL的图片或视频的处理框架&#xff0c;其本身内置了多达120多种常见的滤镜效果&#xff0c;并且支持照相机和摄像机的实时滤镜&#xff0c;并且能够自定义图像滤镜。同时也很方便在原有基础上加入自己的滤镜Filter&#…

[读书笔记] 敏捷软件开发:原则、模式与实践

关于面向对象编程的一些理解&#xff0c;这本书主要看六大原则的部分&#xff0c;书中关于设计模式的内容由于之前的那本《设计模式与游戏完美开发》已经很好的讲解了游戏开发领域的应用&#xff0c;所以不多关注。 面向对象的六大原则 单一职责原则SRP&#xff1a;一个类应该…

Caffe-SSD相关源码说明和调试记录

1 对Blob的理解及其操作&#xff1a; Blob是一个四维的数组。维度从高到低分别是: (num_&#xff0c;channels_&#xff0c;height_&#xff0c;width_) 对于图像数据来说就是&#xff1a;图片个数&#xff0c;彩色通道个数&#xff0c;宽&#xff0c;高 Blob中数据是row-…

[游戏策划] 读书笔记

交互式媒体最有趣的地方在于&#xff0c;它让玩家直面问题&#xff0c;思考、尝试各种解决方案&#xff0c;并体验各种解决方案的结果。然后玩家可以回到思考阶段&#xff0c;规划下一步行动。这种反复试错的过程中&#xff0c;玩家的脑海里就会构建出一个互动的世界。 [读书笔…

ECS框架学习

DOTS Unity DOTS是Unity官方基于ECS架构开发的一套包含Burst编辑器和JobSystem的技术栈&#xff0c;它旨在充分利用多核处理器的特点&#xff0c;充分发挥ECS的优势。 安装 Entities、Burst、Jobs、Hybrid Renderer&#xff08;必选&#xff0c;用于DOTS的渲染相关&#xf…

辅助排序和Mapreduce整体流程

一、辅助排序 需求&#xff1a;先有一个订单数据文件&#xff0c;包含了订单id、商品id、商品价格&#xff0c;要求将订单id正序&#xff0c;商品价格倒序&#xff0c;且生成结果文件个数为订单id的数量&#xff0c;每个结果文件中只要一条该订单最贵商品的数据。 思路&#xf…

[读书笔记] 史玉柱自述:我的营销心得

与下属的关系 从玩家角度设定目标 目标感的设计 论随机性 在前15分钟留住玩家 实际观察玩家对于游戏的翻译反应 好游戏是改出来的 注重细节 决策民主、责任人制度 论简单与复杂的关系 游戏经济中的投放与回收 避免进入降低压力的怪圈 创业初期的股份分配 单个行业…

记一次面试腾讯的奇葩经历

阅读本文大概需要 2.8 分钟。 作者&#xff1a;黄小斜 文章来源&#xff1a;微信公众号【程序员江湖】 ​ 上回说到&#xff0c;我腾讯面试出师不利&#xff0c;简历随即进入备胎池&#xff0c;不过没过多久&#xff0c;转机还是来了。 大概是一周之后&#xff0c;我的电话响起…

foot

码云链接&#xff1a;https://gitee.com/zyyyyyyyyyyy/codes/rcfdzmin4a82v975pl1ko47 效果图&#xff1a; 原网站截图&#xff1a; 源代码&#xff1a; <!DOCTYPE html><html><head><meta charset"UTF-8"><title></title><s…

Taro项目遇到的问题

1. https://taro-ui.aotu.io/#/docs/questions 请在Taro项目根目录找到 config/index.js 文件中的h5项&#xff0c;添加如下&#xff1a; h5: {...esnextModules: [taro-ui] } 2. 原则&#xff1a;少什么就装什么 少了 babel-plugin-transform-decorators-legacy &#xff0c;那…

严重: StandardServer.await: create[localhost:8005]

①看看任务管理器&#xff0c;是否打开了多个Tomcat程序 如果是&#xff0c;关闭其中一个 ②可能是端口冲突 1、将tomcat安装目录下的conf/server.xml中的8005端口号改为其它的端口号。&#xff08;不建议&#xff0c;因为会衍生出其他错误&#xff09; 2、将正在使用的8005端…

java里short,int,long,float,double范围及可写位数

一、取值范围 1、int二进制位数&#xff1a;32 包装类&#xff1a;java.lang.Integer最小值&#xff1a;Integer.MIN_VALUE -2147483648 &#xff08;-2的31次方&#xff09;最大值&#xff1a;Integer.MAX_VALUE 2147483647 &#xff08;2的31次方-1&#xff09;2、short 二…

第六周编程总结

6-1 求两数平方根之和 &#xff08;10 分) 函数fun的功能是&#xff1a;求两数平方根之和&#xff0c;作为函数值返回。例如&#xff1a;输入12和20&#xff0c;输出结果是&#xff1a;y 7.936238。 函数接口定义&#xff1a; double fun (double a, double b); 其中 a和 b是用…

【CH5105】Cookies

也是一道线型动态规划的好题…… 读入每个人的贪婪度之后&#xff0c;对其按照从大到小的顺序排序&#xff0c;定义状态f[i][j]为前i个人&#xff08;排序后&#xff09;分j个饼干的答案&#xff0c;那么答案为f[n][m],考虑状态转移方程。 1、若给第i个人的饼干数大于1 &#x…

sharing-jdbc实现读写分离及分库分表

需求&#xff1a; 分库&#xff1a;按业务线business_id将不同业务线的订单存储在不同的数据库上&#xff1b; 分表&#xff1a;按user_id字段将不同用户的订单存储在不同的表上&#xff0c;为方便直接用非分片字段order_id查询&#xff0c;可使用基因法&#xff1b; 读写分离&…

性能测试学习05_lr(根据接口文档写脚本+参数化)

1、根据接口文档写脚本&#xff0c;函数&#xff08;web_custom_request&#xff09;&#xff0c;完成get&#xff0c;post请求&#xff08;注册&#xff0c;登录&#xff09; 代码&#xff1a; Action() {lr_save_string("请填写你的IP", "IP");//注册/*w…

java 历届试题 合根植物

问题描述w星球的一个种植园&#xff0c;被分成 m * n 个小格子&#xff08;东西方向m行&#xff0c;南北方向n列&#xff09;。每个格子里种了一株合根植物。这种植物有个特点&#xff0c;它的根可能会沿着南北或东西方向伸展&#xff0c;从而与另一个格子的植物合成为一体。如…

(软件项目管理)项目会议纪要模板

备注&#xff1a; 七: 1、报送&#xff1a;把整理好的会议的内容报给上级的相关部门。2、主送&#xff1a;把整理好的会议的内容发放给下级相关部门。3、抄送&#xff1a;把整理好的会议的内容送给相关的同级单位或不相隶属的单位。

EVE-NG安装步骤

首先&#xff0c;需要EVE-NG客户端工具包 1、 1.1部分图 点击next 2、 保持默认全选&#xff0c;点击next 3、 点击install 4、选择I accept the agreement&#xff0c;点击next 5、下一步&#xff0c;继续点击next 6、选定安装位置&#xff0c;不清楚就默认C盘&#x…