此文章原文来源于《设计模式与完美游戏开发》(蔡升达著),笔者只是在学习过程中受益颇多,从而进行了总结,有兴趣的读者可以去阅读原书。

1.场景的转换

当游戏比较复杂的时候,通常会设计多个场景,让玩家在几个场景之间切换,某一个场景可能是角色在一个大地图上行走,另一个场景则可能是在洞穴探险,这样的设计方式类似于舞台剧,一幕幕的呈现,但对于观众来说,同一时间只能看到演员在某一个场景中的演出。

当我们打开一款游戏的时候,可能会遇到以下场景:出现游戏Logo、播放游戏片头、加载游戏数据、出现游戏主画面、等待玩家登录游戏、进入游戏主画面、进入副本打怪······。

我们可以利用“状态图”将各场景的关系连接起来,并且说明他们之间的转换条件以及状态转换的流程,如图所示:

切分场景的好处:将游戏中不同的功能分类在不同的场景中来执行,除了可以将游戏功能执行时需要的环境明确分类之外,"重复使用"也是使用场景转换的好处之一。

范例场景的规划:

2.游戏场景可能的切换方式

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类中被保留,当这些对象被多个状态共享时,可能会产生混淆,不太容易识别是由哪个状态设置的,造成游戏程序调试上的困难。

每一个状态可能使用不同的类对象,容易造成StageManager类过度依赖其他类,让SceneManager类不容易移植到其他项目中。

每一个状态可能使用不同的类对象,容易造成StageManager类过度依赖其他类,让SceneManager类不容易移植到其他项目中。

为了避免出现上述缺点,修正的目标会希望使用一个"场景类"来负责维护一个场景,让与此场景相关的程序代码和对象能整合在一起.这个负责维护的"场景类",其主要工作如下:

场景初始化。

场景结束后,负责清除资源。

定时更新游戏逻辑单元。

转换到其他场景。

其他与该场景有关的游戏实现。

我们可以使用GoF的状态模式(State)来解决这些问题。

3.状态模式(State)的定义

状态模式(State), 在GoF中的解释是:“让一个对象的行为随着内部状态的改变而变化,而该对象也像是换了类一样”。

可以将GoF对状态模式(State)的定义用游戏的方式来解释:

“当德鲁伊(对象)由人性改变为兽性状态(内部改变)时,他所施展的技能(对象的行为)也会有所变化,玩家此时就像是在操作另外一个不同的角色(像是换了类)”。

“德鲁伊”是一种经常出现在RPG游戏中的角色,变换外形是他们的能力。当玩家决定施展外形转换能力时,德鲁伊会进入“兽形状态”,这时德鲁伊会以“兽形”来表现其行为,包含移动和攻击的方式;当玩家决定转换为人形时,德鲁伊会恢复为一般形态。

所以,变换外形的能力可以看成是一种“内部状态的转换”。通过变化外形,角色展现出另外一种行为模式,而这一切的转化过程都是由德鲁伊内部控制功能来完成,玩家不需要去理解这个转化过程。但无论怎么变化,玩家操作的角色都是德鲁伊,并不会因为他内部状态的转变而有所差异。

当某个对象改变状态时,虽然它“表现的行为”有所变化,但是对于客户端来说,并不会因为这样的变化,而改变对他的“操作方法”或“信息沟通”的方式。也就是说,这个对象与外界的对应方式不会有任何改变。但是,对象的内部确实是会通过“更换状态类对象”的方式来进行状态的装换。当状态对象更换到另一个类时,对象就会通过新的状态类,表现出它在这个状态下该有的行为。但这一切只会发生在对象内部,对客户端来说,完全不需要了解这些状态的转换过程以及对应的方式。

4.状态模式(State)的说明

参与者的说明如下:

Context(状态拥有者):

是一个具有"状态"属性的类, 可以制定相关的接口, 让外界能够得知状态的改变或通过操作让状态改变。

有状态属性的类, 例如: 游戏角色有潜行, 攻击, 施法等状态; 好友上线, 脱机, 忙碌等状态; GoF使用TCP联网为例, 有已连接, 等待连接, 断线等状态. 这些类中会有一个ConcreteState[X]子类的对象为其成员, 用来代表当前的状态。

State(状态接口类) :

制定状态的接口, 负责规范Context(状态拥有者)在特定状态下要表现的行为。

