WillKen's Blog.

3D游戏编程与设计-游戏智能

Word count: 1.3kReading time: 6 min
2019/12/07 Share

游戏智能

P&D 过河游戏智能帮助实现,程序具体要求:

代码原理

本次游戏基于之前的Priests&Devils过河小游戏。在原先的基础上增加了”AI Move”,能够提示用户进行正确的操作。

基本原理是:根据当前左岸上的牧师和恶魔数量以及船停靠的位置来判断下一步动作。

状态转换

状态转换图如下图所示:

相应代码如下:

采用P_D[]来记录要移动的角色。P_D[0]是要向对岸移动的牧师的数量。P_D[1]是要向对岸移动的恶魔的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
using UnityEngine;

namespace Mycontroller {
public class AIcontroller : MonoBehaviour {

public int[] getAIaction(int devilNUM, int priestNUM, int boatPosition)
{
//P_D[0] NUMBER OF PRIEST TO MOVE
//P_D[1] NUMBER OF DEVIL TO MOVE
int[] P_D = new int[2];

if (priestNUM == 3 && devilNUM == 3 && boatPosition == 1)
{
P_D[0] = 1;
P_D[1] = 1;
}
else if(priestNUM == 3 && devilNUM == 2 && boatPosition == -1)
{
P_D[0] = 0;
P_D[1] = 1;
}
else if(priestNUM == 2 && devilNUM == 2 && boatPosition == -1)
{
P_D[0] = 1;
P_D[1] = 0;
}
else if(priestNUM == 3 && devilNUM == 1 && boatPosition == -1)
{
P_D[0] = 0;
P_D[1] = 1;
}
else if(priestNUM == 3 && devilNUM == 2 && boatPosition == 1)
{
P_D[0] = 0;
P_D[1] = 2;
}
else if(priestNUM == 3 && devilNUM == 0 && boatPosition == -1)
{
P_D[0] = 0;
P_D[1] = 1;
}
else if(priestNUM == 3 && devilNUM == 1 && boatPosition == 1)
{
P_D[0] = 2;
P_D[1] = 0;
}
else if(priestNUM == 1 && devilNUM == 1 && boatPosition == -1)
{
P_D[0] = 1;
P_D[1] = 1;
}
else if(priestNUM == 2 && devilNUM == 2 && boatPosition == 1)
{
P_D[0] = 2;
P_D[1] = 0;
}
else if(priestNUM == 0 && devilNUM == 2 && boatPosition == -1)
{
P_D[0] = 0;
P_D[1] = 1;
}
else if(priestNUM == 0 && devilNUM == 3 && boatPosition == 1)
{
P_D[0] = 0;
P_D[1] = 2;
}
else if(priestNUM == 0 && devilNUM == 1 && boatPosition == -1)
{
P_D[0] = 0;
P_D[1] = 1;
}
else if(priestNUM == 2 && devilNUM == 1 && boatPosition == 1)
{
P_D[0] = 2;
P_D[1] = 0;
}
else if(priestNUM == 1 && devilNUM == 1 && boatPosition == 1)
{
P_D[0] = 1;
P_D[1] = 1;
}
else if(priestNUM == 0 && devilNUM == 2 && boatPosition == 1)
{
P_D[0] = 0;
P_D[1] = 2;
}
return P_D;
}
}
}

AI Move原理

AI Move 运行原理为:

