23.角色被击中后——状态模式
角色扮演游戏和战争策略游戏的确不太一样,在战争游戏中,每个单元要不就正常运行,要不就是被摧毁;而在角色扮演游戏中,由于角色们有很多必杀技,在被这些必杀技击中后,有会出现的不同的影响,如攻击力下降、不能行动等等。
在这一章里,我们将要实现这个功能。
刚接到这个任务时,你可能会考虑根据不同的必杀技的参数来修改每个单位的各种动作的相关方法,比如:
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