在线客服
设计模式与游戏开发图书
人气:37

设计模式与游戏开发

十年磨一剑,作者将设计模式理论巧妙地融入到实践中,以一个游戏的完整实现呈现设计模式的应用及经验的传承

内容简介

《设计模式与游戏开发》是作者“十年磨一剑”,将设计模式理论巧妙地融合到实践中的教材。 全书采用了整合式的项目教学,即以一个游戏的范例来应用23种设计模式的实现贯穿全书,让读者学习到整个游戏开发的全过程和作者想要传承的经验,并以浅显易懂的比喻来解析难以理解的设计模式,让想深入了解此领域的读者更加容易上手。 本书既可以作为大学、专科和职业院校游戏程序设计专业的教材,也可以作为游戏从业人员提高游戏设计能力和规范运用设计模式的培训教材,还可以作为有这方面职业兴趣的读者学习和提高的自学参考书。

编辑推荐

十年磨一剑,作者将设计模式理论巧妙地融入到实践中,以一个游戏的完整实现呈现设计模式的应用及经验的传承 《轩辕剑》之父——蔡明宏、博学游戏制作人——李佳泽、Product Evangelist at Unity Technologies——Kelvin Lo、信仁软件设计创办人—— 赖信仁、博学3D游戏美术——刘明恺 联合推荐全书采用了整合式的项目教学,即以一个游戏的范例来应用23种设计模式的实现贯穿全书,让读者学习到整个游戏开发的全过程和作者想要传承的经验,并以浅显易懂的比喻来解析难以理解的设计模式,让想深入了解此领域的读者更加容易上手。

作者简介

蔡升达当年为实现游戏梦从学术界进入游戏业,累积了10年游戏开发经验,包含多款大型多人线上游戏(MMORPG)、网页游戏(Web Game)、移动平台连线游戏(APP Game)。曾任职于台湾地区知名游戏开发公司,担任游戏设计师、专案程序统筹、研发技术中心经理等职务,现任知名新创团队研发总监一职。擅长游戏程序设计、网络程序设计、分布式系统设计、电脑图学、影像搜索、游戏制作规划及运作、游戏专案管理。喜欢阅读,家中有上千本藏书,包含信息技术、文学、奇幻小说、历史小说等,因为看了很多,所以也有很多想法想与人分享。构想多年之后,某日下午终于下笔开始了这本书。十年磨一剑,作者将设计模式理论巧妙地融入到实践中,以一个游戏的完整实现呈现设计模式的应用及经验的传承

目录

第1篇 设计模式与游戏设计

第1章 游戏实现中的设计模式 2

1.1 设计模式的起源 2

1.2 软件的设计模式是什么? 3

1.3 面向对象设计中常见的设计原则 4

1.4 为什么要学习设计模式 7

1.5 游戏程序设计与设计模式 8

1.6 模式的应用与学习方式 10

1.7 结论 11

第2章 游戏范例说明 12

2.1 游戏范例 12

2.2 GoF的设计模式范例 15

第2篇 基础系统

第3章 游戏场景的转换——状态模式(State) 20

3.1 游戏场景 20

3.1.1 场景的转换 20

3.1.2 游戏场景可能的实现方式 23

3.2 状态模式(State) 24

3.2.1 状态模式(State)的定义 24

3.2.2 状态模式(State)的说明 25

3.2.3 状态模式(State)的实现范例 25

3.3 使用状态模式(State)实现游戏场景的转换 28

3.3.1 SceneState的实现 28

3.3.2 实现说明 29

3.3.3 使用状态模式(State)的优点 35

3.3.4 游戏执行流程及场景转换说明 36

3.4 状态模式(State)面对变化时 37

3.5 结论 37

第4章 游戏主要类——外观模式(Facade) 39

4.1 游戏子功能的整合 39

4.2 外观模式(Facade) 41

4.2.1 外观模式(Facade)的定义 41

4.2.2 外观模式(Facade)的说明 42

4.2.3 外观模式(Facade)的实现说明 43

4.3 使用外观模式(Facade)实现游戏主程序 44

4.3.1 游戏主程序架构设计 44

4.3.2 实现说明 45

4.3.3 使用外观模式(Facade)的优点 47

4.3.4 实现外观模式(Facade)时的注意事项 48

4.4 外观模式(Facade)面对变化时 48

4.5 结论 48

第5章 获取游戏服务的对象——单例模式(Singleton) 50

