RPG游戏战斗场景的设计

之前在学习设计模式,很有感触,感觉之前写的代码真的是太挫了。于是就打算开发一个简单的RPG游戏练一练。

正所谓有了锤子看什么都像钉子,各个模块写之前都想一想能不能用一些设计模式来设计。

RPG游戏的战斗场景是RPG游戏关键的一个部分,其中涉及到很多复杂的状态转换,比如直接物理攻击,选择魔法攻击,使用物品。。。

如下图是我模仿《仙剑奇侠传》中的战斗场景,稍微简化了一点,比如使用物品只能使用药品,没有投掷道具的功能。

RPG游戏战斗场景的设计_第1张图片

行动类型总体上包括三类:物理攻击、魔法攻击、使用物品,分别对应左下角的三个picturebox

战斗场景有明显的状态转换关系,比如,只有先点击物理攻击的按钮,才能选择攻击哪个敌人;点击魔法攻击后,需要先选择魔法,才能继续选择攻击哪个敌人。

状态转换图如下:

RPG游戏战斗场景的设计_第2张图片

(图中少了两条线,一条从选择魔法状态指向选择敌人状态,一条相反,分别为选择魔法和返回)

状态比较复杂,如果按照传统的方式,每次需要判断当前的状态,需要使用很多if语句,如果需要加入新的状态,代码的扩展性不强,修改起来比较麻烦。

我考虑到可以使用状态模式来设计。

状态模式(State Pattern)是设计模式的一种,属于行为模式。

定义(源于Design Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。
意图:允许一个对象在其内部状态改变时改变它的行为
适用场景:
1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2.一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。
(来自百度百科)
RPG游戏战斗场景的设计_第3张图片


首先定义状态接口:

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
            }

 
  
 
  

 
  

如果状态不是很多,需要选择的动作也不多,其实也不需要使用设计模式,这样容易使简单问题复杂化。

文章有不正确的地方欢迎指正。

 
 

你可能感兴趣的:(开发与设计模式,游戏,设计模式,设计)