RPG游戏中的设计模式之策略模式
来源:互联网 发布:python dom解析xml 编辑:程序博客网 时间:2024/05/16 17:57
上篇博客分析了游戏中的单例模式,这篇博客分析一下游戏中使用到的策略模式。
看这篇博客的读者,如果没有看过上篇博客对游戏和单例模式的分析,建议看一下上一篇博客,最好将游戏下载下来运行一下,看一下源码和类图,对游戏的整体结构有个大概了解,这样博客读起来会比较轻松,也比较容易理解其中的模式.
上篇博客链接:RPG游戏中的设计模式之单例模式
策略模式
定义
定义了算法族,分别封装起来,让他们之间可以相互替换,此模式的算法可以独立于使用它的客户。[1]P24
可以将算法族理解为同一个算法的不同变体,典型的策略模式其实就是客户可以使用同一个算法的不同变体,使用的过程中可以更换。
适用性
以下情况可以使用策略模式
1. 需要使用一个算法的不同变体,如本文的游戏,Hero会使用不同的行为。
2. 许多相关的类仅仅行为有异,策略模式提供了一种用多个行为中的一个行为配置一个类的方法上面对策略模式的描述说的可能比较抽象,下面我们还是结合游戏分析一下策略模式。
游戏中的客户与算法族
游戏中的客户就是角色(Roles),而算法族(同一个算法的不同变体)就是发射子弹的行为。
打开VS工程中的类图ClassDiagram1.cd,我们可以看到。客户(所有角色)
这些角色,包括英雄,怪兽。双击Roles和Hero可以看到源码:
public abstract class Roles : RoAndMi { //拥有发射子弹的行为 protected FireBehavior fireBehavior; //更换发射子弹的行为 public void SetFireBehavior(FireBehavior fireBehavior) { this.fireBehavior = fireBehavior; } /// <summary> /// 调用fireBehavior.Fire()实现真正的发射 /// </summary> public void Fire() { fireBehavior.Fire(); } ... }
public class Hero : Roles { public Hero(int x, int y, int xspeed, int yspeed, int life, bool good) : base(x, y, myImage.Width, myImage.Height, xspeed, yspeed, life, good) { blb = new BloodBar(x,y, life); //Hero拥有FireOneMissilesByHero行为 SetFireBehavior(new FireOneMissilesByHero(this)); } }
算法族(角色的行为)
public abstract class FireBehavior { protected Roles role;//哪个角色的射击 public abstract void Fire(); }
//Hero拥有的行为 class FireOneMissilesByHero : FireBehavior { public FireOneMissilesByHero(Roles r) { this.role = r; } //工厂方法,生产子弹 public override void Fire() { if (!role.Live) { return; } HitCheck.GetInstance().AddElement(new MissileHero(HitCheck.GetInstance().MyHero, 20, 20, HitCheck.GetInstance().MyHero.Good, MissileDirection.U, 10)); } }
类图
看一下他们的类图,会更加清晰的看到他们之间的关系
分析类图
游戏中所有角色都有个发射子弹的行为FireBehavior(继承自Roles),但是每个角色拥有的行为又不一样,如EnemyOne拥有FireMissileOneByEnemy,Hero拥有FireOneMissilesByHero,并且客户在程序运行过程中可以更换行为,如Hero在程序运行过程中可以通过调用SetFireBehavior方法更换发射子弹的行为为FireThreeMissilesByHero,也就是说Hero使用了算法FireBehavior的不同变体,同时也可以更换行为,实现了同一算法不同变体的互换。
Hero更换发射行为
Hero更换发射行为的代码在HitCheck中,游戏中,当英雄的经验值>100后,Hero装备升级,能够同时发射3个子弹,通过SetFireBehavior方法更换行为(同一算法的不同变体)
//更换行为,升级装备 if (myHero.score > 100) myHero.SetFireBehavior(new FireThreeMissilesByHero(myHero));//策略模式
这里为什么没有使用接口,而使用了抽象类
在游戏的实现过程中,发射子弹的行为与角色相关,所以在发射的时候,需要先判断是哪个角色,所以FireBehavior里面需要成员变量Roles,而接口是不能包含成员变量的,所以需要使用抽象类。仔细分析一下游戏的源码就会非常清楚为什么用抽象类了。
关于在设计过程中优先采用抽象类还是接口,这个问题要考虑很多东西,我不敢妄作评论,在具体设计过程中,我个人更加倾向于优先采用抽象类,而不是接口。
游戏中为什么要用策略模式
游戏中,英雄和敌人有个发射子弹的行为FireBehavior(继承自Roles),但是每个角色的行为都不一样,而且同一个角色在游戏中的发射子弹的行为也会发生变化,如英雄一开始每次只能发射一颗子弹,后来由于经验值增加,每次可以发射多个子弹,发射行为在游戏中会发生变化,在程序中需要更换行为,如果以后游戏要升级,角色会拥有更多的行为,如果使用策略模式,将不同的发射子弹的行为看成是FireBehavior的不同变体,让他们可以相互替换,那么以后升级游戏将会变得很容易。
在最初的版本中,是没有用策略模式的,也没有一种在程序中使用设计模式的意识,但是后来由于对设计模式的理解逐渐加深,自己也在思考,能否在游戏中加入设计模式,而且这个游戏非常适合加入策略模式,就这样,后面就把游戏给修改了。如果看过游戏的源码,就可以发现,以前的角色的行为都是作为角色类的一个方法,直接写在角色类里面的,如Hero源码中:
//public override void fire()//实际发射 //{ // if (!live) // { // return; // } // HitCheck.GetInstance().AddElement(new MissileHero(this, 20, 20, this.good, MissileDirection.LUU, 10)); // HitCheck.GetInstance().AddElement(new MissileHero(this, 20, 20, this.good, MissileDirection.U, 10)); // HitCheck.GetInstance().AddElement(new MissileHero(this, 20, 20, this.good, MissileDirection.RUU, 10)); //}
用了策略模式后,将这些都注释掉了,将所有角色的行为都抽取出来了,封装成算法族了。使用策略模式,能够降低客户和算法族之间的耦合性,能够使系统具有良好的扩展性和维护性。
OO原则
单例模式中,由于只有一个类,体现不出什么OO设计原则,在策略模式中,就可以体现出一些重要的OO设计原则。
OO原则:针对接口编程,而不是针对实现编程
[1]P11
其实整个设计模式的一个核心思想就是针对接口编程,而不是针对实现编程。这里的接口,并不是java或者C#中的interface,这里的接口其实指的是超类型,包括接口和抽象类,核心思想就是多态。看一下上面的类图
Roles的成员变量fireBehavior
//使用发射子弹的行为 protected FireBehavior fireBehavior;
和SetFireBehavior方法:
public void SetFireBehavior(FireBehavior fireBehavior) { this.fireBehavior = fireBehavior; }
我们可以发现,fireBehavior是抽象类型,SetFireBehavior参数也是抽象类型,这样做的一个好处就是,调用SetFireBehavior方法的时候,可以将任何一个FireBehavior的子类作为参数,如英雄初始行为为
SetFireBehavior(new FireOneMissilesByHero(this))
当经验值>100,更换发射子弹行为myHero.SetFireBehavior(new FireThreeMissilesByHero(myHero))
,最后统一通过FireBehavior中的Fire()方法实现多态调用,而不用知道具体是什么类型,这样可以实现在程序运行过程中更换行为,增加系统的扩展性。再举一个简单的例子
如下类图
图片来源[1]P12针对实现编程
Dog d=new Dog();d.bark();
针对接口编程
Animal animal=new Dog();animal.bark();
当我们针对接口编程时,就可以利用多态调用,而不用管实际的类型到底是什么,我们只需要关心如何正确地进行makeSound()就可以了。这样就增加了系统的弹性。
参考文献
[1] 《Head First设计模式(中文版)》 ,中国电力出版社
[2] 《设计模式:可复用面向对象软件的基础》(著名的GOF设计模式),机械工业出版社
- RPG游戏中的设计模式之策略模式
- RPG游戏中的设计模式之单例模式
- RPG游戏中的设计模式之工厂方法模式
- php中的设计模式之--策略模式
- 设计模式在游戏中的应用--策略模式(二)
- 游戏中的设计模式五(策略模式)
- 设计模式之策略模式——角色游戏
- 设计模式之策略模式
- 设计模式之策略模式
- 设计模式之策略模式
- 设计模式之策略模式
- 设计模式之策略模式
- 设计模式之策略模式
- 设计模式之策略模式
- 设计模式之策略模式
- 设计模式之策略模式
- 设计模式之策略模式
- 设计模式之策略模式
- java反射
- php 通过 ip地址 进行城市定位
- android 之 Handler 详解----(三)更新UI的方法
- Linux INCLUDE与LIB环境变量
- UML软件建模之UML的构成
- RPG游戏中的设计模式之策略模式
- 浙江大学PAT_乙级_1018. 锤子剪刀布 (20)
- linux shell中的单引号与双引号的区别(看完就不会有引号的疑问了)
- 使用 LiquiBase 管理数据库变更
- 黑马程序员-----第二篇 C语言总结(二)
- 集合框架面试
- 【第七届河南省赛】A.物资调度
- 正则表达式基本语法
- angularjs分页