5.1 游戏实现中的对象 50

5.2 单例模式(Singleton) 51

5.2.1 单例模式(Singleton)的定义 51

5.2.2 单例模式(Singleton)的说明 51

5.2.3 单例模式(Singleton)的实现范例 52

5.3 使用单例模式(Singleton)获取的游戏服务对象 53

5.3.1 游戏服务类的单例模式实现 53

5.3.2 实现说明 54

5.3.3 使用单例模式(Singleton)后的比较 55

5.3.4 反对使用单例模式(Singleton)的原因 55

5.4 少用单例模式(Singleton)时如何方便地引用到单一对象 58

5.5 结论 63

第6章 游戏内各系统的整合——中介者模式(Mediator) 64

6.1 游戏系统之间的沟通 64

6.2 中介者模式(Mediator) 68

6.2.1 中介者模式(Mediator)的定义 69

6.2.2 中介者模式(Mediator)的说明 69

6.2.3 中介者模式(Mediator)的实现范例 69

6.3 中介者模式(Mediator)作为系统之间的沟通接口 72

6.3.1 使用中介者模式(Mediator)的系统架构 73

6.3.2 实现说明 73

6.3.3 使用中介者模式(Mediator)的优点 79

6.3.4 实现中介者模式(Mediator)时的注意事项 79

6.4 中介者模式(Mediator)面对变化时 80

6.5 结论 80

第7章 游戏的主循环——Game Loop 82

7.1 GameLoop由此开始 82

7.2 怎么实现游戏循环(Game Loop) 84

7.3 在Unity3D中实现游戏循环 85

7.4 P级阵地的游戏循环 89

7.5 结论 92

第3篇 角色的设计

第8章 角色系统的设计分析 94

8.1 游戏角色的架构 94

8.2 角色类的规划 95

第9章 角色与武器的实现——桥接模式(Bridge) 98

9.1 角色与武器的关系 98

9.2 桥接模式(Bridge) 103

9.2.1 桥接模式(Bridge)的定义 103

9.2.2 桥接模式(Bridge)的说明 107

9.2.3 桥接模式(Bridge)的实现范例 108

9.3 使用桥接模式(Bridge)实现角色与武器接口 110

9.3.1 角色与武器接口设计 110

9.3.2 实现说明 111

9.3.3 使用桥接模式(Bridge)的优点 116

9.3.4 实现桥接模式(Bridge)的注意事项 116

9.4 桥接模式(Bridge)面对变化时 116

9.5 结论 117

第10章 角色属性的计算——策略模式(Strategy) 118

10.1 角色属性的计算需求 118

10.2 策略模式(Strategy) 121

10.2.1 策略模式(Strategy)的定义 122

10.2.2 策略模式(Strategy)的说明 122

10.2.3 策略模式(Strategy)的实现范例 123

10.3 使用策略模式(Strategy)实现攻击计算 124

10.3.1 攻击流程的实现 125

10.3.2 实现说明 125

10.3.3 使用策略模式(Strategy)的优点 132

10.3.4 实现策略模式(Strategy)时的注意事项 133

10.4 策略模式(Strategy)面对变化时 134

10.5 结论 135

第11章 攻击特效与击中反应——模板方法模式(Template Method) 137

11.1 武器的攻击流程 137

11.2 模板方法模式(Template Method) 139

11.2.1 模板方法模式(Template Method)的定义 139

11.2.2 模板方法模式(Template Method)的说明 141

11.2.3 模板方法模式(Template Method)的实现范例 141

11.3 使用模板方法模式实现攻击与击中流程 142

11.3.1 攻击与击中流程的实现 143

11.3.2 实现说明 143

11.3.3 运用模板方法模式(Template Method)的优点 145

11.3.4 修改击中流程的实现 145

11.4 模板方法模式(Template Method)面对变化时 147

11.5 结论 149

第12章 角色AI——状态模式(State) 150

12.1 角色的AI 150

12.2 状态模式(State) 158

12.3 使用状态模式(State)实现角色AI 159

12.3.1 角色AI的实现 159

12.3.2 实现说明 160

12.3.3 使用状态模式(State)的优点 169

12.3.4 角色AI执行流程 169

12.4 状态模式(State)面对变化时 170

12.5 结论 172

第13章 角色系统 174

13.1 角色类 174

13.2 游戏角色管理系统 176

第4篇 角色的产生

