[Unity算法]A星寻路(一):基础版本
参考链接:
https://www.cnblogs.com/yangyxd/articles/5447889.html
一.原理
1.将场景简化,分割为一个个正方形格子,这些格子称之为节点(node),从一个节点到另一个节点的距离称之为代价(cost)。一个节点与水平/垂直方向的相邻节点的代价是1,与对角节点的代价是1.4。这里引用公式f = g + h,f表示该节点的总代价,g表示该节点与上一路径节点的代价,h表示该节点与目标节点的代价。
2.需要两个列表,开启列表(openList)和关闭列表(closeList)。开启列表用来记录需要考虑的节点,关闭列表用来记录不会再考虑的节点。
3.在开启列表中添加起始节点。
4.在开启列表中找到总代价最低的节点,然后从开启列表中移除该节点,从关闭列表中添加该节点,把与该节点相邻的可通行的节点添加到开启列表,并且更新这些节点的代价。
5.循环第四步,如果当前节点等于目标节点,则退出循环。
二.实现
FindWayNode.cs
using UnityEngine;
public class FindWayNode {
public bool isObstacle;//是否是障碍物
public Vector3 scenePos;//场景位置
public int x, y;//坐标
public int gCost;//与起始点的距离
public int hCost;//与目标点的距离
public int fCost {
get { return gCost + hCost; }
}//总距离
public FindWayNode parentNode;//父节点
public FindWayNode(bool isObstacle, Vector3 scenePos, int x, int y)
{
this.isObstacle = isObstacle;
this.scenePos = scenePos;
this.x = x;
this.y = y;
}
}
FindWayGrid.cs
using System.Collections.Generic;
using UnityEngine; public class FindWayGrid { public int width;//格子水平方向个数
public int height;//格子垂直方向个数
public float nodeLength;//格子长度 private FindWayNode[,] findWayNodes;//格子数组
private float halfNodeLength;//格子长度的一半
private Vector3 startPos;//场景坐标起点 public FindWayGrid(int width, int height, float nodeLength = 1f)
{
this.width = width;
this.height = height;
this.nodeLength = nodeLength; findWayNodes = new FindWayNode[width, height];
halfNodeLength = nodeLength / ;
startPos = new Vector3(-width / * nodeLength + halfNodeLength, , -height / * nodeLength + halfNodeLength); for (int x = ; x < width; x++)
{
for (int y = ; y < height; y++)
{
Vector3 pos = CoordinateToScenePos(x, y);
findWayNodes[x, y] = new FindWayNode(false, pos, x, y);
}
}
} //坐标转场景坐标
public Vector3 CoordinateToScenePos(int x, int y)
{
Vector3 pos = new Vector3(startPos.x + x * nodeLength, startPos.y, startPos.z + y * nodeLength);
return pos;
} //根据场景坐标获取节点
public FindWayNode GetNode(Vector3 pos)
{
int x = (int)(Mathf.RoundToInt(pos.x - startPos.x) / nodeLength);
int y = (int)(Mathf.RoundToInt(pos.z - startPos.z) / nodeLength);
x = Mathf.Clamp(x, , width - );
y = Mathf.Clamp(y, , height - );
return GetNode(x, y);
} //根据坐标获取节点
public FindWayNode GetNode(int x, int y)
{
return findWayNodes[x, y];
} //获取相邻节点列表
public List<FindWayNode> GetNearbyNodeList(FindWayNode node)
{
List<FindWayNode> list = new List<FindWayNode>();
for (int i = -; i <= ; i++)
{
for (int j = -; j <= ; j++)
{
if (i == && j == )
{
continue;
}
int x = node.x + i;
int y = node.y + j;
if (x >= && x < width && y >= && y < height)
{
list.Add(findWayNodes[x, y]);
}
}
}
return list;
} //获取两个节点之间的距离
int GetDistance(FindWayNode nodeA, FindWayNode nodeB)
{
int countX = Mathf.Abs(nodeA.x - nodeB.x);
int countY = Mathf.Abs(nodeA.y - nodeB.y);
if (countX > countY)
{
return * countY + * (countX - countY);
}
else
{
return * countX + * (countY - countX);
}
} //找出起点到终点的最短路径
public List<FindWayNode> FindWay(Vector3 startPos, Vector3 endPos)
{
FindWayNode startNode = GetNode(startPos);
FindWayNode endNode = GetNode(endPos); List<FindWayNode> openList = new List<FindWayNode>();
List<FindWayNode> closeList = new List<FindWayNode>();
openList.Add(startNode); while (openList.Count > )
{
FindWayNode nowNode = openList[]; //选择花费最低的
for (int i = ; i < openList.Count; i++)
{
if (openList[i].fCost <= nowNode.fCost &&
openList[i].hCost < nowNode.hCost)
{
nowNode = openList[i];
}
} openList.Remove(nowNode);
closeList.Add(nowNode); //找到目标节点
if (nowNode == endNode)
{
return GeneratePath(startNode, endNode);
} List<FindWayNode> nearbyNodeList = GetNearbyNodeList(nowNode);
for (int i = ; i < nearbyNodeList.Count; i++)
{
FindWayNode node = nearbyNodeList[i];
//如果是墙或者已经在关闭列表中
if (node.isObstacle || closeList.Contains(node))
{
continue;
}
//计算当前相邻节点与开始节点的距离
int gCost = nowNode.gCost + GetDistance(nowNode, node);
//如果距离更小,或者原来不在打开列表
if (gCost < node.gCost || !openList.Contains(node))
{
//更新与开始节点的距离
node.gCost = gCost;
//更新与结束节点的距离
node.hCost = GetDistance(node, endNode);
//更新父节点为当前选定的节点
node.parentNode = nowNode;
//加入到打开列表
if (!openList.Contains(node))
{
openList.Add(node);
}
}
}
} return null;
} //生成路径
public List<FindWayNode> GeneratePath(FindWayNode startNode, FindWayNode endNode)
{
List<FindWayNode> nodeList = new List<FindWayNode>();
if (endNode != null)
{
FindWayNode tempNode = endNode;
while (tempNode != startNode)
{
nodeList.Add(tempNode);
tempNode = tempNode.parentNode;
}
nodeList.Reverse();//反转路径
}
return nodeList;
}
}
TestFindWay.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic; public class TestFindWay : MonoBehaviour { public Transform startTra;//起点tra
public Transform endTra;//终点tra
public Transform floorTra;//地板tra public GameObject obstacleGridPrefab;//障碍物格子
public GameObject pathGridPrefab;//路径格子
public LayerMask obstacleLayer;//障碍物所在的层 private FindWayGrid findWayGrid; private GameObject obstacleRootGo;//障碍物格子的父go
private GameObject pathRootGo;//路径格子的父go
private List<GameObject> pathGridGoList;//路径格子go列表 void Start ()
{
MeshFilter meshFilter = floorTra.GetComponent<MeshFilter>();
int width = Mathf.CeilToInt(meshFilter.mesh.bounds.size.x) * (int)floorTra.localScale.x;
int height = Mathf.CeilToInt(meshFilter.mesh.bounds.size.z) * (int)floorTra.localScale.z; findWayGrid = new FindWayGrid(width, height); obstacleRootGo = new GameObject("ObstacleRoot");
pathRootGo = new GameObject("PathRoot");
pathGridGoList = new List<GameObject>(); ShowObstacle();
} void Update ()
{
List<FindWayNode> nodeList = findWayGrid.FindWay(startTra.position, endTra.position);
if (nodeList != null)
{
ShowPath(nodeList);
}
} //展示路径
public void ShowPath(List<FindWayNode> list)
{
for (int i = ; i < list.Count; i++)
{
if (i < pathGridGoList.Count)
{
pathGridGoList[i].transform.position = list[i].scenePos;
pathGridGoList[i].SetActive(true);
}
else
{
GameObject go = Instantiate(pathGridPrefab);
go.transform.SetParent(pathRootGo.transform);
go.transform.position = list[i].scenePos;
pathGridGoList.Add(go);
}
} for (int i = list.Count; i < pathGridGoList.Count; i++)
{
pathGridGoList[i].SetActive(false);
}
} //展示障碍物
public void ShowObstacle()
{
int width = findWayGrid.width;
int height = findWayGrid.height;
float halfNodeLength = findWayGrid.nodeLength / ; for (int x = ; x < width; x++)
{
for (int y = ; y < height; y++)
{
FindWayNode node = findWayGrid.GetNode(x, y);
bool isObstacle = Physics.CheckSphere(node.scenePos, halfNodeLength, obstacleLayer);
if (isObstacle)
{
GameObject go = GameObject.Instantiate(obstacleGridPrefab, node.scenePos, Quaternion.identity) as GameObject;
go.transform.SetParent(obstacleRootGo.transform);
}
node.isObstacle = isObstacle;
}
}
}
}
三.使用
把TestFindWay.cs挂上,然后对public变量进行拖拽赋值即可。效果如下:

