Unity学习之P&D 过河游戏智能帮助实现

  前面已经实现了牧师魔鬼过河小游戏,这次,学习了AI之后,尝试了实现其智能帮助功能。以下是效果图:
这里写图片描述
  这个AI比较简单,只要分析清楚了牧师魔鬼过河过程中的各个状态,再用算法实现即可。以下是状态图:
这里写图片描述
  该状态图只记录了游戏过程中左岸的情况。P代表牧师,D代表魔鬼,B代表船。当船在右岸时不记录。双箭头代表两个状态可以相互转化。
  
  实现AI的基本原理是:点击next之后,分析当前场景的状态,读取成功路径上的下一个状态,执行相应动作以达到下一个状态。
  基于以上原理,我们可以按如下方式实现。首先,根据当前的状态,分析接下来需要执行的一个动作。以第一个状态为例:3P3DB。代表左岸有3个牧师,3个魔鬼,船也在左岸。它的下一个状态有4个,但是其中一个执行后会输掉游戏,不考虑。剩下三个状态中,只有2P2D和3P1D是成功路径上的状态,可以任选一个,所以我增加了随机选择函数。找到对应状态后,把达到下一个状态需要运送的船员数目作为返回值,这样就可以直接执行上下船了。具体实现如下:

//要执行的动作
public enum Boataction { P, D, PP, DD, PD }
    //P:船运载一个牧师
    //D:船运载一个恶魔
    //PP:船运载两个牧师
    //DD:船运载两个恶魔
    //PD:船运载一个牧师,一个恶魔

//船将要承载的船员的情况及船的状态
public struct nextPassenger
{
    public Status boat;
    public Boataction boataction;
}

 //获取一个随机数  有的路径有两条可选,随机选择一条
private int randomValue()
{
    float num = Random.Range(0f, 1f);
    if (num <= 0.5f) return 1;
    else return 2;
}

//获取目前的牧师、魔鬼分布状态  返回值 = 船将要承载的船员的情况
private nextPassenger getNextPassenger()
{
    next.boat = nowStatue;
    if (next.boat == Status.BLRING ||
        next.boat == Status.BRLING ||
        next.boat == Status.LOSE ||
        next.boat == Status.WIN || boatSize() != 0)
     {
          next.boat = Status.None;
          return next;
      }

      else
      {
           if (next.boat == Status.BLEFT &&
               leftPriests.Count == 3 && 
               leftDevils.Count == 3)
           {
                int turn = randomValue();
                if (turn == 1)
                    next.boataction = Boataction.PD;
                else
                    next.boataction = Boataction.DD;
            }

          else if (next.boat == Status.BRIGHT && 
               leftPriests.Count == 2 && 
               leftDevils.Count == 2)
           {
               next.boataction = Boataction.P;
           }

          else if (next.boat == Status.BRIGHT && 
               leftPriests.Count == 3 &&
               leftDevils.Count == 2)
           {
               next.boataction = Boataction.D;
            }

          else if (next.boat == Status.BRIGHT && 
               leftPriests.Count == 3 &&
               leftDevils.Count == 1)
           {
               next.boataction = Boataction.D;
           }

          else if (next.boat == Status.BLEFT && 
               leftPriests.Count == 3 &&
               leftDevils.Count == 2)
           {
               next.boataction = Boataction.DD;
            }

          else if (next.boat == Status.BRIGHT && 
               leftPriests.Count == 3 && 
               leftDevils.Count == 0)
            {
                next.boataction = Boataction.D;
            }

          else if (next.boat == Status.BLEFT &&
               leftPriests.Count == 3 &&
               leftDevils.Count == 1)
           {
               next.boataction = Boataction.PP;
           }

          else if (next.boat == Status.BRIGHT &&
              leftPriests.Count == 1 &&
              leftDevils.Count == 1)
          {
              next.boataction = Boataction.PD;
          }

         else if (next.boat == Status.BLEFT &&
             leftPriests.Count == 2 &&
             leftDevils.Count == 2)
         {
             next.boataction = Boataction.PP;
         }

         else if (next.boat == Status.BRIGHT &&
             leftPriests.Count == 0 &&
             leftDevils.Count == 2)
         {
             next.boataction = Boataction.D;
         }

        else if (next.boat == Status.BLEFT &&
            leftPriests.Count == 0 &&
            leftDevils.Count == 3)
        {
            next.boataction = Boataction.DD;
        }

       else if (next.boat == Status.BRIGHT &&
           leftPriests.Count == 0 &&
           leftDevils.Count == 1)
       {
           int turn = randomValue();
           if (turn == 1) next.boataction = Boataction.D;
           else next.boataction = Boataction.P;
        }

        else if (next.boat == Status.BLEFT &&
            leftPriests.Count == 2 &&
            leftDevils.Count == 1)
         {
              next.boataction = Boataction.P;
         }

         else if (next.boat == Status.BLEFT &&
             leftPriests.Count == 0 &&
             leftDevils.Count == 2)
         {
             next.boataction = Boataction.DD;
         }

         else if (next.boat == Status.BLEFT &&
             leftPriests.Count == 1 &&
                 leftDevils.Count == 1)
         {
             next.boataction = Boataction.PD;
         }

     }

     return next;
}

  接下来,执行一些动作,以便到达下一个状态。不过,这次需要自动执行上船,开船,和下船动作。其中,上船和下船,可以复用前面实现的代码。
  上船,需要判断船员情况,所以稍微复杂一些。

public void nextOnboat()
{
    nextPassenger next = getNextPassenger();
    if (next.boat == Status.None) return;
    if (next.boataction == Boataction.D)
        devilOnBoat();
    else if (next.boataction == Boataction.DD)
    {
        devilOnBoat();
        devilOnBoat();
    }
    else if (next.boataction == Boataction.P)
        priestOnBoat();
    else if (next.boataction == Boataction.PD)
    {
        priestOnBoat();
        devilOnBoat();
    }
    else if (next.boataction == Boataction.PP)
    {
        priestOnBoat();
        priestOnBoat();
    }
    else { }

    if (next.boat == Status.BLEFT)
         Invoke("boatLeftToRight", 0.5f);
    else {
         Invoke("boatRightToLeft", 0.5f);
    }
    instatus = true;//这是用来判断何时下船的一个辅助变量
}

  接下来,就是下船的实现了。这个非常简单。

public void nextOffBoat()
{
   if (nextStatus == true)//nextStatus = true 点击了next,一切处于自动运行状态。需要实现自动上下船。
   {
       while(boatSize() != 0)
       {
            int pos = boatSize() - 1;
            if (boatPassName[pos] == "devil")
                devilOffBoat(pos);
            else
                priestOffBoat(pos);
        }
    }
    instatus = false;
    nextStatus = false;
}

  那么,最后一步,更新IUserAction接口和OnGUI函数,实现界面。在OnGUI中只要新增加了Next按钮和自动上下船。具体代码如下:
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

  以上是本次学习的全部内容,希望对大家有所帮助。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kiloveyousmile

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值