第14章 游戏角色的产生——工厂方法模式(Factory Method) 183

14.1 产生角色 183

14.2 工厂方法模式(Factory Method) 188

14.2.1 工厂方法模式(Factory Method)的定义 188

14.2.2 工厂方法模式(Factory Method)的说明 189

14.2.3 工厂方法模式(Factory Method)的实现范例 189

14.3 使用工厂方法模式(Factory Method)产生角色对象 195

14.3.1 角色工厂类 195

14.3.2 实现说明 196

14.3.3 使用工厂方法模式(Factory Method)的优点 199

14.3.4 工厂方法模式(Factory Method)的实现说明 199

14.4 工厂方法模式(Factory Method)面对变化时 203

14.5 结论 205

第15章 角色的组装——建造者模式(Builder) 206

15.1 角色功能的组装 206

15.2 建造者模式(Builder) 213

15.2.1 建造者模式(Builder)的定义 213

15.2.2 建造者模式(Builder)的说明 214

15.2.3 建造者模式(Builder)的实现范例 215

15.3 使用建造者模式(Builder)组装角色的各项功能 217

15.3.1 角色功能的组装 218

15.3.2 实现说明 219

15.3.3 使用建造者模式(Builder)的优点 226

15.3.4 角色建造者的执行流程 226

15.4 建造者模式(Builder)面对变化时 227

15.5 结论 228

第16章 游戏属性管理功能——享元模式(Flyweight) 229

16.1 游戏属性的管理 229

16.2 享元模式(Flyweight) 236

16.2.1 享元模式(Flyweight)的定义 236

16.2.2 享元模式(Flyweight)的说明 237

16.2.3 享元模式(Flyweight)的实现范例 238

16.3 使用享元模式(Flyweight)实现游戏 242

16.3.1 SceneState的实现 242

16.3.2 实现说明 245

16.3.3 使用享元模式(Flyweight)的优点 250

16.3.4 享元模式(Flyweight)的实现说明 250

16.4 享元模式(Flyweight)面对变化时 252

16.5 结论 252

第5篇 战争开始

第17章 Unity3D的界面设计——组合模式(Composite) 254

17.1 玩家界面设计 254

17.2 组合模式(Composite) 259

17.2.1 组合模式(Composite)的定义 259

17.2.2 组合模式(Composite)的说明 260

17.2.3 组合模式(Composite)的实现范例 261

17.2.4 分了两个子类但是要使用同一个操作界面 264

17.3 Unity3D游戏对象的分层式管理功能 265

17.3.1 游戏对象的分层管理 265

17.3.2 正确有效地获取UI的游戏对象 266

17.3.3 游戏用户界面的实现 267

17.3.4 兵营界面的实现 269

17.4 结论 274

第18章 兵营系统及兵营信息显示 276

18.1 兵营系统 276

18.2 兵营系统的组成 277

18.3 初始兵营系统 281

18.4 兵营信息的显示流程 287

第19章 兵营训练单位——命令模式(Command) 288

19.1 兵营界面上的命令 288

19.2 命令模式(Command) 291

19.2.1 命令模式(Command)的定义 291

19.2.2 命令模式(Command)的说明 294

19.2.3 命令模式(Command)的实现范例 294

19.3 使用命令模式(Command)实现兵营训练角色 297

19.3.1 训练命令的实现 297

19.3.2 实现说明 298

19.3.3 执行流程 302

19.3.4 实现命令模式(Command)时的注意事项 303

19.4 命令模式(Command)面对变化时 305

19.5 结论 306

第20章 关卡设计——责任链模式(Chain of Responsibility) 307

20.1 关卡设计 307

20.2 责任链模式(Chain of Responsibility) 312

20.2.1 责任链模式(Chain of Responsibility)的定义 312

20.2.2 责任链模式(Chain of Responsibility)的说明 314

20.2.3 责任链模式(Chain of Responsibility)的实现范例 314

20.3 使用责任链模式(Chain of Responsibility)实现关卡系统 317

20.3.1 关卡系统的设计 317

20.3.2 实现说明 318

20.3.3 使用责任链模式(Chain of Responsibility)的优点 329

20.3.4 实现责任链模式(Chain of Responsibility)时的注意事项 329

20.4 责任链模式(Chain of Responsibility)面对变化时 330

20.5 结论 332

第6篇 辅助系统

第21章 成就系统—观察者模式(Observer) 334

21.1 成就系统 334

