之前在学习设计模式,很有感触,感觉之前写的代码真的是太挫了。于是就打算开发一个简单的RPG游戏练一练。
正所谓有了锤子看什么都像钉子,各个模块写之前都想一想能不能用一些设计模式来设计。
RPG游戏的战斗场景是RPG游戏关键的一个部分,其中涉及到很多复杂的状态转换,比如直接物理攻击,选择魔法攻击,使用物品。。。
如下图是我模仿《仙剑奇侠传》中的战斗场景,稍微简化了一点,比如使用物品只能使用药品,没有投掷道具的功能。
行动类型总体上包括三类:物理攻击、魔法攻击、使用物品,分别对应左下角的三个picturebox
战斗场景有明显的状态转换关系,比如,只有先点击物理攻击的按钮,才能选择攻击哪个敌人;点击魔法攻击后,需要先选择魔法,才能继续选择攻击哪个敌人。
状态转换图如下:
(图中少了两条线,一条从选择魔法状态指向选择敌人状态,一条相反,分别为选择魔法和返回)
状态比较复杂,如果按照传统的方式,每次需要判断当前的状态,需要使用很多if语句,如果需要加入新的状态,代码的扩展性不强,修改起来比较麻烦。
我考虑到可以使用状态模式来设计。
状态模式(State Pattern)是设计模式的一种,属于行为模式。
首先定义状态接口:
public interface State
{
void chooseAction(int action);
void chooseMagic(Magic magic);
//使用物品及使用药品
void chooseDrug(Drug drug);
void chooseEnemy(Fighter enemy);
void choosePlayer(Player player);
void goBack();
}
五个子类继承State接口,分别为Start、ChooseMagic、ChooseDrug、ChooseEnemy、ChoosePlayer。
根据状态转换图,实现类中的函数,代码如下:
public class Start:State
{
FightModel fightModel;
public void chooseAction(int action)
{
if (action == 0)
{
fightModel.setState(fightModel.getCHOOSE_MAGIC());
}
else if (action == 1)
{
fightModel.setState(fightModel.getCHOOSE_DRUG());
}
else if (action == 2)
{
fightModel.setState(fightModel.getCHOOSE_ENEMY());
}
}
public void chooseMagic(Magic magic)
{ }
public void chooseDrug(Drug drug)
{ }
public void chooseEnemy(Fighter fighter)
{ }
public void choosePlayer(Player player)
{ }
public void goBack()
{ }
}
public class ChooseMagic : State
{
FightModel fightModel;
public void chooseAction(int action)
{ }
public void chooseMagic(Magic magic)
{
fightModel.setChooseMagic(magic);
if (magic.getAttackAll())
{
fightModel.getActionList().Add(
new AttackNode(fightModel.getFightingPlayer(), magic.getID()));
fightModel.setState(fightModel.getSTART());
}
else
{
fightModel.setState(fightModel.getCHOOSE_ENEMY());
}
}
public void chooseDrug(Drug drug)
{ }
public void chooseEnemy(Fighter fighter)
{ }
public void choosePlayer(Player player)
{ }
public void goBack()
{
fightModel.setState(fightModel.getSTART());
}
}
public class ChooseDrug : State
{
FightModel fightModel;
public void chooseAction(int action)
{ }
public void chooseMagic(Magic magic)
{ }
public void chooseDrug(Drug drug)
{
fightModel.setChooseDrug(drug);
fightModel.setState(fightModel.getCHOOSE_PLAYER());
}
public void chooseEnemy(Fighter fighter)
{ }
public void choosePlayer(Player player)
{ }
public void goBack()
{
fightModel.setState(fightModel.getSTART());
}
}
public class ChooseEnemy : State
{
FightModel fightModel;
public void chooseAction(int action)
{ }
public void chooseMagic(Magic magic)
{ }
public void chooseDrug(Drug drug)
{ }
public void chooseEnemy(Fighter enemy)
{
fightModel.setAttackReceiver(enemy);
fightModel.setState(fightModel.getSTART());
int magicID;
if (fightModel.getChooseMagic() != null)
{
magicID = fightModel.getChooseMagic().getID();
}
else
{
magicID = -1;//普通攻击
}
fightModel.getActionList().Add(
new AttackNode(fightModel.getFightingPlayer(), enemy, magicID));
//保存信息后重新设置
fightModel.setAttackReceiver(null);
fightModel.setChooseDrug(null);
fightModel.setChooseMagic(null);
fightModel.setDrugReceiver(null);
}
public void choosePlayer(Player player)
{ }
public void goBack()
{
if (fightModel.getChooseMagic() == null)
{
fightModel.setState(fightModel.getSTART());
}
else
{
fightModel.setChooseMagic(null);
fightModel.setState(fightModel.getCHOOSE_MAGIC());
}
}
}
public class ChoosePlayer : State
{
FightModel fightModel;
public void chooseAction(int action)
{ }
public void chooseMagic(Magic magic)
{ }
public void chooseDrug(Drug drug)
{ }
public void chooseEnemy(Fighter fighter)
{ }
public void choosePlayer(Player player)
{
fightModel.setDrugReceiver(player);
fightModel.setState(fightModel.getSTART());
fightModel.getActionList().Add(
new TakeGrugNode(fightModel.getChooseDrug(), fightModel.getDrugReceiver()));
//保存信息后重新设置
fightModel.setAttackReceiver(null);
fightModel.setChooseDrug(null);
fightModel.setChooseMagic(null);
fightModel.setDrugReceiver(null);
}
public void goBack()
{
fightModel.setState(fightModel.getCHOOSE_DRUG());
fightModel.setChooseDrug(null);
}
}
这样在战斗时,根据具体的操作转换状态。状态图跟编译原理中的有限自动机的图有点类似。
如果不用状态模式设计也可以,但是将需要写大量的if语句,比如:(key指的是键盘的keycode,实际中我没使用鼠标点击,使用的是键盘的上下左右键来选择相应的操作)
if (fightModel.getState() == fightModel.getSTART())
{
if (key == "Left")
{
fightModel.redAction();
}
else if(key=="Right")
{
fightModel.addAction();
}
else if (key == "Up")//Up应该改为Enter
{
int action=fightModel.getAction();
switch (action)
{
case 0:
if (fightModel.enemyList.Count < 2)
{
fightModel.addAttackNode(fightModel.getFightingPlayer(), fightModel.enemyList[0], -1);
//fightModel.setState(fightModel.getSTART());
}
else
{
fightModel.setState(fightModel.getCHOOSE_ENEMY());
}
break;
case 1:
fightModel.setState(fightModel.getCHOOSE_MAGIC());
break;
case 2:
fightModel.setState(fightModel.getCHOOSE_DRUG());
break;
}
}
}
else if (fightModel.getState() == fightModel.getCHOOSE_ENEMY())
{
if (key == "Left")
{
fightModel.redEnemy();
}
else if (key == "Right")
{
fightModel.addEnemy();
}
else if (key == "Up")//应该改为Enter
{
if (fightModel.getChooseMagic() == null)
{
fightModel.addAttackNode(fightModel.getFightingPlayer(), fightModel.enemyList[fightModel.getEnemy()], -1);
}
else
{
fightModel.addAttackNode(fightModel.getFightingPlayer(), fightModel.enemyList[fightModel.getEnemy()], fightModel.getChooseMagic().getID());
}
fightModel.setAttackReceiver(fightModel.enemyList[fightModel.getEnemy()]);
}
}
else if (fightModel.getState() == fightModel.getCHOOSE_MAGIC())
{
//do something
}
else if (fightModel.getState() == fightModel.getCHOOSE_PLAYER())
{
//do something
}
else if (fightModel.getState() == fightModel.getCHOOSE_DRUG())
{
//do something
}
如果状态不是很多,需要选择的动作也不多,其实也不需要使用设计模式,这样容易使简单问题复杂化。
文章有不正确的地方欢迎指正。