[A*算法]基于Unity实现A*算法(二)
写在前面:上一篇当时是非常简单的了解一下A*,昨天还有一些问题没解决,就暂时把自己查阅的文坛摘抄了过来(毕竟人家写的比我要好的多 :> )
今天终于解决了,就又写了这一篇,正好我自己再梳理一遍,把Unity的实现也记录一下(Unity版本:2019.3.7.f1)
====================================================================================
- 一、Unity UI表现实现
UI制作很简单,如下图

- Canvas

UGUI画布(参考官网文档)
我这边主要是在上面挂载一个寻路脚本CSPathFindManager_AStar.CS
2.Panel

这里我用的是UGUI创建的Panel,主要是用来生成寻路点,挂载生成脚本CopyManager.CS
3.Node

UGUI创建的Image,用的纯色,用来充当路径点,挂载路径点脚本CSNode.CS
4.F/G/H/Pos/Index

UGUI创建的Text,通过修改文件对齐方式,使他们的文字分别位于Node的上下左右和中间,用来方便显示相关的值
二、Unity UI显示代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine; public class CSPathFindManager : MonoBehaviour
{
internal CopyObjs copyObjs; public Vector2 CellSize = new Vector2(); public List<CSNode> AllNodesList = new List<CSNode>();
public List<CSNode> openNodeList = new List<CSNode>();
public List<CSNode> closeNodeList = new List<CSNode>(); public int notMoveNodeCount = ;
public CSNode curNode = null; public bool isEndFind = false; public bool isFinding = false; public bool isAutoFind = true; public bool canBevel = true; // Start is called before the first frame update
void Start()
{
copyObjs = gameObject.GetComponentInChildren<CopyObjs>();
if (copyObjs == null) return;
InitAllPath();
} // Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (isAutoFind)
{
isFinding = !isFinding;
}
else
{
InitNearNodes();
}
} if (isFinding)
{
InitNearNodes();
}
} public virtual void InitAllPath()
{
AllNodesList.Clear();
openNodeList.Clear();
closeNodeList.Clear(); if (copyObjs == null) return;
copyObjs.MaxCount = (int)(CellSize.x * CellSize.y); int nodeIndex = ;
//System.Random tempRandom = new System.Random(GetRandomSeedbyGuid()); for (int y = ; y < CellSize.x; y++)
{
for (int x = ; x < CellSize.y; x++)
{
CSNode tempNode = copyObjs.copyObjList[nodeIndex].GetComponent<CSNode>();
tempNode.SetPos(x, y); if (tempNode.curMoveState == EnumMoveState.NULL)
{
tempNode.SetMoveState(EnumMoveState.moveing);
} AllNodesList.Add(tempNode);
nodeIndex++;
}
}
} public virtual List<CSNode> GetNearNode(CSNode node)
{
List<CSNode> tempNode = AllNodesList.FindAll(temp => IsChild(node, temp)); if (tempNode != null) tempNode.Remove(node);
return tempNode;
} public virtual bool IsChild(CSNode parentNode, CSNode childNode)
{
if (parentNode == null || childNode == null) return false;
if (canBevel)
{
return Mathf.Abs(childNode.pos.y - parentNode.pos.y) <= && Mathf.Abs(childNode.pos.x - parentNode.pos.x) <= ;
}
else
{
return (childNode.pos.x == parentNode.pos.x && Mathf.Abs(childNode.pos.y - parentNode.pos.y) == ) || (childNode.pos.y == parentNode.pos.y && Mathf.Abs(childNode.pos.x - parentNode.pos.x) == );
}
} public virtual int GetGValue(CSNode startNode, CSNode targetNode)
{
int gV;
if (targetNode.pos.x == startNode.pos.x || targetNode.pos.y == startNode.pos.y) gV = ;
int absX = (int)Mathf.Abs(targetNode.pos.x - startNode.pos.x);
int absY = (int)Mathf.Abs(targetNode.pos.y - startNode.pos.y);
if (canBevel)
{
if (absX > absY)
{
gV = * absY + * (absX - absY);
}
else
{
gV = * absX + * (absY - absX);
}
}
else
{
gV = * absX + * absY;
}
return gV + startNode.GValue;
} public virtual int GetHValue(CSNode node)
{
if (CSNode.FinialNode == null) return ;
return (int)Mathf.Abs(node.pos.x - CSNode.FinialNode.pos.x) * + (int)Mathf.Abs(node.pos.y - CSNode.FinialNode.pos.y) * ;
} public virtual void InitNearNodes()
{ } //int GetRandomSeedbyGuid()
//{
// return new Guid().GetHashCode();
//} public virtual void ReSetPath()
{
curNode = null;
for (int i = ; i < openNodeList.Count; i++)
{
openNodeList[i].SetIndex();
openNodeList[i].parent = null;
}
openNodeList.Clear();
for (int i = ; i < closeNodeList.Count; i++)
{
if ((int)closeNodeList[i].curMoveState > && (int)closeNodeList[i].curMoveState < ) closeNodeList[i].SetMoveState(EnumMoveState.moveing);
closeNodeList[i].parent = null;
}
closeNodeList.Clear();
isEndFind = false;
} public virtual CSNode GetMinNode(List<CSNode> nodes)
{
if (nodes == null || nodes.Count == ) return null;
nodes.Sort((x, y) => x.FValue.CompareTo(y.FValue));
return nodes[];
} public virtual void RemoveNotMoveNode(ref List<CSNode> tempNearList)
{
List<CSNode> notMoveNode = tempNearList.FindAll(temp => temp.curMoveState == EnumMoveState.notmove);
tempNearList.RemoveAll(temp => temp.curMoveState == EnumMoveState.notmove);
for (int i = ; i < notMoveNode.Count; i++)
{
if (notMoveNode[i].pos.y == curNode.pos.y || notMoveNode[i].pos.x == curNode.pos.x) tempNearList.RemoveAll(temp => temp.pos.y != curNode.pos.y && temp.pos.x != curNode.pos.x && (temp.pos.x == notMoveNode[i].pos.x || temp.pos.y == notMoveNode[i].pos.y));
tempNearList.Remove(notMoveNode[i]);
}
}
}
CSPathFindManager.CS
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine; public class CSPathFindManager_AStar : CSPathFindManager
{ public override void InitNearNodes()
{
if (CSNode.StartNode == null || CSNode.FinialNode == null)
{
isFinding = false;
return;
}
if (!isEndFind)
{
if (curNode == null)
{
curNode = CSNode.StartNode;
curNode.SetIndex();
}
else if (curNode != CSNode.FinialNode)
{
curNode = GetMinNode(openNodeList); curNode.SetIndex(curNode.parent.Index + );
}
else
{
isEndFind = true;
}
} if(isEndFind)
{
if (curNode!= null && curNode.parent != null && curNode.parent != CSNode.StartNode)
{
curNode = curNode.parent;
curNode.SetMoveState(EnumMoveState.best);
}
else
{
isFinding = false; Debug.LogError("找到了!");
}
return;
}
if (curNode.curMoveState == EnumMoveState.moveing) curNode.SetMoveState(EnumMoveState.select);
if (AllNodesList == null) return;
for (int i = ; i < closeNodeList.Count; i++)
{
if (closeNodeList[i].curMoveState == EnumMoveState.select) closeNodeList[i].SetMoveState(EnumMoveState.path);
}
closeNodeList.Add(curNode);
openNodeList.Remove(curNode);
List<CSNode> tempNearList = GetNearNode(curNode);
tempNearList.RemoveAll(temp => closeNodeList.Contains(temp));
RemoveNotMoveNode(ref tempNearList); for (int i = ; i < tempNearList.Count; i++)
{
if (tempNearList[i].parent == null || tempNearList[i].parent.Index >= curNode.Index)
{
if (tempNearList[i].parent!= null && tempNearList[i].parent.Index == curNode.Index)
{
int gV1 = GetGValue(curNode, tempNearList[i]);
int gV2 = GetGValue(tempNearList[i].parent, tempNearList[i]);
if (gV1 > gV2)
{
continue;
}
}
tempNearList[i].parent = curNode;
int gV = GetGValue(tempNearList[i].parent, tempNearList[i]);
int hV = GetHValue(tempNearList[i]);
tempNearList[i].SetNodeInfo(gV, hV);
}
} openNodeList.AddRange(tempNearList.FindAll(temp=>!closeNodeList.Contains(temp) && !openNodeList.Contains(temp))); //for (int i = 0; i < openNodeList.Count; i++)
//{
// int gV = GetGValue(openNodeList[i].parent, openNodeList[i]);
// int hV = GetHValue(openNodeList[i]);
// openNodeList[i].SetNodeInfo(gV, hV);
//}
if (openNodeList.Count == )
{
isFinding = false;
Debug.LogError("死路!");
}
}
}
CSPathFindManager_AStar
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI; public enum EnumMoveState
{
NULL,
moveing,
notmove,
start,
finial,
path,
select,
best,
} public class CSNode : MonoBehaviour
{ private Text F;
private Text G;
private Text H;
private Text PosText;
private Text IndexText;
private Image movePoint; public Vector2 pos;
public int FValue;
public int GValue;
public int HValue;
public int Index; public CSNode parent;
public EnumMoveState curMoveState; private EventTrigger eventTrigger;
private CSPathFindManager cSPathFindManager; public static CSNode StartNode;
public static CSNode FinialNode;
public static List<CSNode> NotMoveNodeList = new List<CSNode>(); public CSNode(int _x,int _y)
{
pos.x = _x;
pos.y = _y;
} public static Dictionary<EnumMoveState, Color> pointColorDic = new Dictionary<EnumMoveState, Color>()
{
{ EnumMoveState.moveing,Color.white},
{ EnumMoveState.notmove,Color.red},
{ EnumMoveState.start,Color.blue},
{ EnumMoveState.finial,Color.yellow},
{ EnumMoveState.path,Color.gray},
{ EnumMoveState.select,Color.cyan},
{ EnumMoveState.best,Color.green},
};
// Start is called before the first frame update
void Awake()
{
F = transform.Find("F").GetComponent<Text>();
G = transform.Find("G").GetComponent<Text>();
H = transform.Find("H").GetComponent<Text>();
PosText = transform.Find("Pos").GetComponent<Text>();
IndexText = transform.Find("Index").GetComponent<Text>();
movePoint = GetComponent<Image>();
eventTrigger = gameObject.GetComponent<EventTrigger>();
if (eventTrigger == null) eventTrigger = gameObject.AddComponent<EventTrigger>();
List<EventTrigger.Entry> entryList = eventTrigger.triggers;
if (entryList == null) entryList = new List<EventTrigger.Entry>();
EventTrigger.Entry tempEntry = new EventTrigger.Entry();
bool isExist = false;
for (int i = ; i < entryList.Count; i++)
{
if (entryList[i].eventID == EventTriggerType.PointerDown)
{
tempEntry = entryList[i];
isExist = true;
break;
}
}
tempEntry.callback.AddListener(OnClick);
if (!isExist)
{
tempEntry.eventID = EventTriggerType.PointerDown;
entryList.Add(tempEntry);
}
cSPathFindManager = GetComponentInParent<CSPathFindManager>();
} private void OnClick(BaseEventData arg0)
{
SetMoveState((EnumMoveState)SetNextType());
cSPathFindManager.ReSetPath();
} public int SetNextType()
{
int tempType = ((int)curMoveState + ) % ;
if (tempType < || tempType > ) tempType = ;
return tempType;
} // Update is called once per frame
void Update()
{ } public void SetMoveState(EnumMoveState state)
{
if (state != curMoveState)
{
ResetNodeInfo(curMoveState, true);
ResetNodeInfo(state,false);
curMoveState = state;
if (movePoint) movePoint.color = pointColorDic[state]; }
} public void ResetNodeInfo(EnumMoveState state,bool isDel = false)
{
switch (state)
{
case EnumMoveState.notmove:
if (isDel)
{
NotMoveNodeList.Remove(this);
}
else
{
if (NotMoveNodeList.Contains(this)) NotMoveNodeList.Add(this);
}
break;
case EnumMoveState.start:
if (isDel)
{
if (StartNode == this)
{
StartNode = null;
}
}
else
{
if (StartNode != this)
{
if(StartNode != null)StartNode.SetMoveState(EnumMoveState.moveing);
StartNode = this;
}
}
break;
case EnumMoveState.finial:
if (isDel)
{
if (FinialNode == this)
{
FinialNode = null;
}
}
else
{
if (FinialNode != this)
{
if (FinialNode != null) FinialNode.SetMoveState(EnumMoveState.moveing);
FinialNode = this;
}
}
break;
default:
break;
}
//Debug.LogErrorFormat("当前状态:{0}; 是否删除:{3} 起点:{1}; 终点:{2}",state,StartNode,FinialNode,isDel);
} public void SetNodeInfo(int _G,int _H)
{
FValue = _G + _H;
GValue = _G;
HValue = _H;
if (F) F.text = (_G + _H).ToString();
if (G) G.text = _G.ToString();
if (H) H.text = _H.ToString();
} internal void SetPos(int _x, int _y)
{
pos.x = _x;
pos.y = _y;
if (PosText) PosText.text = string.Format("{0},{1}", _x, _y);
} internal void SetIndex(int index)
{
Index = index;
if (IndexText) IndexText.text = string.Format("{0}", index);
}
}
CSNode
三、寻路算法
寻路算法逻辑都在CSPathFindManager_AStar
1.获取当前点cueNode
初始化当前点为出发点,之后为从搜索列表openNodeList中FValue(下面会有介绍)最小的节点,并为其设置当前索引值为父节点的索引值+1
2.获取FValue最小节点(GetMinNode)
列表排序,取第一个