21.2 观察者模式(Observer) 338

21.2.1 观察者模式(Observer)的定义 338

21.2.2 观察者模式(Observer)的说明 340

21.2.3 观察者模式(Observer)的实现范例 341

21.3 使用观察者模式(Observer)实现成就系统 344

21.3.1 成就系统的新架构 344

21.3.2 实现说明 346

21.3.3 使用观察者模式(Observer)的优点 358

21.3.4 实现观察者模式(Observer)时的注意事项 358

21.4 观察者模式(Observer)面对变化时 359

21.5 结论 361

第22章 存盘功能—备忘录模式(Memento) 362

22.1 存储成就记录 362

22.2 备忘录模式(Memento) 366

22.2.1 备忘录模式(Memento)的定义 366

22.2.2 备忘录模式(Memento)的说明 367

22.2.3 备忘录模式(Memento)的实现范例 367

22.3 使用备忘录模式(Memento)实现成就记录的保存 371

22.3.1 成就记录保存的功能设计 371

22.3.2 实现说明 371

22.3.3 使用备忘录模式(Memento)的优点 374

22.3.4 实现备忘录模式(Memento)的注意事项 374

22.4 备忘录模式(Memento)面对变化时 374

22.5 结论 375

第23章 角色信息查询—访问者模式(Visitor) 376

23.1 角色信息的提供 376

23.2 访问者模式(Visitor) 385

23.2.1 访问者模式(Visitor)的定义 386

23.2.2 访问者模式(Visitor)的说明 390

23.2.3 访问者模式(Visitor)的实现范例 392

23.3 使用访问者模式(Visitor)实现角色信息查询 397

23.3.1 角色信息查询的实现设计 397

23.3.2 实现说明 398

23.3.3 使用访问者模式(Visitor)的优点 405

23.3.4 实现访问者模式(Visitor)时的注意事项 405

23.4 访问者模式(Visitor)面对变化时 405

23.5 结论 408

第7篇 调整与优化

第24章 前缀字尾—装饰模式(Decorator) 410

24.1 前缀后缀系统 410

24.2 装饰模式(Decorator) 415

24.2.1 装饰模式(Decorator)的定义 415

24.2.2 装饰模式(Decorator)的说明 418

24.2.3 装饰模式(Decorator)的实现范例 419

24.3 使用装饰模式(Decorator)实现前缀后缀的功能 422

24.3.1 前缀后缀功能的架构设计 423

24.3.2 实现说明 423

