多人联机射击游戏中的设计模式应用(一)

为了方便大家更加系统地学习和掌握各种常用的设计模式,下面通过一个综合实例——“多人联机射击游戏”来学习如何在实际开发中综合使用设计模式。

反恐精英(Counter-Strike, CS)、三角洲部队、战地等多人联机射击游戏广受玩家欢迎,在多人联机射击游戏的设计中,可以使用多种设计模式。下面我选取一些较为常用的设计模式进行分析:

(1) 抽象工厂模式

在联机射击游戏中提供了多种游戏场景,不同的游戏场景提供了不同的地图、不同的背景音乐、不同的天气等,因此可以使用抽象工厂模式进行设计,类图如图1所示:

多人联机射击游戏中的设计模式应用(一) - 图1

图1 抽象工厂模式实例类图

在图1中,SceneFactory充当抽象工厂,其子类SceneAFactory等充当具体工厂,可以创建具体的地图(Map)、背景音乐(Music)和天气(Weather)等产品对象,如果需要增加新场景,只需增加新的具体场景工厂类即可。

(2) 建造者模式

在联机射击游戏中每一个游戏人物角色都需要提供一个完整的角色造型,包括人物造型、服装、武器等,可以使用建造者模式来创建一个完整的游戏角色,类图如图2所示:

多人联机射击游戏中的设计模式应用(一) - 图2

图2 建造者模式实例类图

在图2中,PlayerCreatorDirector充当指挥者角色,PlayerBuilder是抽象建造者,其子类PlayerBuilderA和PlayerBuilderB是具体建造者,用于创建不同的游戏角色,Player是所创建的完整产品,即完整的游戏角色,它包含形体(body)、服装(costume)和武器(weapon)等组成部分。

(3) 工厂方法模式

在射击游戏中,AK47冲锋步枪、狙击枪、手枪等不同武器(Weapon)的外观、使用方法和杀伤力都不相同,玩家可以使用不同的武器,而且游戏升级时还可以增加新的武器,无需对现有系统做太多修改,可使用工厂方法模式来设计武器系统,类图如图3所示:

多人联机射击游戏中的设计模式应用(一) - 图3

图3 工厂方法模式实例类图

在图3中,WeaponFactory接口表示抽象武器工厂,其子类AK47GunFactory生产AK47Gun,SniperRifleFactory生产SniperRifle,不同的武器的display()、use()和fire()等方法有不同的实现。

(4) 迭代器模式

在射击游戏中,一个玩家可以拥有多种武器,如既可以拥有AK47冲锋枪,还可以拥有手枪和匕首,因此系统需要定义一个弹药库(武器的集合),在游戏过程中可以遍历弹药库(Magazine),选取合适的武器,在遍历弹药库时可使用迭代器模式,如类图如图4所示:

多人联机射击游戏中的设计模式应用(一) - 图4

图4 迭代器模式实例类图

在类Magazine中,可以通过迭代器遍历弹药库,Magazine类的代码片段如下所示:

  1. public class Magazine {
  2. private ArrayList weapons;
  3. private Iterator iterator;
  4. public Magazine() {
  5. weapons = new ArrayList();
  6. iterator = weapons.iterator();
  7. }
  8. public void display() {
  9. while(iterator.hasNext()) {
  10. ((Weapon)iterator.next()).display();
  11. }
  12. }
  13. ......
  14. }

除了遍历弹药库外,迭代器模式还可以用于遍历战队盟友等聚合对象。

(5) 命令模式

在射击游戏中,用户可以自定义快捷键,根据使用习惯来设置快捷键,如“W”键可以设置为“开枪”的快捷键,也可以设置为“前进”的快捷键,可通过命令模式来实现快捷键设置,类图如图5所示:

多人联机射击游戏中的设计模式应用(一) - 图5

图5 命令模式实例类图 在图5中,ShortcutKey充当请求调用者,在其press()方法中将判断用户按的是哪个按键,再调用命令对象的execute()方法,在具体命令对象的execute()方法中将调用接收者如ShotHandler、GoAheadHandler的action()方法来执行具体操作。在实现时可以将具体命令类类名和键盘按键的键码(Keycode)存储在配置文件中,配置文件格式如下所示:

  1. ……
  2. <FunctionMapping keycode="87" commandClass="ShotCommand"/>
  3. <FunctionMapping keycode="38" commandClass="GoAheadCommand"/>
  4. ……

如果需要更换快捷键,只需修改键码和具体命令类的映射关系即可;如果需要在游戏的升级版本中增加一个新功能,只需增加一个新的具体命令类,可通过修改配置文件来为其设置对应的按键,原有类库代码无需任何修改,很好地符合开闭原则。