写在前面:上一篇当时是非常简单的了解一下A*,昨天还有一些问题没解决,就暂时把自己查阅的文坛摘抄了过来(毕竟人家写的比我要好的多 :> )

今天终于解决了,就又写了这一篇,正好我自己再梳理一遍,把Unity的实现也记录一下(Unity版本:2019.3.7.f1)

====================================================================================

  • 一、Unity UI表现实现

  UI制作很简单,如下图

  

  1. 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*算法(二)的更多相关文章

  1. 简单易学的机器学习算法—基于密度的聚类算法DBSCAN

    简单易学的机器学习算法-基于密度的聚类算法DBSCAN 一.基于密度的聚类算法的概述 我想了解下基于密度的聚类算法,熟悉下基于密度的聚类算法与基于距离的聚类算法,如K-Means算法之间的区别.    ...

  2. 简单易学的机器学习算法——基于密度的聚类算法DBSCAN

    一.基于密度的聚类算法的概述     最近在Science上的一篇基于密度的聚类算法<Clustering by fast search and find of density peaks> ...

  3. 素数测试算法(基于Miller-Rabin的MC算法) // Fermat素数测试法

    在以往判断一个数n是不是素数时,我们都是采用i从2到sqrt(n)能否整除n.如果能整除,则n是合数;否则是素数.但是该算法的时间复杂度为O(sqrt(n)),当n较大时,时间性能很差,特别是在网络安 ...

  4. 基于改进人工蜂群算法的K均值聚类算法(附MATLAB版源代码)

    其实一直以来也没有准备在园子里发这样的文章,相对来说,算法改进放在园子里还是会稍稍显得格格不入.但是最近邮箱收到的几封邮件让我觉得有必要通过我的博客把过去做过的东西分享出去更给更多需要的人.从论文刊登 ...

  5. A*寻路算法的探寻与改良(二)

    A*寻路算法的探寻与改良(二) by:田宇轩                                                     第二部分:这部分内容主要是使用C语言编程实现A*, ...

  6. 基于MapReduce的SimRank++算法研究与实现

    一.算法应用背景 计算广告学(Computational Advertising)是一门广告营销科学,以追求广告投放的收益最大化为目标,重点解决用户与广告匹配的相关性和广告的竞价模型问题,涉及到自然语 ...

  7. 基于FPGA的Cordic算法实现

    CORDIC(Coordinate Rotation Digital Computer)算法即坐标旋转数字计算方法,是J.D.Volder1于1959年首次提出,主要用于三角函数.双曲线.指数.对数的 ...

  8. Google Cardboard的九轴融合算法——基于李群的扩展卡尔曼滤波

    Google Cardboard的九轴融合算法 --基于李群的扩展卡尔曼滤波 极品巧克力 前言 九轴融合算法是指通过融合IMU中的加速度计(三轴).陀螺仪(三轴).磁场计(三轴),来获取物体姿态的方法 ...

  9. [Python]基于K-Nearest Neighbors[K-NN]算法的鸢尾花分类问题解决方案

    看了原理,总觉得需要用具体问题实现一下机器学习算法的模型,才算学习深刻.而写此博文的目的是,网上关于K-NN解决此问题的博文很多,但大都是调用Python高级库实现,尤其不利于初级学习者本人对模型的理 ...

随机推荐

  1. Windows 版本 Enterprise、Ultimate、Home、Professional

    关于Windows 的安装光盘版本很多种,很多人不知道选择哪些. Ultimate 旗舰版,VISTA开始有了这个级别,是最全最高级的,一般程序开发的电脑,玩游戏的电脑,建议用它,不过对配置稍有一些要 ...

  2. Java ASM3学习(3)

    MethodVisitor ClassVisitor的visitMethod能够访问到类中某个方法的一些入口信息,那么针对具体方法中字节码的访问是由MethodVisitor来进行的 访问顺序如下,其 ...

  3. js之用IndexOf返回指定字符串的次数

    代码 var Str = "strs,strs,stras,str,strs,strs"; var subStr ="strs" ; var count = 0 ...

  4. mysql备份及恢复

    第四章:MySQL数据库的备份与恢复                            2016-09-30 00:58:05 标签:数据库备份 工作原理 数据库表 mysql source 原创 ...

  5. 从零开始搭建口袋妖怪管理系统(4)-借助webpack4.6工程化项目(上)

    "手动是不可能手动的了,这辈子都不可能手动的了." 一.目标 上一章我们借助ngRoute,完成了口袋妖怪SPA系统的多模块导航开发,但是现在引用的东西越来越多,项目文件目录开始变 ...

  6. fullpage.js禁止滚动

    http://www.wenjiangs.com/doc/fullpage-method 转载于:https://www.cnblogs.com/hzz-/p/8268771.html

  7. 2019 ICPC 银川网络赛 H. Fight Against Monsters

    It is my great honour to introduce myself to you here. My name is Aloysius Benjy Cobweb Dartagnan Eg ...

  8. CF1316E Team Building

    CF1316E [Team Building] 状压dp,感觉比D简单 \(f[i][s]\),表示考虑前\(i\)个人,状态为\(s\)(\(s\)的第\(j-1\)个二进制位表示队员的第\(j\) ...

  9. Fiddler 弱网测试

    1.设置上传下载速率 在Fiddler Script选项中查找uploaded,找到设置网络上传和下载设置值 分析一下这几行代码: 首先来判断 m_SimulateModem 是否为 true,也就是 ...

  10. mui日期设置与时钟样式时间设置

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...