24.3.3 使用装饰模式(

在线预览

第3章游戏场景的转换——状态模式(State)

3.1 游戏场景本书使用Unity3D游戏引擎作为开发工具,而Unity3D是使用场景(Scene)作为游戏运行时的环境。开始制作游戏时,开发者会将游戏需要的素材(3D模型、游戏对象)放到一个场景中,然后编写对应的程序代码,之后只要单击Play按钮,就可以开始运行游戏。除了Unity3D之外,笔者过去开发游戏时使用的游戏引擎(Game Engine)或开发框架(SDK、Framework),多数也都存在“场景”的概念,例如:? 早期Java Phone的J2ME开发SDK中使用的Canvas类;? Android的Java开发SDK中使用的Activity类;? iOS上2D游戏开发工具Cocos2D中使用的CCScene类。虽然各种工具不见得都使用场景(Scene)这个名词,但在实现上,一样可使用相同的方式来呈现。而上面所列的各个类,都可以被拿来作为游戏实现中“场景”转换的目标。3.1.1 场景的转换当游戏比较复杂时,通常会设计成多个场景,让玩家在几个场景之间转换,某一个场景可能是角色在一个大地图上行走,而另一个场景则是在地下洞穴探险。这样的设计方式其实很像是舞台剧的呈现方式,编剧们设计了一幕幕的场景让演员们在其间穿梭演出,而每幕之间的差异,可能是在布景摆设或参与演出角色的不同,但对于观众来说,同时间只会看到演员们在某一个场景中的演出。要怎样才能真正应用“场景”来开发游戏呢?读者可以回想一下,当我们打开游戏App或开始运行游戏软件时,会遇到什么样的画面:出现游戏Logo、播放游戏片头、加载游戏数据、出现游戏主画面、等待玩家登录游戏、进入游戏主画面,接下来玩家可能是在大地图上打怪或进入副本刷关卡……。游戏画面转换如图3-1所示。 图3-1 游戏画面转换就以上面的说明为例,我们可规划出下面数个场景,每个场景分别负责多项功能的执行,如图3-2所示。 图3-2 每个场景负责执行的游戏功能? 登录场景:负责游戏片头、加载游戏数据、出现游戏主画面、等待玩家登录游戏。? 主画面场景:负责进入游戏画面、玩家在主城/主画面中的操作、在地图上打怪打宝……? 战斗场景:负责与玩家组队之后进入副本关卡、挑战王怪……在游戏场景规划完成后,就可以利用“状态图”将各场景的关系连接起来,并且说明它们之间的转换条件以及状态转换的流程,如图3-3所示。 图3-3 各场景转换条件以及状态转换的“状态图”即便我们换了一个游戏类型来实现,一样可以使用相同的场景分类方式,将游戏功能进行归类,例如:卡牌游戏可按如下分类:? 登录场景:负责游戏片头、加载游戏数据、出现游戏主画面、等待玩家登录游戏。? 主画面场景:玩家抽卡片、合成卡牌、查看卡牌……? 战斗场景:挑战关卡、卡片对战……转珠游戏可按如下分类:? 登录场景:负责游戏片头、加载游戏数据、出现游戏主画面、等待玩家登录游戏。? 主画面场景:查看关卡进度、关卡信息、商城、抽转珠……? 战斗场景:挑战关卡、转珠对战……当然,如果是更复杂的单机版游戏或大型多人在线游戏(MMORPG),还可以再细分出多个场景来负责对应的游戏功能。切分场景的好处将游戏中不同的功能分类在不同的场景中来执行,除了可以将游戏功能执行时需要的环境明确分类之外,“重复使用”也是使用场景转换的好处之一。从上面几个例子中可以看出,“登录场景”几乎是每款游戏必备的场景之一。而一般在登录场景中,会实现游戏初始化功能或玩家登录游戏时需要执行的功能,例如:? 单机游戏:登录场景可以有加载游戏数据、让玩家选择存盘、进入游戏等步骤。? 在线游戏:登录场景包含了许多复杂的在线登录流程,比如使用第三方认证系统、使用玩家自定义账号、与服务器连接、数据验证……对于大多数的游戏开发公司来说,登录场景实现的功能,会希望通用于不同的游戏开发项目,使其保持流程的一致性。尤其对于在线游戏这种类型的项目而言,由于登录流程较为复杂,若能将各项目共同的部分(场景)独立出来,由专人负责开发维护并同步更新给各个项目,那么效率就能获得提升,也是比较安全的方式。在项目开发时,若是能重复使用这些已经设计良好的场景,将会减少许多开发时间。更多的优点将在后续章节中说明。本书范例场景的规划在本书范例中,《P级阵地》规划了3个场景,如图3-4所示。 图3-4 《P级阵地》规划的3个场景? 开始场景(StarScene):GameLoop游戏对象(GameObject)的所在,游戏启动及相关游戏设置的加载。? 主画面场景(MainMenuScene):显示游戏名称和“开始”按钮。? 战斗场景(BattleScene):游戏主要执行的场景。3.1.2 游戏场景可能的实现方式实现Unity3D的场景转换较为直接的方式如下:Listing 3-1 一般场景控制的写法public class SceneManager{ private string m_state = "开始"; // 改换场景 public void ChangeScene(string StateName) { m_state = StateName;

switch(m_state) { case "菜单": Application.LoadLevel(“MainMenuScene”); break; case "主场景": Application.LoadLevel(“GameScene”); break; } } // 更新 public void Update() { switch(m_state) { case "开始": //... break; case "菜单": //... break; case "主场景": //... break; } }}

上述的实现方式会有以下缺点:只要增加一个状态,则所有switch(m_state)的程序代码都需要增加对应的程序代码。与每一个状态有关的对象,都必须在SceneManager类中被保留,当这些对象被多个状态共享时,可能会产生混淆,不太容易识别是由哪个状态设置的,造成游戏程序调试上的困难。每一个状态可能使用不同的类对象,容易造成SceneManager类过度依赖其他类,让SceneManager类不容易移植到其他项目中。为了避免出现上述缺点,修正的目标会希望使用一个“场景类”来负责维护一个场景,让与此场景相关的程序代码和对象能整合在一起。这个负责维护的“场景类”,其主要工作如下:? 场景初始化;? 场景结束后,负责清除资源;? 定时更新游戏逻辑单元;? 转换到其他场景;? 其他与该场景有关的游戏实现。由于在范例程序中我们规划了3个场景,所以会产生对应的3个“场景类”,但如何让这3个“场景类”相互合作、彼此转换呢?我们可以使用GoF的状态模式(State)来解决这些问题。3.2 状态模式(State)状态模式(State),在多数的设计模式书籍中都会提及,它也是游戏程序设计中应用最频繁的一种模式。主要是因为“状态”经常被应用在游戏设计的许多环节中,包含AI人工智能状态、账号登录状态、角色状态……3.2.1 状态模式(State)的定义状态模式(State),在GoF中的解释是:“让一个对象的行为随着内部状态的改变而变化,而该对象也像是换了类一样”。如果将GoF对状态模式(State)的定义改以游戏的方式来解释,就会像下面这样:“当德鲁伊(对象)由人形变化为兽形状态(内部状态改变)时,他所施展的技能(对象的行为)也会有所变化,玩家此时就像是在操作另一个不同的角色(像是换了类)”。“德鲁伊“是一种经常出现在角色扮演游戏(RPG)中的角色名称。变化外形是他们常使用的能力,通过外形的变化,使德鲁伊具备了转换为其他形体的能力,而变化为“兽形”是比较常见的游戏设计。当玩家决定施展外形转换能力时,德鲁伊会进入“兽形状态”,这时候的德鲁伊会以“兽形”来表现其行为,包含移动和攻击施展的方式;当玩家决定转换回人形时,德鲁伊会复原为一般形态,继续与游戏世界互动。所以,变化外形的能力可以看成是德鲁伊的一种“内部状态的转换”。通过变化外形的结果,角色表现出另外一种行为模式,而这一切的转化过程都可以由德鲁伊的内部控制功能来完成,玩家不必理解这个转化过程。但无论怎么变化,玩家操作的角色都是德鲁伊,并不会因为他内部状态的转变而有所差异。当某个对象状态改变时,虽然它“表现的行为”会有所变化,但是对于客户端来说,并不会因为这样的变化,而改变对它的“操作方法”或“信息沟通”的方式。也就是说,这个对象与外界的对应方式不会有任何改变。但是,对象的内部确实是会通过“更换状态类对象”的方式来进行状态的转换。当状态对象更换到另一个类时,对象就会通过新的状态类,表现出它在这个状态下该有的行为。但这一切只会发生在对象内部,对客户端来说,不需要了解这些状态转换的过程及对应的方式。3.2.2 状态模式(State)的说明状态模式(State)的结构如图3-5所示。 图3-5 状态模式的结构图参与者的说明如下:? Context(状态拥有者)? 是一个具有“状态”属性的类,可以制定相关的接口,让外界能够得知状态的改变或通过操作让状态改变。? 有状态属性的类,例如:游戏角色有潜行、攻击、施法等状态;好友上线、脱机、忙碌等状态;GoF使用TCP联网为例,有已连接、等待连接、断线等状态。这些类中会有一个ConcreteState[X]子类的对象为其成员,用来代表当前的状态。? State(状态接口类):制定状态的接口,负责规范Context(状态拥有者)在特定状态下要表现的行为。? ConcreteState(具体状态类)? 继承自State(状态接口类)。? 实现Context(状态拥有者)在特定状态下该有的行为。例如,实现角色在潜行状态时该有的行动变缓、3D模型变半透明、不能被敌方角色察觉等行为。3.2.3 状态模式(State)的实现范例首先定义Context类:Listing 3-2 定义Context类(State.cs)public class Context{ State m_State = null;

public void Request(int Value) { m_State.Handle(Value); }

public void SetState(State theState ) { Debug.Log ("Context.SetState:" theState); m_State = theState; }}Context类中,拥有一个State属性用来代表当前的状态,外界可以通过Request方法,让Context类呈现当前状态下的行为。SetState方法可以指定Context类当前的状态,而State状态接口类则用来定义每一个状态该有的行为:Listing 3-3 State类(State.cs)public abstract class State{ protected Context m_Context = null; public State(Context theContext) { m_Context = theContext; } public abstract void Handle(int Value);}在产生State类对象时,可以传入Context类对象,并将其指定给State的类成员m_Context,让State类在后续的操作中,可以获取Context对象的信息或操作Context对象。然后定义Handle抽象方法,让继承的子类可以重新定义该方法,来呈现各自不同的状态行为。定义3个继承自State类的子类:Listing 3-4 定义3个状态(State.cs)// 状态Apublic class ConcreteStateA : State{ public ConcreteStateA(Context theContext):base(theContext) {}

public override void Handle (int Value) { Debug.Log ("ConcreteStateA.Handle"); if( Value > 10) m_Context.SetState( new ConcreteStateB(m_Context)); }}