3.获取当前点的子节点

设置当前点的UI表现为选中状态
遍历挑选出来的列表,将其UI表现设置为已挑选过状态
将当前点加入已挑选列表
获取当前点的子节点,删选掉已挑选过的节点,(在RemoveNotMoveNode里面处理不可走点的删选)
4.为上面获取到的子节点赋值
这块就是昨天我困扰很久的地方,下面是我曾踩过的坑
(1).在这里遍历子节点,为他们赋值GValue是相对于当前点的移动距离
(2).把所有子节点加入搜索列表,再为列表里面的所有节点赋值GValue为当前相对于当前点的移动距离
(3).把所有子节点加入搜索列表,再为列表里面的所有节点赋值GValue为当前相对于起点的移动距离
我这么修改是因为下面这种情况:

后来我改了第四种方式,并将获取G的方式改为子节点到中转点的G+中转点到起点的G:
(4). 在这里遍历子节点,判断如果自己点的父节点不存在或者父节点的索引值大于当前点的索引点,将当前点设为子节点的当前父节点,为他们赋值GValue是当前点为中转点的移动距离

结果一样,因为这里索引值是相同的,于是我又加了=
(5). 在这里遍历子节点,判断如果自己点的父节点不存在或者父节点的索引值大于或等于当前点的索引点,将当前点设为子节点的当前父节点,为他们赋值GValue是当前点为中转点的移动距离

