A star 寻路
大白话说一下几个点:
通俗的来说,其实就是以一个规则来 从A点走到B点。
怎么来判断我们走的格子是一个合适的格子?
就是靠一个规则来计算,这个规则就是估价函数。
估价函数:
常用:曼哈顿算法
F = G + H
G:表示从起点 A 移动到网格上指定方格的移动耗费 (可沿斜方向移动).
H:表示从指定的方格移动到终点 B 的预计耗费
G值的计算:
假定:左右上下方向的移动产生的耗费是10
那么在斜方向的移动耗费 就是 左右上下方向耗费的 根号2 倍,就是14咯
H值的计算:
(H 有很多计算方法, 这里我们设定只可以上下左右移动).
就是算是指定格子 到 终点格子的 横方向上的耗费+竖方向上的耗费。
开启列表:就是一个等待检查方格的列表.
关闭列表:存放不需要再次检查的方格的列表
伪代码:
把起始格添加到 “开启列表”
do
{
寻找开启列表中F值最低的格子, 我们称它为当前格.
把它切换到关闭列表.
对当前格相邻的8格中的每一个
if (它不可通过 || 已经在 “关闭列表” 中)
{
什么也不做.
}
if (它不在开启列表中)
{
把它添加进 “开启列表”, 把当前格作为这一格的父节点, 计算这一格的 FGH
}
if (它已经在开启列表中)
{
if (用G值为参考检查新的路径是否更好, 更低的G值意味着更好的路径)
{
把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值.
}
}
} while( 目标格已经在 “开启列表”, 这时候路径被找到)
如果开启列表已经空了, 说明路径不存在.
最后从终点开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径。
地图信息:
1,不可走
0,可走
在Unity里效果如下:
节点:
using UnityEngine;
using System.Collections; public class Node
{
public bool canWalk;//是否可以行走
public Vector3 worldPos;//节点的位置
public int gridX, gridY;//地形网格的下标 public int gCost;//离起始点的耗费
public int hCost;//离目标点的耗费
public int fCost { get { return gCost + hCost; } } public Node parent;//父对象 public Node(bool _canWalk, Vector3 _pos, int _x, int _y)
{
canWalk = _canWalk;
worldPos = _pos;
gridX = _x;
gridY = _y;
}
}
地图网格:
using UnityEngine;
using System.Collections;
using System.Collections.Generic; public class Grid : MonoBehaviour
{
public Node[,] grid;//网格,是Node节点的二维数组
public Vector2 gridSize;//网格的大小
public float nodeRadius;//节点的半径
private float nodeDiameter;//节点的直径 public LayerMask whatLayer;//是可走层还是不可走层 public int gridCountX, gridCountY;//每一行、列有几个Node public List<List<Node>> AllPath = new List<List<Node>>();//所有人的路径 void Start()
{
nodeDiameter = nodeRadius * ;
gridCountX = Mathf.RoundToInt(gridSize.x / nodeDiameter);
gridCountY = Mathf.RoundToInt(gridSize.y / nodeDiameter); grid = new Node[gridCountX, gridCountY]; CreateGrid();
} void CreateGrid()
{
//左下角
Vector3 startPoint = this.transform.position - gridSize.x / * Vector3.right - gridSize.y / * Vector3.forward; for (int i = ; i < gridCountX; i++)
{
for (int j = ; j < gridCountY; j++)
{
Vector3 worldPoint = startPoint + Vector3.right * (i * nodeDiameter + nodeRadius) + Vector3.forward * (j * nodeDiameter + nodeRadius);
bool walkable = !Physics.CheckSphere(worldPoint, nodeRadius *, whatLayer);//检测半径(直径)范围内是否可行走,发射球形射线检测层
grid[i, j] = new Node(walkable, worldPoint, i, j);
}
}
} void OnDrawGizmos()
{
//画地形网格边缘
Gizmos.DrawWireCube(transform.position, new Vector3(gridSize.x, , gridSize.y)); //画节点Node
if (grid == null) return;
foreach (var node in grid)
{
Gizmos.color = node.canWalk ? Color.white : Color.red;
Gizmos.DrawCube(node.worldPos, Vector3.one * (nodeDiameter - .1f));
}
//画角色
/* Node playerNode = GetFromPositon(player.position);
if (playerNode != null && playerNode.canWalk)
{
Gizmos.color = Color.yellow;
Gizmos.DrawCube(playerNode.worldPos, Vector3.one * (nodeDiameter - .1f));
}*/ //画路径
//if (path != null)
//{
// foreach (var node in path)
// {
// Gizmos.color = Color.black;
// Gizmos.DrawCube(node.worldPos, Vector3.one * (nodeDiameter - .1f));
// }
//} if (AllPath.Count > )
{
for (int i = ; i < AllPath.Count; i++)
{
if (AllPath[i].Count > )
{
foreach (var node in AllPath[i])
{
Gizmos.color = Color.black;
Gizmos.DrawCube(node.worldPos, Vector3.one * (nodeDiameter - .1f));
}
}
}
}
}
}
寻路:
using UnityEngine;
using System.Collections;
using System.Collections.Generic; public class FindPath
{
public Grid mapGrid;
private GameObject npc, target;
private List<Node> FinalPath = new List<Node>(); public List<Node> GetFinalPath(GameObject self, GameObject target)
{
FindingPath(self.transform.position, target.transform.position);
return FinalPath;
} public void FindingPath(Vector3 _startPos, Vector3 _endPos)
{
//Node startNode = mapGrid.GetFromPositon(_startPos);
//Node endNode = mapGrid.GetFromPositon(_endPos); Node startNode = GetFromPositon(_startPos);
Node endNode = GetFromPositon(_endPos); //开启列表
List<Node> openSet = new List<Node>(); //关闭列表
HashSet<Node> closeSet = new HashSet<Node>(); openSet.Add(startNode); while (openSet.Count > )
{
Node currentNode = openSet[]; for (int i = ; i < openSet.Count; i++)
{
if (openSet[i].fCost < currentNode.fCost && openSet[i].hCost < currentNode.hCost)
{
currentNode = openSet[i];
}
} openSet.Remove(currentNode);
closeSet.Add(currentNode); if (currentNode == endNode)
{
GeneratePath(startNode,endNode);
return;
} foreach (var node in GetNeibourhood(currentNode))
{
if (!node.canWalk || closeSet.Contains(node)) continue;
int newCost = currentNode.gCost + GetNodesDistance(currentNode,node);
if (newCost < node.gCost || !openSet.Contains(node))
{
node.gCost = newCost;
node.hCost = GetNodesDistance(node,endNode);
node.parent = currentNode;
if (!openSet.Contains(node))
{
openSet.Add(node);
}
}
}
}
}
private void GeneratePath(Node startNode,Node endNode)
{
List<Node> path = new List<Node>();
Node temp = endNode;
while (temp != startNode)
{
path.Add(temp);
temp = temp.parent;
}
path.Reverse();
FinalPath = path; mapGrid.AllPath.Add(FinalPath);
}
public int GetNodesDistance(Node a, Node b)
{
int countX = Mathf.Abs(a.gridX - b.gridX);
int countY = Mathf.Abs(a.gridY - b.gridY);
if (countX > countY)
{
return * countY + * (countX - countY);
}
else
{
return * countX + * (countY - countX);
}
} //角色在哪个节点之上
public Node GetFromPositon(Vector3 _PlayerPos)
{
float percentX = (_PlayerPos.x + mapGrid.gridSize.x / ) / mapGrid.gridSize.x;
float percentY = (_PlayerPos.z + mapGrid.gridSize.y / ) / mapGrid.gridSize.y; percentX = Mathf.Clamp01(percentX);
percentY = Mathf.Clamp01(percentY); int x = Mathf.RoundToInt((mapGrid.gridCountX - ) * percentX);
int y = Mathf.RoundToInt((mapGrid.gridCountY - ) * percentY); return mapGrid.grid[x, y];
} //获取节点周围的节点
public List<Node> GetNeibourhood(Node node)
{
List<Node> neribourhood = new List<Node>();
for (int i = -; i <= ; i++)
{
for (int j = -; j <= ; j++)
{
if (i == && j == )
{
continue;
}
int tempX = node.gridX + i;
int tempY = node.gridY + j;
if (tempX > && tempY > && tempX < mapGrid.gridCountX && tempY < mapGrid.gridCountY)
{
neribourhood.Add(mapGrid.grid[tempX, tempY]);
}
}
}
return neribourhood;
}
}
还有许多优化的地方:
1.多单位寻路
2.动态障碍
- 本文固定链接: http://www.shihuanjue.com/?p=103
- 转载请注明: 乔 2015年09月26日 于 是幻觉 发表
A star 寻路的更多相关文章
- 数据结构和算法总结(三):A* 寻路算法
前言 复习下寻路相关的东西,而且A star寻路在游戏开发中应用挺多的,故记录下. 正文 迪杰斯特拉算法 说起A*得先谈谈Dijkstra算法,它是在BFS基础上的一种带权值的两点最短寻路贪心算法. ...
- atc游戏bot
游戏玩起来太虐心了,就写了个bot来玩, 代码在此: git clone https://github.com/chenfengyuan/atc-bot-new.gitgit clone https: ...
- RCP:gef智能寻路算法(A star)
本路由继承自AbstactRouter,参数只有EditPart(编辑器内容控制器),gridLength(寻路用单元格大小),style(FLOYD,FLOYD_FLAT,FOUR_DIR). 字符 ...
- A*(也叫A star, A星)寻路算法Java版
寻路算法有非常多种,A*寻路算法被公觉得最好的寻路算法. 首先要理解什么是A*寻路算法,能够參考这三篇文章: http://www.gamedev.net/page/resources/_/techn ...
- 算法:Astar寻路算法改进
早前写了一篇<RCP:gef智能寻路算法(A star)> 出现了一点问题. 在AStar算法中,默认寻路起点和终点都是N x N的方格,但如果用在路由上,就会出现问题. 如果,需要连线的 ...
- 无递归 A星寻路算法
整理硬盘的时候,发现我早些年写的A星寻路算法.特放上来,待有缘人拿去,无递归噢,性能那是杠杠的. 码上伺候 public class Node { public int X { get; set; } ...
- 一种高效的寻路算法 - B*寻路算法
在此把这个算法称作B* 寻路算法(Branch Star 分支寻路算法,且与A*对应),本算法适用于游戏中怪物的自动寻路,其效率远远超过A*算法,经过测试,效率是普通A*算法的几十上百倍. 通过引入该 ...
- CritterAI与Recast Navigation寻路
版权声明:本文为博主吴欣伟原创文章,未经博主允许不得转载. 前言 这篇文章写于去年,由于工作需要,故写出这个研究文档,发现网上有关此寻路库的中文资源十分稀少,故发布出来与诸位共享交流,如文中有不对之处 ...
- A* Pathfinding Project (Unity A*寻路插件) 使用教程
Unity4.6 兴许版本号都已经内置了寻路AI了.之前的文章有介绍 Unity3d 寻路功能 介绍及项目演示 然而两年来项目中一直使用的是 A* Pathfinding 这个插件的.所以抽时间来写下 ...
随机推荐
- Python之路【第二十篇】其他WEB框架
WEB框架功能分析 WEB框架本质上,就是一个SOCKET Server WEB框架前面有WSGI或者是自己写的SOCKET,然后交给URL路由系统处理,然后交给某个函数或某个类,然后在模板里拿到模板 ...
- [NHibernate]HQL查询
目录 写在前面 文档与系列文章 查询的几种方式 HQL查询 一个例子 总结 写在前面 上篇文章介绍了nhibernate在项目中的基本配置,包括数据库连接字符串的设置,映射文件的配置及需注意的地方,这 ...
- Maven、SecureCRT使用问题汇集
1 Maven 无法下载pom文件中相关的依赖包 该问题可能有很多原因,我的原因是host中的localhost被修改了,改回来即可! 看起来好像出了一些网络原因的问题,顺着这个方向搜索,发现国外也有 ...
- Silicon C8051F340之时钟系统
一.背景 做个记录,以备下次快速开发. 二.正文 C8051F340有一个可编程内部高频振荡器.一个可编程内部低频振荡器.一个外部振荡器驱动电路 和一个4倍时钟乘法器.其中可编程内部高频振荡器在系统复 ...
- 微信服务号模板消息接口新增"设置行业"和"添加模板"及细节优化
微信服务号模板消息可以向用户发送重要的服务通知,如信用卡刷卡通知,商品购买成功通知等.昨日,微信团队发布公告称模板消息新增“设置行业”和“添加模板”接口及细节优化,详细变动如下 模板消息[业务通知]自 ...
- ORACLE "ORA--22992:无法使用远程表选择的LOB定位器,database link"
解决办法: 先创建一个临时表,然后把远程的含CLOB字段的表导入到临时表中,再倒入本表. create global temporary table demo_temp as select * ...
- ICMP的应用--Traceroute
Traceroute是用来侦测主机到目的主机之间所经路由情况的重要工具,也是最便利的工具.前面说到,尽管ping工具也可以进行侦测,但是,因为ip头的限制,ping不能完全的记录下所经过的路由器.所以 ...
- angularjs 笔记(1) -- 引导
首先: 1,引入angularJS文件,<script type="text/javascript" src="angularjs.min.js"> ...
- Java的内存机制
Java 把内存划分成两种:一种是栈内存,另一种是堆内存.在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空 ...
- java10
1:正则表达式(理解) (1)就是符合一定规则的字符串 (2)常见规则 A:字符 x 字符 x.举例:'a'表示字符a \\ 反斜线字符. \n 新行(换行)符 ('\u000A') \r 回车符 ( ...