// 状态Bpublic class ConcreteStateB : State{ public ConcreteStateB(Context theContext):base(theContext) {}

public override void Handle (int Value) { Debug.Log ("ConcreteStateB.Handle"); if( Value > 20) m_Context.SetState( new ConcreteStateC(m_Context)); } }

// 状态Cpublic class ConcreteStateC : State{ public ConcreteStateC(Context theContext):base(theContext) {} public override void Handle (int Value) { Debug.Log ("ConcreteStateC.Handle"); if( Value > 30) m_Context.SetState( new ConcreteStateA(m_Context)); } }上述3个子类,都要重新定义父类State的Handle抽象方法,用来表示在各自状态下的行为。在范例中,我们先让它们各自显示不同的信息(代表当前的状态行为),再按照本身状态的行为定义来判断是否要通知Context对象转换到另一个状态。Context类中提供了一个SetState方法,让外界能够设置Context对象当前的状态,而所谓的“外界”,也可以是由另一个State状态来调用。所以实现上,状态的转换可以有下列两种方式:? 交由Context类本身,按条件在各状态之间转换;? 产生Context类对象时,马上指定初始状态给Context对象,而在后续执行过程中的状态转换则交由State对象负责,Context对象不再介入。笔者在实现时,大部分情况下会选择第2种方式,原因在于:状态对象本身比较清楚“在什么条件下,可以让Context对象转移到另一个State状态”。所以在每个ConcreteState类的程序代码中,可以看到“状态转换条件”的判断,以及设置哪一个ConcreteState对象成为新的状态。每个ConcreteState状态都可以保持自己的属性值,作为状态转换或展现状态行为的依据,不会与其他的ConcreteState状态混用,在维护时比较容易理解。因为判断条件及状态属性都被转换到ConcreteState类中,故而可缩减Context类的大小。4个类定义好之后,我们可以通过测试范例来看看客户端程序会怎样利用这个设计:Listing 3-5 State的测试范例(StateTest.cs) void UnitTest() { Context theContext = new Context(); theContext.SetState( new ConcreteStatA(theContext ));