[Unity算法]A星寻路(一):基础版本的更多相关文章
- 基于Unity的A星寻路算法(绝对简单完整版本)
前言 在上一篇文章,介绍了网格地图的实现方式,基于该文章,我们来实现一个A星寻路的算法,最终实现的效果为: 项目源码已上传Github:AStarNavigate 在阅读本篇文章,如果你对于里面提到的 ...
- A星寻路算法入门(Unity实现)
最近简单学习了一下A星寻路算法,来记录一下.还是个萌新,如果写的不好,请谅解.Unity版本:2018.3.2f1 A星寻路算法是什么 游戏开发中往往有这样的需求,让玩家控制的角色自动寻路到目标地点, ...
- A星寻路算法介绍
你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢? 如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! 在网上已经有很多篇关于A星寻路算法 ...
- 用简单直白的方式讲解A星寻路算法原理
很多游戏特别是rts,rpg类游戏,都需要用到寻路.寻路算法有深度优先搜索(DFS),广度优先搜索(BFS),A星算法等,而A星算法是一种具备启发性策略的算法,效率是几种算法中最高的,因此也成为游戏中 ...
- A星寻路算法
A星寻路算法 1.准备一个close关闭列表(存放已被检索的点),一个open开启列表(存放未被检索的点),一个当前点的对象cur 2.将cur设成开始点 3.从cur起,将cur点放入close表中 ...
- cocos2d-x学习日志(13) --A星寻路算法demo
你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢?如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! A星算法简介: A*搜寻算法俗称A星 ...
- 无递归 A星寻路算法
整理硬盘的时候,发现我早些年写的A星寻路算法.特放上来,待有缘人拿去,无递归噢,性能那是杠杠的. 码上伺候 public class Node { public int X { get; set; } ...
- A星寻路算法(A* Search Algorithm)
你是否在做一款游戏的时候想创造一些怪兽或者游戏主角,让它们移动到特定的位置,避开墙壁和障碍物呢? 如果是的话,请看这篇教程,我们会展示如何使用A星寻路算法来实现它! 在网上已经有很多篇关于A星寻路算法 ...
- A星寻路算法-Mind&Hand(C++)
//注1:Mind & Hand,MIT校训,这里指的理解与实现(动脑也动手) //注2:博文分为两部分:(1)理解部分,为参考其他优秀博文的摘要梳理:(2)代码部分,是C++代码实现的,源码 ...
随机推荐
- 【转】基于keras 的神经网络股价预测模型
from matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, MONDAY,YEARLY from matplotli ...
- JSON C# Class Generator ---由json字符串生成C#实体类的工具
json作为互联网上轻量便捷的数据传输格式,越来越受到重视.但在服务器端编程过程中,我们常常希望能通过智能提示来提高编码效率.JSON C# Class Generator 能将json格式所表示的J ...
- RedHat7.3安装MySQL5.7
1.下载Mysql安装包 https://cdn.mysql.com//Downloads/MySQL-5.7/mysql-5.7.20-1.el6.x86_64.rpm-bundle.tar 附百度 ...
- 把指定的字符串翻译成 pig latin。
freecodecamp上的算法题: 把指定的字符串翻译成 pig latin. Pig Latin 把一个英文单词的第一个辅音或辅音丛(consonant cluster)移到词尾,然后加上后缀 & ...
- Thinkphp 缓存和静态缓存局部缓存设置
1.S方法缓存设置 if(!$rows = S('indexBlog')){ //*$rows = S('indexBlog') $rows = D('blog')->select(); S(' ...
- 修改machine.config遇到System.Net.ServicePointManager 的类型初始值设定项引发异常
<system.net>节点应该在</configuration>上面添加,即config页尾. 而不是在<configuration> 后面添加. 在</s ...
- 关于java多线程理解到集群分布式和网络设计的浅析
对于JAVA多线程的应用非常广泛,现在的系统没有多线程几乎什么也做不了,很多时候我们在何种场合如何应用多线程成为一种首先需要选择的问题, 另外关于java多线程的知识也是非常的多,本文中先介绍和说明一 ...
- C++11--编译器生成的函数
using namespace std; class Dog {}; /* C++ 03 * 1 默认构造函数(只有当用户没有声明任何构造函数) * 2 拷贝构造(只有当用户没有声明5,6),扩展到C ...
- bzoj4940: [Ynoi2016]这是我自己的发明
用dfs序把询问表示成询问dfs序的两个区间中的信息 拆成至多9个询问(询问dfs序的两个前缀),对这些询问用莫队处理,时间复杂度$O(n\sqrt{m})$ #include<bits/std ...
- 服务容错保护断路器Hystrix之六:缓存功能的使用
高并发环境下如果能处理好缓存就可以有效的减小服务器的压力,Java中有许多非常好用的缓存工具,比如Redis.EHCache等,当然在Spring Cloud的Hystrix中也提供了请求缓存的功能, ...