角色扮演游戏和战争策略游戏的确不太一样,在战争游戏中,每个单元要不就正常运行,要不就是被摧毁;而在角色扮演游戏中,由于角色们有很多必杀技,在被这些必杀技击中后,有会出现的不同的影响,如攻击力下降、不能行动等等。
在这一章里,我们将要实现这个功能。
刚接到这个任务时,你可能会考虑根据不同的必杀技的参数来修改每个单位的各种动作的相关方法,比如:
l 如果被冰封必杀技击中,则角色的状态就是锁定,这时角色不能移动,也不能攻击其他目标;
l 如果被水淹七军必杀技击中,则攻击力和速度下降;
l ……
这样,我们必需创建这些状态的常量:
Public Const StateNormal = 0 Public Const StateLocked = 1 Public Const StateSlowly = 2 |
或者,我们使用枚举来表示这些状态:
Public Enum EState Normal = 0 Locked = 1 Slowly = 2 End Enum |
然后,我们需要在CUnit类中添加一个值作为角色状态。
最后,我们需要修改每个角色的Move()方法和Attack()方法,比如,当关羽被冰封后,他就不能移动,我们必须在Move()方法内针对三种状态进行编程,代码可能会是这个样子:
''关羽 Public Class CGuanYu Inherits CUnitDecorator Protected myState As EState Public Sub New() myName = "关羽" myLife = 200 myPower = 100 UnitId = EUnit.GuanYu End Sub Public Sub Move(ByVal x As Integer, ByVal y As Integer) Select Case myState Case EState.Normal Console.Write("{0}移动到位置({0}, {1})",myName,x,y) Case EState.Locked Console.Write("{0}不能移动",myName) Case EState.Slowly Console.Write("{0}慢速移动到位置({0}, {1})",myName,x,y) End Select End Sub … End Class |
然后,针对Attack()方法也应用做出相应的修改;但这样一来,每一个角色类都需要做出修改;原有的代码将被修改的面目全非。你真的想这样吗?
请注意,设计模式的基本概念之一就是“多扩展、少修改”,我们再想想别的办法吧。
在前面是不是多次提到了“状态”两个字,好像有个模式就是状态模式,我们何不在这里试试呢?好的,那我们就开始……
使用状态模式
状态模式(State Pattern)的定义如下:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
好吧,一切如故,这也是一种模式,J。我们首先创建状态接口IState,代码如下:
''游戏状态接口 Public Interface IState Property Description As String Sub Move(ByVal x As Integer, ByVal y As Integer) Sub Attack(ByVal x As Integer, ByVal y As Integer) '还可有更多的动作影响 End Interface |
(项目:StatePatternDemo 文件:Interfaces.vb)
然后,就是每一种可能的角色状态类,本例为我们依然使用三种状态:正常状态(CNormalState)、锁定状态(CLockedState)和慢速状态(CSlowlyState)。下面是正常状态类的定义:
''正常状态 Public Class CNormalState Implements IState Protected myUnit As IUnit '状态影响的角色 Protected myDescription As String = "角色状态正常" '状态描述 Public Sub New(ByVal unit As IUnit) myUnit=unit End Sub '状态描述 Public Property Description As String Implements IState.Description Get Return myDescription End Get Set(ByVal value As String) myDescription = value End Set End Property '攻击 Public Sub Attack(ByVal x As Integer, ByVal y As Integer) _ Implements IState.Attack Console.WriteLine("<{0}>正常攻击位置({1},{2}) 攻击力:{3}", _ myUnit.Name, x, y, myUnit.Power) End Sub '移动 Public Sub Move(ByVal x As Integer, ByVal y As Integer) Implements IState.Move Console.WriteLine("<{0}>正常移动到({1},{2})", myUnit.Name, x, y) End Sub End Class |
(项目:StatePatternDemo 文件:States.vb)
下面是锁定状态类(CLockedState)和慢速状态类(CSlowlyState)的代码:
''锁定状态 Public Class CLockedState Implements IState Protected myUnit As IUnit '状态影响的角色 Protected myDescription As String = "角色被锁定" '状态描述 Public Sub New(ByVal unit As IUnit) myUnit=unit End Sub '状态描述 Public Property Description As String Implements IState.Description Get Return myDescription End Get Set(ByVal value As String) myDescription = value End Set End Property '攻击 Public Sub Attack(ByVal x As Integer, ByVal y As Integer) _ Implements IState.Attack Console.WriteLine("<{0}>无法攻击目标", myUnit.Name) End Sub '移动 Public Sub Move(ByVal x As Integer, ByVal y As Integer) Implements IState.Move Console.WriteLine("<{0}>无法移动", myUnit.Name) End Sub End Class
''慢速状态 Public Class CSlowlyState Implements IState Protected myUnit As IUnit '状态影响的角色 Protected myDescription As String = "角色速度减缓" '状态描述 Public Sub New(ByVal unit As IUnit) myUnit=unit End Sub '状态描述 Public Property Description As String Implements IState.Description Get Return myDescription End Get Set(ByVal value As String) myDescription = value End Set End Property '攻击 Public Sub Attack(ByVal x As Integer, ByVal y As Integer) _ Implements IState.Attack Console.WriteLine("<{0}>减缓攻击位置({1},{2}) 攻击力:{3}", _ myUnit.Name, x, y, myUnit.Power \ 2) End Sub '移动 Public Sub Move(ByVal x As Integer, ByVal y As Integer) Implements IState.Move Console.WriteLine("<{0}>慢速移动到({1},{2})", myUnit.Name, x, y) End Sub End Class |
(项目:StatePatternDemo 文件:States.vb)
现在,我们需要将各种状态组合到角色单位中,首先修改单位接口IUnit,我们只需要添加一个IState接口类型的属性就可以了,代码如下:
''游戏角色接口 Public Interface IUnit Property Name As String '名称 Property UnitId As EUnit '游戏类型ID Property Life As Integer '生命值 Property Power As Integer '攻击力 Property State As IState '角色状态 Sub Move(ByVal x As Integer, ByVal y As Integer) Sub Attack(ByVal x As Integer, ByVal y As Integer) End Interface |
(项目:StatePatternDemo 文件:Interfaces.vb)
然后,我们修改CUnit类,添加State属性的实现,以及修改单位移动(Move)和攻击(Attack)的方法。代码如下:
''游戏单位基类 Public MustInherit Class CUnit Implements IUnit ... Protected myState As IState =New CNormalState(Me) ... '攻击 Public Sub Attack(ByVal x As Integer, ByVal y As Integer) _ Implements IUnit.Attack myState.Attack(x, y) End Sub '移动 Public Sub Move(ByVal x As Integer, ByVal y As Integer) _ Implements IUnit.Move myState.Move(x, y) End Sub '状态 Public Property State As IState Implements IUnit.State Get Return myState End Get Set(ByVal value As IState) myState = value End Set End Property End Class |
(项目:StatePatternDemo 文件:CUnit.vb)
在这里,我们只给出了CUnit类需要修改和添加的代码,其它代码与上一章的CUnit类是一致的。此外,别忘了创建更多的人物和武器装备,下面是吕布和方天画戟的代码:
''吕布 Public Class CLvBu Inherits CUnitDecorator Public Sub New() myName = "吕布" myLife = 260 myPower = 110 UnitId = EUnit.LvBu End Sub End Class ''方天画戟 Public Class CWeapon4 Inherits CUnit Public Sub New() myName = "方天画戟" myLife = 0 myPower = 60 UnitId = EUnit.Weapon4 End Sub End Class |
(项目:StatePatternDemo 文件:Units.vb)
好的,代码修改完毕,现在测试场景“三英战吕布”。代码如下:
Module Module1 Sub Main() Console.WriteLine(">>>>出道时的吕布") Dim LvBu As New CLvBu LvBu.ShowInfo() Console.WriteLine() ' Console.WriteLine(">>>>得意时的吕布") LvBu.AddUnit(New CWeapon4) LvBu.AddUnit(New CEquipper1) LvBu.ShowInfo() Console.WriteLine() ' Console.WriteLine(">>>>场景:三英战吕布") Console.WriteLine(">>>>被关羽‘水淹七军’必杀技击中") LvBu.State = New CSlowlyState(LvBu) LvBu.Move(100, 100) LvBu.Attack(105, 105) Console.WriteLine() ' Console.WriteLine(">>>>被刘备‘冰封’必杀技击中") LvBu.State = New CLockedState(LvBu) LvBu.Move(200, 200) LvBu.Attack(205, 205) ' Console.ReadLine() End Sub End Module |
(项目:StatePatternDemo 文件:Module1.vb)
本场景运行结果如下图:
也许你已经注意到了,我们没有显示角色状态描述的代码,不过,你可以在需要的地主显示角色状态描述;比如,在上例的代码中显示吕布的状态描述只需要使用如下的代码就可以了:
Console.WriteLine("状态:{0}", LvBu.State.Description) |
小结
状态模式一般用于当一个对象有很多方法,在这些方法中会因为对象的不同状态而有着不同的行为时。如果不使用状态模式,我们就需要在这些行为方法中使用条件语句(分支语句)去判断不同状态时的行为;然而,我们通过使用状态模式将“状态”从对象中解耦,就可以最大限度地保证原有对象代码的稳定,当状态改变时,只要修改状态类就可以了,而无需修改每个对象中的行为方法。让我们再看看使用状态模式下的代码结构:
出自:http://www.caohuayu.com/books/B0003/B0003.aspx