PD 过河游戏智能帮助实现
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 过河游戏智能帮助实现相关推荐
- Unity学习之PD 过河游戏智能帮助实现
Unity学习之P&D 过河游戏智能帮助实现 根据之前设计好的动作分离版过河游戏,我们进行一个简单的状态图AI实现. 转移状态图 状态图老师已经给出: 该状态图只记录了游戏过程中左岸的情况.P ...
- 【Unity 3D学习笔记】PD 过河游戏智能实现
P&D 过河游戏智能帮助实现 实现状态图的自动生成 讲解图数据在程序中的表示方法 利用算法实现下一步的计算 对于过河游戏,首先需要知道其中各个状态之间的转换关系,绘制状态转移图如下: 其中,P ...
- unity:PD 过河游戏智能帮助实现
P&D 过河游戏智能帮助实现 github传送门 状态图 状态图课件有 状态图(Statechart Diagram)是描述一个实体基于事件反应的动态行为,显示了该实体如何根据当前所处的状态对 ...
- 3D游戏编程实践——PD 过河游戏智能帮助实现
P&D 过河游戏智能帮助实现 需求 实现状态图的自动生成 讲解图数据在程序中的表示方法 利用算法实现下一步的计算 实现过程 实现状态图的自动生成&讲解图数据在程序中的表示方法 牧师与魔 ...
- Unity3d入门之路-PD 过河游戏智能帮助
文章目录 P&D 过河游戏智能帮助 状态图 实现方法 图的表示方法 广度优先搜索 P&D 过河游戏拓展 结果展示 P&D 过河游戏智能帮助 本次作业基本要求是三选一,我选择了P ...
- 3D游戏编程与设计 PD(牧师与恶魔)过河游戏智能帮助实现
3D游戏编程与设计 P&D(牧师与恶魔) 过河游戏智能帮助实现 文章目录 3D游戏编程与设计 P&D(牧师与恶魔) 过河游戏智能帮助实现 一.作业与练习 二.设计简述 1. 状态图基础 ...
- 【Unity3d学习】魔鬼与牧师过河游戏智能帮助
文章目录 写在前面 实验内容 状态图自动生成(使用DFS) 1. 状态表示 2.DFS算法实现 3.DFS生成结果 更改Controller 效果展示 写在前面 本次项目Github地址:传送门 本次 ...
- 牧师与恶魔过河游戏——智能提示
前言 这次实现一个含提示功能的牧师与恶魔过河小游戏,主要在上一个版本的牧师与恶魔小游戏上进行更改,通过增加一个状态计算和改版了得寻路算法,实现向玩家提示如何胜利完成游戏.游戏主体实现思路见上一篇博客- ...
- unity3D学习9 游戏智能
游戏智能 作业要求 P&D 过河游戏智能帮助实现,程序具体要求: 实现状态图的自动生成 讲解图数据在程序中的表示方法 利用算法实现下一步的计算 参考:P&D 过河游戏智能帮助实现 实现 ...
最新文章
- VM 下装ubuntu系统
- 一分钟经理“游戏图”提要
- HTML——a标签实现空链接(禁止跳转)
- CSS cursor 属性
- String的indexOf()用于获取字符串中某个子字符串的位置
- 如何用wordpress搭建个人博客
- Python 监控 DB 服务器信息(cpu, 磁盘,进程,alert log) 脚本
- 怎么改变图片的尺寸?教你在线修改图片尺寸大小
- php laravel mix,php – Laravel Mix多个入口点生成一个manifest.js
- 基于CIM的智慧城市建设
- 广外专版-msn聊天记录查看器
- CS231n课程笔记翻译:卷积神经网络笔记
- bzoj2037 Sue的小球(区间dp,考虑到对未来的贡献)
- 哈工大硕士生用 Python 实现了 11 种经典数据降维算法,源代码库已开放
- java poi Excel加密文件导出和下载
- 喜欢吃鱼的朋友一定要存哦
- Redis五大数据类型常用命令与使用场景总结
- ubuntu Redis 数据库远程访问服务
- 32位谷歌浏览器的下载网址
- ext4 笔记一(与ext3比较)