现有一个任务在MMORPG(Game)中控制玩家(Player)的状态,现不从总体上去考虑使用状态模式。我一步步引出。我先写在同一个方法里面,因为传统的做法都是这样的。
首先Player的状态初始化为站立状态(stand_state) , 如果玩家按“J”键的话,玩家开始跳跃:
public void Palyer_Handler( string type = "J" ) { switch(type) { case "J": Console.WriteLine("Player Jump"); //to do jumping code break; } }
这段代码肯定是有问题的 , 因为player还在空中 , 如果再输入“J”, Player会再Jump一次,我们释放type为~(此动作做完后发送)所有动作都用这一套路,这显然是不正确的。那好我们队上述代码进行一次修改:
private bool is_jumping = false; public void Palyer_Handler( string type = "J" ) { switch(type) { case "J": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Jump"); //to do jumping code } break; default: if( type == "~" ) { is_jumping = false; } break; } }
上述代码的意思很简单 : 当Player不是出于jumping状态时 , 如果键入“J”则让其jump。否则不做处理。
好了, Player需要扩展动作 :走(walk) , 现定义键入“W”(按着不松 ,释放W自动返回站立stand状态),玩家walk,代码如下:
private bool is_jumping = false; public void Palyer_Handler( string type = "J" ) { switch(type) { case "J": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Jump"); //to do jumping code is_jumping = false; } break; case "W": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Walk"); //to do walking code } break; default: if( type == "~" ) { is_jumping = false; } break; } }
如上述code , 加了一个walk状态 , 那么is_jumping将要做很多的状态处理。此时不是特别麻烦
我们加一个跑的状态(run), type== “R”进入此状态
private bool is_jumping = false; public void Palyer_Handler( string type = "J" ) { switch(type) { case "J": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Jump"); //to do jumping code is_jumping = false; } break; case "W": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Walk"); //to do walking code } break; case "R": Console.WriteLine("Player Run"); //to do runing code break; default: if( type == "~" ) { is_jumping = false; } break; } }
我这样写肯定是错的
jump状态的时候 , 肯定是不能进入run状态 , 但是walk状态的时候绝对可以进入run状态。有人的想法是:
private bool is_jumping = false; public void Palyer_Handler( string type = "J" ) { switch(type) { case "J": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Jump"); //to do jumping code is_jumping = false; } break; case "W": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Walk"); //to do walking code } break; case "R": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Run"); //to do runing code } break; default: if( type == "~" ) { is_jumping = false; } break; } }
当walk状态结束回到stand状态后 , 可以进入run状态。ok ??? 好吧 , 也许策划伙伴们足够宽容,允许你先从走到 站一会 再跑 。
我们再加一个动作俯冲(subduction)以快速结束jump(可能加一个道具啊), 只有在jumping的状态中,才能执行subduction。type==“S”俯冲
private bool is_jumping = false; public void Palyer_Handler( string type = "J" ) { switch(type) { case "J": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Jump"); //to do jumping code } break; case "W": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Walk"); //to do walking code } break; case "R": if (!is_jumping) { is_jumping = true; Console.WriteLine("Player Run"); //to do runing code } break; case "S": if(is_jumping) { is_jumping = false; Console.WriteLine("Player Subduction"); // to do subduction code } break; default: if( type == "~" ) { is_jumping = false; } break; } }
至此代码彻底乱了 , 没法再坐下去了 , 如果再加一个卧倒 , 捡道具的动作,估计开发人员会哭。我们可以把这些状态画出来:
我们拥有一组状态 , 这些状态之间可以互相切换 。同一时刻只能处理一种状态的行为。这样的情况我们可以使用状态模式。
现在用C#实现上图结构的状态模式:
一 : 定义一个接口IState , 上述5个状态都都实现这一接口
namespace StatePattern_Demo { public interface IState { ////// 处理行为 /// /// void Handle(Context context); ////// 切换状态 /// /// /// 是否自动执行切换后动作 ///是否切换成功 Boolean SwitchState(IState state , Boolean is_play = false); } }
跑状态继承IState
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace StatePattern_Demo { ////// 跑 /// public sealed class Run_State : IState { private Context _context = null; public void Handle(Context context) { this._context = context; Console.WriteLine("正在处理Run事件。。。。。。。"); //用于模拟run的结束时间 System.Timers.Timer mytimer = new System.Timers.Timer(5000);//实例化Timer类 mytimer.Elapsed += new System.Timers.ElapsedEventHandler(aotoSwicth);//定义定时到达时执行的事件; mytimer.AutoReset = false;//设置是否自动重启,即自动执行多次; mytimer.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件; } private void aotoSwicth(object source, System.Timers.ElapsedEventArgs e) { if (this._context.State != this) return;//如果state已经改变不做任何处理 //回到站立状态 this.SwitchState(new Stand_State(), true); } public Boolean SwitchState(IState state, Boolean is_play = false) { Type target_state_type = state.GetType(); if (target_state_type == typeof(Walk_State) || target_state_type == typeof(Stand_State)) { //在"跑"状态可以切换至"走"状态或者“站立”状态 this._context.State = state; if( is_play ) { this._context.Play();//立即执行 } return true; } return false; } } }
走状态继承IState:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace StatePattern_Demo { public sealed class Walk_State : IState { private Context _context = null; public void Handle(Context context) { this._context = context; Console.WriteLine("正在处理Walk事件。。。。。。。"); System.Timers.Timer mytimer = new System.Timers.Timer(5000);//实例化Timer类 mytimer.Elapsed+=new System.Timers.ElapsedEventHandler(aotoSwicth);//定义定时到达时执行的事件; mytimer.AutoReset = false;//设置是否自动重启,即自动执行多次; mytimer.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件; } private void aotoSwicth(object source, System.Timers.ElapsedEventArgs e) { if (this._context.State != this) return;//如果state已经改变不做任何处理 //回到站立状态 this.SwitchState(new Stand_State(), true); } public Boolean SwitchState(IState state, Boolean is_play = false) { Type target_state_type = state.GetType(); if (target_state_type == typeof(Run_State) || target_state_type == typeof(Stand_State)) { //在“走”状态可切换至“跑”状态或者站立状态 this._context.State = state; if( is_play ) { this._context.Play(); } return true; } return false; } } }
站立状态继承IState:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace StatePattern_Demo { ////// 站立 /// public sealed class Stand_State : IState { private Context _context = null; public void Handle(Context context) { this._context = context; Console.WriteLine("持续Standing。。。。。。"); } public Boolean SwitchState(IState state, Boolean is_play = false) { Type target_state_type = state.GetType(); if( target_state_type == typeof(Subduction_State) ) { return false;//在站立状态不能俯冲 } else { this._context.State = state; if (is_play) { this._context.Play(); } return true; } return false; } } }
跳跃状态继承IState:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace StatePattern_Demo { ////// 跳跃 /// public sealed class Jump_State : IState { private Context _context = null; public void Handle(Context context) { this._context = context; Console.WriteLine("正在处理Jump事件。。。。。。。"); System.Timers.Timer mytimer = new System.Timers.Timer(5000);//实例化Timer类 mytimer.Elapsed += new System.Timers.ElapsedEventHandler(aotoSwicth);//定义定时到达时执行的事件; mytimer.AutoReset = false;//设置是否自动重启,即自动执行多次; mytimer.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件; } private void aotoSwicth(object source, System.Timers.ElapsedEventArgs e) { if (this._context.State != this) return;//如果state已经改变不做任何处理 //回到站立状态 this.SwitchState(new Stand_State(), true); } public Boolean SwitchState(IState state , Boolean is_play = false) { Type target_state_type = state.GetType(); if (target_state_type == typeof(Subduction_State) || target_state_type == typeof(Stand_State)) { //在“跳跃”状态中可以执行“俯冲”动作 或者 “站立”动作 this._context.State = state; if( is_play ) { this._context.Play(); } return true; } return false; } } }
俯冲状态继承IState:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace StatePattern_Demo { ////// 俯冲 /// public sealed class Subduction_State : IState { private Context _context = null; private Boolean is_play_over = false; public void Handle(Context context) { this._context = context; is_play_over = false; Console.WriteLine("正在处理Subduction事件。。。。。。。"); System.Timers.Timer mytimer = new System.Timers.Timer(5000);//实例化Timer类 mytimer.Elapsed += new System.Timers.ElapsedEventHandler(aotoSwicth);//定义定时到达时执行的事件; mytimer.AutoReset = false;//设置是否自动重启,即自动执行多次; mytimer.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件; } private void aotoSwicth(object source, System.Timers.ElapsedEventArgs e) { if (this._context.State != this) return;//如果state已经改变不做任何处理 //回到站立状态 is_play_over = true;//俯冲动作已经完成 this.SwitchState(new Stand_State(), true); } public Boolean SwitchState(IState state , Boolean is_play = false) { if ( is_play && is_play_over ) { Type target_state_type = state.GetType(); if( target_state_type == typeof(Stand_State) ) { this._context.State = state; this._context.Play();//立即执行 return true; } } return false; } } }
二 : 定义一个Context类来保存当前的IState
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace StatePattern_Demo { public sealed class Context { private IState state; ////// 定义Context的初始状态 /// /// public Context(IState state) { this.state = state; } ////// 可读写的状态属性,用于读取和设置新状态 /// public IState State { get { return state; } set { state = value; } } ////// 播放状态的动作 /// public void Play() { state.Handle(this); } } }
测试一下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace StatePattern_Demo { class Program { static void Main(string[] args) { Context cur_context = new Context(new Stand_State()); cur_context.Play(); if( cur_context.State.SwitchState(new Walk_State()) ) { cur_context.Play(); } Thread.Sleep(1000); cur_context.State.SwitchState(new Run_State(), true); if (!cur_context.State.SwitchState(new Subduction_State())) { Console.WriteLine("Run 状态无法俯冲"); } Thread.Sleep(5100); if( cur_context.State.SwitchState(new Jump_State()) ) { cur_context.Play(); } cur_context.State.SwitchState(new Subduction_State(), true); Console.ReadKey(); } } }
结果:
好了 , 完全符合我们的期待 , 把状态的控制交代到一个个类里面 , 把总体复杂的状态判断分解到相对简单的单独的一个一个的判断。