ConcreteState(具体状态的类):

继承自State(状态接口类)。

实现Context(状态拥有者)在特定状态下该有行为.例如, 实现角色在潜行状态时该有的行动变缓, 3D模型变半透明, 不能被敌方角色察觉等行为。

5.状态模式(State)的实现范例

首先定义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状态接口类则用来定义每一个状态该有的行为:

定义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类的子类(State.cs):

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

上述三个子类,都要重新定义父类State的Handle抽象方法,用来表示各自状态下的行为。在范例中,我们先让他们各自显示不同的信息(代表当前的状态行为),再按照本身状态的行为定义来判断是否要通过Context对象转换到另一个状态。

State的测试范例(StateTest.cs):

void UnitTest() {Context theContext = new Context();theContext.SetState(new ConcreteStateA(theContext));theContext.Request(5);theContext.Request(15);theContext.Request(25);theContext.Request(35);
}

首先产生Context对象theContext ,并立刻设置为ConcreteStateA状态,然后调用Context类的Request方法,并传入作为“状态转换判断”用途的参数,让当前状态(ConcreteStateA)判断是否转换到ConcreteStateB,最后多调用几次Request方法,并传入不同的参数。

State测试范例的结果

Context.SetState:DesignPattern_State.ConcreteStateA
ConcreteStateA.Handle
ConcreteStateA.Handle
Context.SetState:DesignPattern_State.ConcreteStateB
Context.SetState:DesignPattern_State.ConcreteStateC
Context.SetState:DesignPattern_State.ConcreteStateA

6.使用状态模式(State)的优点

使用状态模式(State)来实现游戏场景转换,有下列优点:

减少错误的发生并降低维护难度

不再使用switch(m_state)来判断当前的状态,这样可以减少新增游戏状态时,因未能检查到所有switch(m_state)程序代码而造成的错误。

状态执行环境单一化

与每一个状态有关的对象及操作都被实现在一个场景状态类下,对程序设计师来说,这样可以清楚地了解每一个状态执行时所需要的对象及配合的类。

项目之间可以共享场景

7.状态模式(State)面对变化时

随着项目开发进度进入中后期,游戏企划可能会提出新的系统功能来增加游戏内容.这些提案可能是增加小游戏关卡,提供查看角色信息图鉴,玩家排行等功能.当程序人员在分析这些新增的系统需求后,如果觉得无法在现有的场景(Scene)下实现,就必须使用新的场景来完成.而在现有的架构下,程序人员只需要完成下列几项工作:

  在Unity3D编辑模式下新增场景。

  加入一个新的场景状态类对应到新的场景,并在其中实现相关功能。

  决定要从哪个现有场景转换到新的场景。

  决定新的场景结束后要转换到哪一个场景。

上述流程,就程序代码的修改而言,只会新增一个程序文件(.cs)用来实现新的场景状态类,并修改一个现有的游戏状态,让游戏能按照需求转换到新的场景状态.除此之外,不需要修改其他任何的程序代码。

8.结论

在本章中,我们利用状态模式(State)实现了游戏场景的切换,这种做法并非全然都是优点,但与传统的switch(state_code)相比,已经算是更好的设计,使用状态模式(State)可以清楚地了解某个场景状态执行时所需要配合使用的类对象,并且减少因新增状态而需要大量修改现有程序代码的维护成本。

状态模式(State)的其他应用方式:

角色AI: 使用状态模式(State)来控制角色在不同状态下的AI行为。

游戏服务器连线状态: 网络游戏的客户端,需要处理与游戏服务器的连线状态,一般包含开始连线,连线中,断线等状态,而在不同的状态下,会有不同的封包信息处理方式,需要分别实现。

关卡进行状态: 如果是通关型游戏,进入关卡时通常会分成不同的阶段,包含加载数据,显示关卡信息,倒数通知开始,关卡进行,关卡结束和分数计算,这些不同的阶段可以使用不同的状态类来负责实现。

