github传送门
https://github.com/ddghost/unity3d_n/tree/R%26D%E6%99%BA%E8%83%BD%E5%B8%AE%E5%8A%A9%E6%8F%90%E7%A4%BA

本次作业之所以选择做牧师与恶魔(P&D)的智能帮助,主要是我觉得用代码能够写出游戏中的状态图挺有意思。
接下来看看实现的效果,这个是3个牧师和3个恶魔的效果。

这个是5个牧师和4个恶魔的效果(为什么没有4个牧师和恶魔的效果呢?因为那无解!我也是用了算法算后才发现这种情况是无解的)

算法简述

接下来说说算法,算法主要就是先创建一个有向图,图的存储结构可以用邻接矩阵或者邻接表,这里我选择了后者,用两个链表List来实现。创建完图后,每次要提示就通过当前状态来找到在图中的位置,然后宽度优先搜索找到终点。看一下这条最短路径的起始点就可以得出在当前状态下,下一步应该走去哪一个状态。

因为不想自己画一个图而且代码里的图也不好展示出来,所以不要脸的引用了师兄博客(https://blog.csdn.net/kiloveyousmile/article/details/71727667)的图,这个图是3个牧师和3个恶魔时候的状态图。

节点类

然后就是实现这个图了,首先我先定义了一个状态节点类,这个节点中的P,D是指这个状态下左边的牧师和恶魔数量,其实右岸也行,不过我选择了左岸,这主要是在建图的时候要注意就好了,同时要注意到,船在不同的岸也是不同的状态。

public class node{//node存的是左岸的P,Dpublic int P;public int D;public bool ifBoatSizeRight; //false left , true rightpublic List<node> adjacentNodes;public node (node cpyNode){this.P = cpyNode.P;this.D = cpyNode.D;this.ifBoatSizeRight = cpyNode.ifBoatSizeRight;this.adjacentNodes = new List<node>(cpyNode.adjacentNodes);}public node(int P , int D , bool boatSize){this.P = P;this.D = D;this.ifBoatSizeRight = boatSize;adjacentNodes = new List<node> ();}public void addAdjacentNode(node newAdjacentNode){if (!ifNodeExitInList (newAdjacentNode , adjacentNodes)) {adjacentNodes.Add (newAdjacentNode);}}public static bool ifNodeExitInList(node findNode , List<node> searchList){foreach (node temp in searchList) {if (ifTwoNodeSame(temp , findNode) ) {return true;}}return false;}//两个statu等价,除了P和D要相等外,船也要在同一岸.public static bool ifTwoNodeSame(node first , node second){if (first.P == second.P && first.D == second.D&& first.ifBoatSizeRight == second.ifBoatSizeRight) {return true;} else {return false;}}//到相接的点所用的操作public operation[] getOperationByAnthoerNode(){operation[] result = new operation[adjacentNodes.Count];for (int i = 0; i < adjacentNodes.Count; i++) {result [i] = new operation (Mathf.Abs(adjacentNodes [i].P - P),Mathf.Abs(adjacentNodes [i].D - D) );}return result;}public static node operator - (node first , operation second ){return new node (first.P - second.P, first.D - second.D , !first.ifBoatSizeRight);}public static node operator + (node first , operation second ){return new node (first.P + second.P, first.D + second.D ,  !first.ifBoatSizeRight);}
}

操作类

注意到刚才node的类里用到一个operation类,这个类是用来描述一个”操作”,例如P=1,D=1,的时候,意味着把一个牧师和一个恶魔放上船并且开船的操作。那么可以看到我重载了节点node和”操作”operation的+和-的操作符,这里是什么意义呢?其实一个节点加上一个”操作”,就意味着船把另一岸的P和D运到了这一岸,该节点的P和D增加了。那么同理,意味着的话就是把当前岸的P和D运到另一岸。在这个游戏中,”操作”主要有5种,就是1P,1D,1P1D,2P和2D。

public class operation {public int P;public int D;public operation(int P , int D){this.P = P;this.D = D;}}

带权节点类

然后我还定义了一个带权节点类,在搜索的时候用,继承node,其中的length代表走到该节点的长度(事实上我后来并没有用到),然后startNode就代表着走到该节点时最开始的节点是哪个。

public class withLengthNode:node{public int length;public node startNode;public withLengthNode(node thisNode , int length , node startNode = null) :base(thisNode){this.length = length;this.startNode = startNode;}public static bool ifNodeExitInList(node findNode , List<withLengthNode> searchList){foreach (node temp in searchList) {if (node.ifTwoNodeSame(temp , findNode) ) {return true;}}return false;}
}

状态图类

接来下就是状态图这个类了,主要两个功能,建图和宽度优先找下一步,
建图就是先把初始点放进一个队列,然后进行五个操作,将合法且不在队列中的点再放入队列中,并互相相连,然后直到队列中的每一个点都已经进行过五次操作后,图就建好了。
而宽度优先找下一步也是很类似,也是用一个队列先把起始点放入,然后将邻接点都放入队列,直到找到终点。注意到只要状态合法,就一定能找到终点,返回值是一个“操作”operation,给场记来让人上船并开船。

public class statusGraph{private List<node> allGraphNodes;private operation[] nodeOperations = {new operation (0, 1) , new operation (1, 0) , new operation (1, 1) , new operation (2, 0) , new operation (0, 2) };private int maxP;private int maxD;private bool boatStartSize ;private node endStatusNode ;public statusGraph(int maxP ,int maxD , bool boatStartSize){this.maxP = maxP;this.maxD = maxD;this.boatStartSize = boatStartSize;if (boatStartSize == true) {endStatusNode = new node (maxP, maxD, !boatStartSize);} else {endStatusNode =  new node (0, 0, !boatStartSize);}createGraph ();}private void createGraph(){allGraphNodes = new List<node> ();if (boatStartSize == true) {//开始船在右岸allGraphNodes.Add(new node(0, 0, boatStartSize));} else {//开始船在左岸allGraphNodes.Add(new node(maxP, maxD, boatStartSize));}for (int index = 0 ; index != allGraphNodes.Count ; index++) {node thisNode = allGraphNodes[index];foreach (operation op in nodeOperations) {node adjcentNode;if (thisNode.ifBoatSizeRight ) {//当前船在右岸,由于node是左岸,因此船送人过来,node的P和D应该增加。adjcentNode = thisNode + op;} else {adjcentNode = thisNode - op;}node anotherSizeNode = getAnotherSizeNode (adjcentNode);//若两岸node都合法if (ifNodeValid (adjcentNode) && ifNodeValid(anotherSizeNode)  ) {//若node在Graph中,node不加入graphadjcentNode = getNodeFromList (adjcentNode);//两个node互相接通(函数内会判断是否已有那个新的node,有就不加了)adjcentNode.addAdjacentNode (thisNode);thisNode.addAdjacentNode (adjcentNode);/*Debug.Log (thisNode.P + " " + thisNode.D + " " + thisNode.ifBoatSizeRight +"->" + adjcentNode.P + " " +  adjcentNode.D + " " + adjcentNode.ifBoatSizeRight );*/}}}}private bool ifNodeValid(node test){return (test.P >= test.D || test.P == 0) && test.D <= maxD && test.P <= maxP;}private node getNodeFromList(node findNode){foreach (node temp in allGraphNodes) {//两个statu等价,除了P和D要相等外,船也要在同一岸,list有node就返回nodeif (node.ifTwoNodeSame(temp , findNode) ) {return temp;}}//linkedList没有node,就把node加入里面 allGraphNodes.Add(findNode);return findNode;}//得到另一岸的nodeprivate node getAnotherSizeNode(node thisSizeNode){return new node(maxP - thisSizeNode.P , maxD - thisSizeNode.D , !thisSizeNode.ifBoatSizeRight );}public operation getNextStep(node nowNode){node anotherSizeNode = getAnotherSizeNode (nowNode);//用当前node得到graph中的nodeif(ifNodeValid (nowNode) && ifNodeValid(anotherSizeNode) ){nowNode = getNodeFromList (nowNode);node nextNode = getStartNodeByWidthSearch (nowNode);if (nextNode == null) {return null;} else {return new operation (Mathf.Abs (nowNode.P - nextNode.P), Mathf.Abs (nowNode.D - nextNode.D));}}else{return null;}}private node getStartNodeByWidthSearch(node startNode){List<withLengthNode> alreadySearchNode = new List<withLengthNode>();//开始点已搜索alreadySearchNode.Add (new withLengthNode(startNode , 0) );//与开始点相接的点全部放入list中,起始点就是相接的node自身,也即当前下一步要走的地方foreach (node adjacentNode in startNode.adjacentNodes) {if (node.ifTwoNodeSame (adjacentNode, endStatusNode)) {return adjacentNode;} else {alreadySearchNode.Add (new withLengthNode (adjacentNode, 1, adjacentNode));}}for (int i = 1; i < alreadySearchNode.Count; i++) {//alreadySearchNode [i]是当前要搜索的点,将所有与它相接且不在list中的node都放到待搜索list中foreach (node adjacentNode in alreadySearchNode[i].adjacentNodes ) {//如果当前node的下一个node是目的点,返回node的开始点,也就是下一步要走的地方if (node.ifTwoNodeSame (adjacentNode, endStatusNode)) {return alreadySearchNode[i].startNode;}//如果当前node的下一个node不是目的点,看它是否已被搜索,没被搜索就加入listelse if (!withLengthNode.ifNodeExitInList (adjacentNode, alreadySearchNode)) {alreadySearchNode.Add (new withLengthNode(adjacentNode , alreadySearchNode[i].length + 1 , alreadySearchNode[i].startNode ) );}}}return null;}
}

状态图的代码已经给完了,这个类最重要的作用就是提供一个得到下一步应该做什么的接口,然后写完这些,在原来的P&D的基础上稍做修改(之前写的不是很好,改起来也挺麻烦),点击提示按钮完成相应的动作即可,P&D的代码就不再详细讲述了。

一些想法

状态图(有限状态机)对于这些当前状态已知,操作较少,状态较少而且后果已知的游戏感觉还是挺好的,但假若当前状态未知,比如打牌是不知道对方有什么牌的,又或者执行一个操作可后果未知,比如放一个技能伤害不固定,还有生成出来的状态太多,这些情况下状态图可能就不太有效了。

PD 过河游戏智能帮助实现相关推荐

  1. Unity学习之PD 过河游戏智能帮助实现

    Unity学习之P&D 过河游戏智能帮助实现 根据之前设计好的动作分离版过河游戏,我们进行一个简单的状态图AI实现. 转移状态图 状态图老师已经给出: 该状态图只记录了游戏过程中左岸的情况.P ...

  2. 【Unity 3D学习笔记】PD 过河游戏智能实现

    P&D 过河游戏智能帮助实现 实现状态图的自动生成 讲解图数据在程序中的表示方法 利用算法实现下一步的计算 对于过河游戏,首先需要知道其中各个状态之间的转换关系,绘制状态转移图如下: 其中,P ...

  3. unity:PD 过河游戏智能帮助实现

    P&D 过河游戏智能帮助实现 github传送门 状态图 状态图课件有 状态图(Statechart Diagram)是描述一个实体基于事件反应的动态行为,显示了该实体如何根据当前所处的状态对 ...

  4. 3D游戏编程实践——PD 过河游戏智能帮助实现

    P&D 过河游戏智能帮助实现 需求 实现状态图的自动生成 讲解图数据在程序中的表示方法 利用算法实现下一步的计算 实现过程 实现状态图的自动生成&讲解图数据在程序中的表示方法 牧师与魔 ...

  5. Unity3d入门之路-PD 过河游戏智能帮助

    文章目录 P&D 过河游戏智能帮助 状态图 实现方法 图的表示方法 广度优先搜索 P&D 过河游戏拓展 结果展示 P&D 过河游戏智能帮助 本次作业基本要求是三选一,我选择了P ...

  6. 3D游戏编程与设计 PD(牧师与恶魔)过河游戏智能帮助实现

    3D游戏编程与设计 P&D(牧师与恶魔) 过河游戏智能帮助实现 文章目录 3D游戏编程与设计 P&D(牧师与恶魔) 过河游戏智能帮助实现 一.作业与练习 二.设计简述 1. 状态图基础 ...

  7. 【Unity3d学习】魔鬼与牧师过河游戏智能帮助

    文章目录 写在前面 实验内容 状态图自动生成(使用DFS) 1. 状态表示 2.DFS算法实现 3.DFS生成结果 更改Controller 效果展示 写在前面 本次项目Github地址:传送门 本次 ...

  8. 牧师与恶魔过河游戏——智能提示

    前言 这次实现一个含提示功能的牧师与恶魔过河小游戏,主要在上一个版本的牧师与恶魔小游戏上进行更改,通过增加一个状态计算和改版了得寻路算法,实现向玩家提示如何胜利完成游戏.游戏主体实现思路见上一篇博客- ...

  9. unity3D学习9 游戏智能

    游戏智能 作业要求 P&D 过河游戏智能帮助实现,程序具体要求: 实现状态图的自动生成 讲解图数据在程序中的表示方法 利用算法实现下一步的计算 参考:P&D 过河游戏智能帮助实现 实现 ...

最新文章

  1. VM 下装ubuntu系统
  2. 一分钟经理“游戏图”提要
  3. HTML——a标签实现空链接(禁止跳转)
  4. CSS cursor 属性
  5. String的indexOf()用于获取字符串中某个子字符串的位置
  6. 如何用wordpress搭建个人博客
  7. Python 监控 DB 服务器信息(cpu, 磁盘,进程,alert log) 脚本
  8. 怎么改变图片的尺寸?教你在线修改图片尺寸大小
  9. php laravel mix,php – Laravel Mix多个入口点生成一个manifest.js
  10. 基于CIM的智慧城市建设
  11. 广外专版-msn聊天记录查看器
  12. CS231n课程笔记翻译:卷积神经网络笔记
  13. bzoj2037 Sue的小球(区间dp,考虑到对未来的贡献)
  14. 哈工大硕士生用 Python 实现了 11 种经典数据降维算法,源代码库已开放
  15. java poi Excel加密文件导出和下载
  16. 喜欢吃鱼的朋友一定要存哦
  17. Redis五大数据类型常用命令与使用场景总结
  18. ubuntu Redis 数据库远程访问服务
  19. 32位谷歌浏览器的下载网址
  20. ext4 笔记一(与ext3比较)

热门文章

  1. nba2kol小程序 nba2kol2小程序 nba2k小程序
  2. lInux常用命令上(文件、远程管理相关)
  3. js中的堆内存和栈内存
  4. 一文搞定数据库连接池,太TM简单了,收藏!!!
  5. 干货收藏,100个抖音直播行业术语
  6. 《网络攻防实践》 第六周作业
  7. 人在迷茫时该干什么?
  8. 传统音乐制作与计算机音乐制作,电脑音乐制作与传统音乐制作的方式比较
  9. 终于把Carbide.c++ 1.2配置好了
  10. 打印机服务器响应错误是什么意思,打印机应用指南之打印机无法响应怎么办