就在我以为没问题的时候 :>

在我一步一步看选点顺序的时候,发现也没毛病,但总有哪里不对劲 :>
后来我发现,他是在搜索点里面去找F值最小的,但是主要就是在第5遍修改后加的==判断,当索引一样的时候,该子节点在当前点的斜方向上,G值可能会更大
(6).在这里遍历子节点,判断如果自己点的父节点不存在或者父节点的索引值大于当前点的索引点,将当前点设为子节点的当前父节点,为他们赋值GValue是当前点为中转点的移动距离;而当索引值相等时,比较分别以当前点和父节点为中转点的G值

nice!!!! :>

====================================================================================
我估计上面几种困扰可能也是我理解不够造成的,走过路过的还请不吝赐教。:>
[A*算法]基于Unity实现A*算法(二)的更多相关文章
- 简单易学的机器学习算法—基于密度的聚类算法DBSCAN
简单易学的机器学习算法-基于密度的聚类算法DBSCAN 一.基于密度的聚类算法的概述 我想了解下基于密度的聚类算法,熟悉下基于密度的聚类算法与基于距离的聚类算法,如K-Means算法之间的区别. ...
- 简单易学的机器学习算法——基于密度的聚类算法DBSCAN
一.基于密度的聚类算法的概述 最近在Science上的一篇基于密度的聚类算法<Clustering by fast search and find of density peaks> ...
- 素数测试算法(基于Miller-Rabin的MC算法) // Fermat素数测试法
在以往判断一个数n是不是素数时,我们都是采用i从2到sqrt(n)能否整除n.如果能整除,则n是合数;否则是素数.但是该算法的时间复杂度为O(sqrt(n)),当n较大时,时间性能很差,特别是在网络安 ...
- 基于改进人工蜂群算法的K均值聚类算法(附MATLAB版源代码)
其实一直以来也没有准备在园子里发这样的文章,相对来说,算法改进放在园子里还是会稍稍显得格格不入.但是最近邮箱收到的几封邮件让我觉得有必要通过我的博客把过去做过的东西分享出去更给更多需要的人.从论文刊登 ...
- A*寻路算法的探寻与改良(二)
A*寻路算法的探寻与改良(二) by:田宇轩 第二部分:这部分内容主要是使用C语言编程实现A*, ...
- 基于MapReduce的SimRank++算法研究与实现
一.算法应用背景 计算广告学(Computational Advertising)是一门广告营销科学,以追求广告投放的收益最大化为目标,重点解决用户与广告匹配的相关性和广告的竞价模型问题,涉及到自然语 ...
- 基于FPGA的Cordic算法实现
CORDIC(Coordinate Rotation Digital Computer)算法即坐标旋转数字计算方法,是J.D.Volder1于1959年首次提出,主要用于三角函数.双曲线.指数.对数的 ...
- Google Cardboard的九轴融合算法——基于李群的扩展卡尔曼滤波
Google Cardboard的九轴融合算法 --基于李群的扩展卡尔曼滤波 极品巧克力 前言 九轴融合算法是指通过融合IMU中的加速度计(三轴).陀螺仪(三轴).磁场计(三轴),来获取物体姿态的方法 ...
- [Python]基于K-Nearest Neighbors[K-NN]算法的鸢尾花分类问题解决方案
看了原理,总觉得需要用具体问题实现一下机器学习算法的模型,才算学习深刻.而写此博文的目的是,网上关于K-NN解决此问题的博文很多,但大都是调用Python高级库实现,尤其不利于初级学习者本人对模型的理 ...
随机推荐
- 15个有趣好玩的linux shell 命令
今天介绍一些有趣的linux shell命令,所有的命令都可以使用man + 命令名称 来查看完整的使用方法. 1,figlet 字符画 figlet 可以将英文字符串以字符画的形式输出: >& ...
- js的属性监听
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title ...
- material UI中withStyles和makeStyles的区别
在material UI中,withStyles和makeStyles是经常使用的两个用于封装样式的函数.对于刚使用material UI的开发者而言,可能不太清楚这两者的区别. 本文简要探究 ...
- 通过express框架为前端提供api(数据),妈妈再也不用担心后端不给我数据了~
个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! 首先,确保自己安装了express框架. 没有安装的同学可以参照下面这篇博 ...
- C语言入门经典题目及其答案
写在开始: 我叫风骨散人,名字的意思是我多想可以不低头的自由生活,可现实却不是这样.家境贫寒,总得向这个世界低头,所以我一直在奋斗,想改变我的命运给亲人好的生活,希望同样被生活绑架的你可以通过自己的努 ...
- CF思维联系– Codeforces-988C Equal Sums (哈希)
ACM思维题训练集合 You are given k sequences of integers. The length of the i-th sequence equals to ni. You ...
- 2019年广东工业大学腾讯杯新生程序设计竞赛(同步赛)E-缺席的神官
链接:https://ac.nowcoder.com/acm/contest/3036/E 来源:牛客网 题目描述 面前的巨汉,让我想起了多年前的那次,但这个巨汉身上散布着让人畏惧害怕的黑雾.即使看不 ...
- 使用Redis构建电商网站
涉及到的key: 1. login,hash结构,存储用户token与用户ID之间的映射. 2. recent_tokens,存储最近登陆用户token,zset结构 member: token,sc ...
- weak_ptr
#include <iostream> #include <memory> using namespace std; int main(int argc, char **arg ...
- 使用kubeadm部署k8s集群[v1.18.0]
使用kubeadm部署k8s集群 环境 IP地址 主机名 节点 10.0.0.63 k8s-master1 master1 10.0.0.63 k8s-master2 master2 10.0.0.6 ...