theContext.Request( 5 ); theContext.Request( 15 ); theContext.Request( 25 ); theContext.Request( 35 ); }首先产生Context对象theContext,并立即设置为ConcreteStateA状态;然后调用Context类的Request方法,并传入作为“状态转换判断”用途的参数,让当前状态(ConcreteStateA)判断是否要转移到ConcreteStateB;调用几次Request方法,并传入不同的参数。从输出的信息中可以看到,Context对象的状态由ConcreteStateA按序转换到ConcreteStateB、 ConcreteStateC状态,回到ConcreteStateA状态。 State测试范例产生的信息 Context.SetState:DesignPattern_State.ConcreteStateA ConcreteStateA.Handle ConcreteStateA.Handle Context.SetState:DesignPattern_State.ConcreteStateB Context.SetState:DesignPattern_State.ConcreteStateC Context.SetState:DesignPattern_State.ConcreteStateA3.3 使用状态模式(State)实现游戏场景的转换在Unity3D的环境中,游戏只会在一个场景中运行,所以我们可以让每个场景都由一个“场景类”来负责维护。此时,如果将场景类当成“状态”来比喻的话,那么就可以利用状态模式(State)的转换原理,来完成场景转换的功能。由于每个场景所负责执行的功能不同,通过状态模式(State)的状态转移,除了可以实现游戏内部功能的转换外,对于客户端来说,也不必根据不同的游戏状态来编写不同的程代码,同时也减少了外界对于不同游戏状态的依赖性。而原本的Unity3D场景转换判断功能,可以在各自的场景类中完成,并且状态模式(State)同时间也只会让一个状态存在(同时间只会有一个状态在运行),因此可以满足Unity3D执行时只能有一个场景(状态)存在的要求。3.3.1 SceneState的实现《P级阵地》的场景分成3个:开始场景(StarScene)、主画面场景(MainM

网友评论(不代表本站观点)

免责声明

更多出版社