点击按钮后,首先判断当前游戏状态。若游戏没有结束,则读取当前岸上的各个角色和船的位置,根据状态图找到下一动作进行执行。

  • 判断当前游戏状态

    1
    2
    3
    int flag = judge.check();
    if (flag == 1 || flag ==2)
    return;
  • 获取from岸上的牧师数量和恶魔数量

    int numDevilLeft = judge.from_devil, numPriestLeft = judge.from_priest;

  • 获取船的位置

    int boatPos = boat.get_is_from();

  • 输入状态,获取下一步动作

    1
    2
    3
    4
    Debug.Log("State:"+numDevilLeft+" "+numPriestLeft+" "+boat.get_is_from());
    int[] P_D = agent.getAIaction(numDevilLeft, numPriestLeft, boat.get_is_from());
    Debug.Log("P_D[0] " + P_D[0]);
    Debug.Log("P_D[1] " + P_D[1]);
  • 人从船上上岸的过程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    int numPriestToBoat = 0, numDevilToBoat = 0;
    int[] personOnBoat = boat.getPersonOnBoat();
    Debug.Log("Boat0 "+personOnBoat[0]+" boat1"+personOnBoat[1]);
    if (personOnBoat[0] >= 3 && personOnBoat[0] <= 6)
    {
    if (P_D[0] > 0)
    numPriestToBoat++;
    else if (P_D[0] == 0)
    {
    Debug.Log("getoff P"+personOnBoat[0]);
    moveCharacter(characters[personOnBoat[0]]);
    }
    }
    else if (personOnBoat[0] >= 0 && personOnBoat[0] <= 2)
    {
    if (P_D[1] > 0)
    numDevilToBoat++;
    else if (P_D[1] == 0)
    {
    Debug.Log("getoff D"+personOnBoat[0]);
    moveCharacter(characters[personOnBoat[0]]);
    }
    }

    if (personOnBoat[1] >= 3 && personOnBoat[1] <= 6)
    {
    if (P_D[0] == 2 || (P_D[0] == 1 && numPriestToBoat == 0))
    numPriestToBoat++;
    else if (P_D[0] == 0 || (P_D[0] == 1 && numPriestToBoat == 1))
    {
    Debug.Log("getoff P"+personOnBoat[1]);
    moveCharacter(characters[personOnBoat[1]]);
    }
    }
    else if (personOnBoat[1] >= 0 && personOnBoat[1] <= 2)
    {
    if (P_D[1] == 2 || (P_D[1] == 1 && numDevilToBoat == 0))
    numDevilToBoat++;
    else if (P_D[1] == 0 || (P_D[1] == 1 && numDevilToBoat == 1))
    {
    Debug.Log("getoff D"+personOnBoat[1]);
    moveCharacter(characters[personOnBoat[1]]);
    }
    }
  • 人从岸边上船的过程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    for (int i = 0; i < P_D[0] - numPriestToBoat; i++)
    {
    int index = -1;
    if (boatPos == 1)
    index = fromCoast.getAPriestIndex();
    else if (boatPos == -1)
    index = toCoast.getAPriestIndex();
    Debug.Log("geton P"+index);
    moveCharacter(characters[index]);
    }
    for (int i = 0; i < P_D[1] - numDevilToBoat; i++)
    {

    int index = -1;
    if (boatPos == 1)
    index = fromCoast.getADevilIndex();
    else if (boatPos == -1)
    index = toCoast.getADevilIndex();
    Debug.Log("geton D"+index);
    moveCharacter(characters[index]);
    }

接口处理

  • 在智能动作函数中传递的参数都是角色的下标。所以对角色控制器添加index属性。

    • 修改构造函数如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public CharacterController(string name,int t){
      if(name == "priest"){
      character = Object.Instantiate(Resources.Load("Prefab/priest",typeof(GameObject)),Vector3.zero,Quaternion.identity,null) as GameObject;
      is_devil = false;
      }else if(name == "devil"){
      character = Object.Instantiate(Resources.Load("Prefab/devil",typeof(GameObject)),Vector3.zero,Quaternion.identity,null) as GameObject;
      is_devil = true;
      }
      index = t;
      move = character.AddComponent(typeof(Move))as Move;
      click = character.AddComponent(typeof(UserClick))as UserClick;
      click.setController(this);
      }
    • 添加返回index函数

      1
      2
      3
      4
      public int getCharacterIndex()
      {
      return index;
      }
  • 传递在岸上的角色的下标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public int getAPriestIndex()
    {
    for (int i = 0; i < characters.Length; i++)
    {
    if (characters [i] == null)
    continue;
    if (!characters[i].Is_Devil())
    return characters[i].getCharacterIndex();
    else
    continue;
    }
    return -1;
    }

    public int getADevilIndex()
    {
    for (int i = 0; i < characters.Length; i++)
    {
    if (characters [i] == null)
    continue;
    if (characters[i].Is_Devil())
    return characters[i].getCharacterIndex();
    else
    continue;
    }
    return -1;
    }
  • 传递在船上角色的下标

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public int[] getPersonOnBoat()
    {
    int[] result = new int[2];
    for (int i = 0; i < 2; i++)
    {
    if (characters [i] == null)
    result[i] = -1;
    else
    result[i] = characters[i].getCharacterIndex();
    }
    return result;
    }

效果展示

游戏界面:

游戏视频:

视频地址

项目地址:

https://github.com/WillKen/Priests-and-Devils-AI

CATALOG
  1. 1. 游戏智能
    1. 1.1. 代码原理
      1. 1.1.1. 状态转换
      2. 1.1.2. AI Move原理
      3. 1.1.3. 接口处理
    2. 1.2. 效果展示