C#常用设计模式(Unity)——游戏场景的转换——状态模式(State)相关推荐

  1. Java设计模式及应用场景之《状态模式》

    文章目录 一.状态模式定义 二.状态模式的结构和说明 三.状态模式示例 四.状态模式的优缺点 五.状态模式的应用场景及案例 一.状态模式定义 Allow an object to alter its ...

  2. 【转】设计模式 ( 十七) 状态模式State(对象行为型)

    设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...

  3. 用设计模式去掉没必要的状态变量 —— 状态模式

    这是设计模式系列的第四篇,系列文章目录如下: 一句话总结殊途同归的设计模式:工厂模式=?策略模式=?模版方法模式 使用组合的设计模式 -- 美颜相机中的装饰者模式 使用组合的设计模式 -- 追女孩要用 ...

  4. 项目实训(三)unity游戏场景的搭建

    文章目录 前言 一.组件的介绍 二.场景搭建 总结 前言 ` unity场景的搭建 一.组件的介绍 首先对scene场景中的一些组件介绍: 常用快捷键 1.按下鼠标滚轮拖动场景(或者拖动小手),滑动滚 ...

  5. 天上的街市Unity游戏场景制作案例(一)

    目录 Downtown Street of Heaven 场景 Unity实现过程 新建项目 场景布置 场景制作参考 移动媒介的添加 Downtown Street of Heaven 场景 Unit ...

  6. unity游戏场景设计

    下载 Fantasy Skybox FREE,构建自己的游戏场景,并为场景设计地 形(含树木和草地等元素) 从asset store中下载素材 打开unity-window-asset store,搜 ...

  7. 趣谈设计模式 | 状态模式(State):如何实现游戏中的状态切换?

    文章目录 案例:马里奥积分竞赛 有限状态机 分支逻辑法 查表法 状态模式 状态模式与策略模式 总结 完整代码与文档 案例:马里奥积分竞赛 喜欢马里奥的小伙伴们都应该知道,前不久马里奥为了庆祝35周年, ...

  8. Java设计模式及应用场景之《命令模式》

    文章目录 一.命令模式定义 二.命令模式的结构和说明 三.命令模式示例 四.命令模式扩展 -- 宏命令示例 五.命令模式扩展 -- 可撤销和恢复操作示例 1.反操作式(补偿式) 2.存储恢复式 六.命 ...

  9. C++设计模式之状态模式(state)(行为型)

    一 定义 状态模式:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类. 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况.把不同状态的操作分散到不同的状态对象 ...

最新文章

  1. html读取csv文件,javaScript读取.csv文件或.xlsx文件
  2. 网络时间协议 --- 网络对时程序
  3. java word打印_如何通过Java打印Word文档
  4. 进程间通信——DLL共享节
  5. 【VC6.0】getline需要输入2次回车才会结束的BUG修复方法
  6. P5904-[POI2014]HOT-Hotels加强版【长链剖分,dp】
  7. Apache2 部署flask项目
  8. http状态码批量查询工具V2.0
  9. matlab图像基础处理小记
  10. set列表对象去重_Redis中hash、set、zset有多牛?从底层告诉你数据结构原理
  11. [C++ primer]运行时类型识别(RTTI)
  12. oracle 手动执行作业,ORACLE 作业操作
  13. RS485无线通讯模块工作原理详解
  14. Alexa技能开发从创建到发布
  15. cpu测试软件一等奖,图吧里捡垃圾的老哥们,制作出了一款超牛 X 的电脑工具箱!...
  16. 最有效率地戒掉晚睡强迫症(熬夜强迫症、假象失眠症等等)
  17. python开发网页视频播放器_python实现媒体播放器功能
  18. 可以EI检索的国际会议论文,对学术研究和资历积累有用吗?
  19. 清华大学用6个无线传感器搞定全身动作捕捉,可跑可跳可打滚
  20. matlab 三维线性插值,MATLAB三维插值与拟合

热门文章

  1. 开源erp软件odoo在线开发环境部署实录
  2. C# error MSB3171: 生成清单时出现问题。未能加载文件或程序集,Windows.winmd
  3. 求职信英语计算机作文,求职信的英语作文(通用5篇)
  4. 如何实现一个最大的正方形
  5. NOI网站OpenJudge1.4.1判断数正负
  6. KiCad #学习笔记02#|国产在线电路设计软件立创EDA (Arduino UNO 单片机最小系统电路原理图)
  7. 如何在PDF文件中编辑文本的大小?
  8. 极速PDF编辑器提示缺少字体解决方案
  9. 精通linux内核年薪,最新IT人才薪酬体系一览表
  10. 河北欧格教育:商家开店铺怎么运营