AStar寻路算法示例
概述
AStar算法是一种图形搜索算法,常用于寻路。他是以广度优先搜索为基础,集Dijkstra算法和最佳优先(best fit)于一身的一种算法。
示例1:4向
示例2:8向
思路
递归的通过估值函数找到最佳路径,估值函数与距离相关,也有可能与通过代价系数相关(例如平地系数为1,坡地系数为2),有三个参数:
- G:起点点到当前点的代价
- H: 当前点到终点的代价
- F: F = G + H 与最佳路径权重负相关的参数
代码示例
位置定义
public struct Vec2
{
public int x;
public int y;
public Vec2(int x, int y)
{
this.x = x;
this.y = y;
}
public static Vec2 Zero
{
get
{
return new Vec2(0, 0);
}
}
public override bool Equals(object obj)
{
if (!(obj is Vec2))
return false;
var o = (Vec2)obj;
return x == o.x && y == o.y;
}
public override int GetHashCode()
{
return x.GetHashCode() + y.GetHashCode();
}
public static Vec2 operator +(Vec2 a, Vec2 b)
{
return new Vec2(a.x + b.x, a.y + b.y);
}
public static Vec2 operator *(Vec2 a, int n)
{
return new Vec2(a.x * n, a.y * n);
}
public static Vec2 operator *(int n, Vec2 a)
{
return new Vec2(a.x * n, a.y * n);
}
public static bool operator ==(Vec2 a, Vec2 b)
{
return a.x == b.x && a.y == b.y;
}
public static bool operator !=(Vec2 a, Vec2 b)
{
return !(a.x == b.x && a.y == b.y);
}
}
方向定义
public enum EDir
{
Up = 0,
Down = 1,
Left = 2,
Right = 3,
UpLeft = 4,
UpRight = 5,
DownLeft = 6,
DownRight = 7,
}
public abstract class CheckDirPol
{
abstract public Dictionary<EDir, Vec2> GetDir();
}
public class CheckDir4Pol : CheckDirPol
{
private Dictionary<EDir, Vec2> dirDict = new Dictionary<EDir, Vec2>
{
{EDir.Up, new Vec2(0, 1) },
{EDir.Down, new Vec2(0, -1) },
{EDir.Left, new Vec2(-1, 0) },
{EDir.Right, new Vec2(1, 0) },
};
override public Dictionary<EDir, Vec2> GetDir()
{
return dirDict;
}
}
public class CheckDir8Pol : CheckDirPol
{
private Dictionary<EDir, Vec2> dirDict = new Dictionary<EDir, Vec2>
{
{EDir.Up, new Vec2(0, 1) },
{EDir.Down, new Vec2(0, -1) },
{EDir.Left, new Vec2(-1, 0) },
{EDir.Right, new Vec2(1, 0) },
{EDir.UpLeft, new Vec2(-1, 1) },
{EDir.UpRight, new Vec2(1, 1) },
{EDir.DownLeft, new Vec2(-1, -1) },
{EDir.DownRight, new Vec2(1, -1) },
};
override public Dictionary<EDir, Vec2> GetDir()
{
return dirDict;
}
}
- 运用策略模式的技巧,以实现4向,8向搜索切换
估值函数
public abstract class EvaPol
{
abstract public float Calc(Vec2 a, Vec2 b);
}
public class MANEvaPol : EvaPol
{
override public float Calc(Vec2 a, Vec2 b)
{
return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y);
}
}
- 直接使用曼哈顿距离作为代价
节点定义
public class Node
{
public int id;
public Vec2 pos;
public float g;
public float h;
public float f;
public Vec2 prePos;
public bool hasPrePos;
public Node(Vec2 pos)
{
this.pos = pos;
}
public void SetPrePos(Vec2 pos)
{
prePos = pos;
hasPrePos = true;
}
}
算法上下文定义
Context context;
EvaPol disPol;
CheckDirPol checkDirPol;
public struct Context
{
public Vec2 end;
public Vec2 start;
public Node[,] nodes;
public List<Node> open;
public List<Node> close;
public int[,] map;
public List<Vec2> result;
public Vec2 size;
}
寻路算法
初始化
public void Init(Vec2 start, Vec2 end, int[,] map)
{
var x = map.GetLength(0);
var y = map.GetLength(1);
context = new Context()
{
start = start,
end = end,
open = new List<Node>(),
close = new List<Node>(),
map = map,
result = new List<Vec2>(),
size = new Vec2(x, y),
};
context.nodes = new Node[x, y];
for (int i = 0; i < x; i++)
for (int j = 0; j < x; j++)
context.nodes[i, j] = new Node(new Vec2(i, j));
disPol = new MANEvaPol();
//checkDirPol = new CheckDir4Pol();
checkDirPol = new CheckDir8Pol();
}
获取路径
public List<Vec2> GetResult()
{
return context.result;
}
寻路
寻路入口
public void FindPath()
{
var s = context.start;
var sn = context.nodes[s.x, s.y];
sn.g = 0;
sn.h = disPol.Calc(s, context.end);
sn.f = sn.g + sn.h;
context.open.Add(sn);
FindArrangement(sn);
}
递归函数
void FindArrangement(Node node)
{
context.close.Add(node);
context.open.Remove(node);
if (node.pos == context.end)
{
SetResult(node);
return;
}
CheckRound(node);
if (context.open.Count == 0)
return;
Node next = context.open[0];
for (int i = 1; i < context.open.Count; i++)
if (context.open[i].f < next.f)
next = context.open[i];
FindArrangement(next);
}
检查周围节点
void CheckRound(Node node)
{
var dirDict = checkDirPol.GetDir();
foreach (var pair in dirDict)
{
var dir = node.pos + pair.Value;
if (IsBlock(dir))
continue;
var dn = context.nodes[dir.x, dir.y];
if (context.close.Contains(dn))
continue;
if (context.open.Contains(dn))
TryOverridePath(node, dn);
else
{
dn.g = disPol.Calc(dn.pos, context.start);
dn.h = disPol.Calc(dn.pos, context.end);
dn.f = dn.g + dn.h;
dn.SetPrePos(node.pos);
context.open.Add(dn);
}
}
}
// 若是从邻节点到该节点路径更优,则替换更新
void TryOverridePath(Node a, Node b)
{
var g = a.g + disPol.Calc(a.pos, b.pos);
if (g < b.g)
{
b.g = g;
b.SetPrePos(a.pos);
}
}
bool IsBlock(Vec2 pos)
{
return !InMap(pos) || context.map[pos.x, pos.y] == 1;
}
bool InMap(Vec2 pos)
{
var x = pos.x;
var y = pos.y;
var size = context.size;
return x >= 0 && x < size.x && y >= 0 && y < size.y;
}
生成路径
void SetResult(Node node)
{
Queue<Node> q = new Queue<Node>();
while(node.hasPrePos)
{
q.Enqueue(node);
node = context.nodes[node.prePos.x, node.prePos.y];
}
while(q.Count > 0)
{
context.result.Add(q.Dequeue().pos);
}
}
完整代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public struct Vec2
{
public int x;
public int y;
public Vec2(int x, int y)
{
this.x = x;
this.y = y;
}
public static Vec2 Zero
{
get
{
return new Vec2(0, 0);
}
}
public override bool Equals(object obj)
{
if (!(obj is Vec2))
return false;
var o = (Vec2)obj;
return x == o.x && y == o.y;
}
public override int GetHashCode()
{
return x.GetHashCode() + y.GetHashCode();
}
public static Vec2 operator +(Vec2 a, Vec2 b)
{
return new Vec2(a.x + b.x, a.y + b.y);
}
public static Vec2 operator *(Vec2 a, int n)
{
return new Vec2(a.x * n, a.y * n);
}
public static Vec2 operator *(int n, Vec2 a)
{
return new Vec2(a.x * n, a.y * n);
}
public static bool operator ==(Vec2 a, Vec2 b)
{
return a.x == b.x && a.y == b.y;
}
public static bool operator !=(Vec2 a, Vec2 b)
{
return !(a.x == b.x && a.y == b.y);
}
}
public enum EDir
{
Up = 0,
Down = 1,
Left = 2,
Right = 3,
UpLeft = 4,
UpRight = 5,
DownLeft = 6,
DownRight = 7,
}
public class AstarFindPath
{
public class Node
{
public int id;
public Vec2 pos;
public float g;
public float h;
public float f;
public Vec2 prePos;
public bool hasPrePos;
public Node(Vec2 pos)
{
this.pos = pos;
}
public void SetPrePos(Vec2 pos)
{
prePos = pos;
hasPrePos = true;
}
}
public abstract class EvaPol
{
abstract public float Calc(Vec2 a, Vec2 b);
}
public class MANEvaPol : EvaPol
{
override public float Calc(Vec2 a, Vec2 b)
{
return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y);
}
}
public abstract class CheckDirPol
{
abstract public Dictionary<EDir, Vec2> GetDir();
}
public class CheckDir4Pol : CheckDirPol
{
private Dictionary<EDir, Vec2> dirDict = new Dictionary<EDir, Vec2>
{
{EDir.Up, new Vec2(0, 1) },
{EDir.Down, new Vec2(0, -1) },
{EDir.Left, new Vec2(-1, 0) },
{EDir.Right, new Vec2(1, 0) },
};
override public Dictionary<EDir, Vec2> GetDir()
{
return dirDict;
}
}
public class CheckDir8Pol : CheckDirPol
{
private Dictionary<EDir, Vec2> dirDict = new Dictionary<EDir, Vec2>
{
{EDir.Up, new Vec2(0, 1) },
{EDir.Down, new Vec2(0, -1) },
{EDir.Left, new Vec2(-1, 0) },
{EDir.Right, new Vec2(1, 0) },
{EDir.UpLeft, new Vec2(-1, 1) },
{EDir.UpRight, new Vec2(1, 1) },
{EDir.DownLeft, new Vec2(-1, -1) },
{EDir.DownRight, new Vec2(1, -1) },
};
override public Dictionary<EDir, Vec2> GetDir()
{
return dirDict;
}
}
public struct Context
{
public Vec2 end;
public Vec2 start;
public Node[,] nodes;
public List<Node> open;
public List<Node> close;
public int[,] map;
public List<Vec2> result;
public Vec2 size;
}
Context context;
EvaPol disPol;
CheckDirPol checkDirPol;
public void Init(Vec2 start, Vec2 end, int[,] map)
{
var x = map.GetLength(0);
var y = map.GetLength(1);
context = new Context()
{
start = start,
end = end,
open = new List<Node>(),
close = new List<Node>(),
map = map,
result = new List<Vec2>(),
size = new Vec2(x, y),
};
context.nodes = new Node[x, y];
for (int i = 0; i < x; i++)
for (int j = 0; j < x; j++)
context.nodes[i, j] = new Node(new Vec2(i, j));
disPol = new MANEvaPol();
//checkDirPol = new CheckDir4Pol();
checkDirPol = new CheckDir8Pol();
}
public void FindPath()
{
var s = context.start;
var sn = context.nodes[s.x, s.y];
sn.g = 0;
sn.h = disPol.Calc(s, context.end);
sn.f = sn.g + sn.h;
context.open.Add(sn);
FindArrangement(sn);
}
public List<Vec2> GetResult()
{
return context.result;
}
void FindArrangement(Node node)
{
context.close.Add(node);
context.open.Remove(node);
if (node.pos == context.end)
{
SetResult(node);
return;
}
CheckRound(node);
if (context.open.Count == 0)
return;
Node next = context.open[0];
for (int i = 1; i < context.open.Count; i++)
if (context.open[i].f < next.f)
next = context.open[i];
FindArrangement(next);
}
void SetResult(Node node)
{
Queue<Node> q = new Queue<Node>();
while(node.hasPrePos)
{
q.Enqueue(node);
node = context.nodes[node.prePos.x, node.prePos.y];
}
while(q.Count > 0)
{
context.result.Add(q.Dequeue().pos);
}
}
void CheckRound(Node node)
{
var dirDict = checkDirPol.GetDir();
foreach (var pair in dirDict)
{
var dir = node.pos + pair.Value;
if (IsBlock(dir))
continue;
var dn = context.nodes[dir.x, dir.y];
if (context.close.Contains(dn))
continue;
if (context.open.Contains(dn))
TryOverridePath(node, dn);
else
{
dn.g = disPol.Calc(dn.pos, context.start);
dn.h = disPol.Calc(dn.pos, context.end);
dn.f = dn.g + dn.h;
dn.SetPrePos(node.pos);
context.open.Add(dn);
}
}
}
void TryOverridePath(Node a, Node b)
{
var g = a.g + disPol.Calc(a.pos, b.pos);
if (g < b.g)
{
b.g = g;
b.SetPrePos(a.pos);
}
}
bool IsBlock(Vec2 pos)
{
return !InMap(pos) || context.map[pos.x, pos.y] == 1;
}
bool InMap(Vec2 pos)
{
var x = pos.x;
var y = pos.y;
var size = context.size;
return x >= 0 && x < size.x && y >= 0 && y < size.y;
}
}
AStar寻路算法示例的更多相关文章
- 算法:Astar寻路算法改进,双向A*寻路算法
早前写了一篇关于A*算法的文章:<算法:Astar寻路算法改进> 最近在写个js的UI框架,顺便实现了一个js版本的A*算法,与之前不同的是,该A*算法是个双向A*. 双向A*有什么好处呢 ...
- C#实现AStar寻路算法
AStar寻路算法是一种在一个静态路网中寻找最短路径的算法,也是在游戏开发中最常用到的寻路算法之一:最近刚好需要用到寻路算法,因此把自己的实现过程记录下来. 先直接上可视化之后的效果图,图中黑色方格代 ...
- 算法:Astar寻路算法改进
早前写了一篇<RCP:gef智能寻路算法(A star)> 出现了一点问题. 在AStar算法中,默认寻路起点和终点都是N x N的方格,但如果用在路由上,就会出现问题. 如果,需要连线的 ...
- 一个高效的A-star寻路算法(八方向)(
这种写法比较垃圾,表现在每次搜索一个点要遍历整个地图那么大的数组,如果地图为256*256,每次搜索都要执行65535次,如果遍历多个点就是n*65535,速度上实在是太垃圾了 简单说下思路,以后补充 ...
- javascript 实现 A-star 寻路算法
在游戏开发中,又一个很常见的需求,就是让一角色从A点走到B点,而我们期望所走的路是最短的,最容易想到的就是两点之间直线最短,我们可以通过勾股定理来求出两点之间的距离,但这个情况只能用于两点之间没有障碍 ...
- 对A-Star寻路算法的粗略研究
首先来看看完成后的效果: 其中灰色代表路障,绿色是起点和移动路径,红色代表终点 // = openArray[i+1].F) { minNode = openArray[i+1]; } } sta ...
- javascript的Astar版 寻路算法
去年做一个模仿保卫萝卜的塔防游戏的时候,自己写的,游戏框架用的是coco2d-html5 实现原理可以参考 http://www.cnblogs.com/technology/archive/2011 ...
- A*寻路算法的探寻与改良(三)
A*寻路算法的探寻与改良(三) by:田宇轩 第三分:这部分内容基于树.查找算法等对A*算法的执行效率进行了改良,想了解细 ...
- A星寻路算法入门(Unity实现)
最近简单学习了一下A星寻路算法,来记录一下.还是个萌新,如果写的不好,请谅解.Unity版本:2018.3.2f1 A星寻路算法是什么 游戏开发中往往有这样的需求,让玩家控制的角色自动寻路到目标地点, ...
- [转] A*寻路算法C++简单实现
参考文章: http://www.policyalmanac.org/games/aStarTutorial.htm 这是英文原文<A*入门>,最经典的讲解,有demo演示 http: ...
随机推荐
- 《吐血整理》高级系列教程-吃透Fiddler抓包教程(26)-Fiddler如何抓取Android7.0以上的Https包-上篇
1.简介 众所周知,假如设备是android 7.0+的系统同时应用设置targetSdkVersion >= 24的话,那么应用默认是不信任安装的Fiddler用户证书的,所以你就没法抓到应用 ...
- csv2ECharts,**一行命令查看数据趋势图 工具分享**
csv2ECharts 一行命令查看数据趋势图! 联系:luomgf@163.com,欢迎交流提出建议 只有一个文件,基于shell,实现将CSV格式数据转化为数据图.运维中尝尝需要查看某个监控指标的 ...
- KNN算法介绍及源码实现
一.KNN算法介绍 邻近算法,或者说K最邻近(KNN,K-NearestNeighbor)分类算法是数据挖掘分类技术中最简单的方法之一.所谓K最近邻,就是K个最近的邻居的意思,说的是每个样本都可以用它 ...
- 从SpringBoot启动,阅读源码设计
目录 一.背景说明 二.SpringBoot工程 三.应用上下文 四.资源加载 五.应用环境 六.Bean对象 七.Tomcat服务 八.事件模型 九.配置加载 十.数据库集成 十一.参考源码 服务启 ...
- Linux 下模拟制作块设备并挂载
Linux 下模拟制作块设备并挂载 作者:Grey 原文地址: 博客园:Linux 下模拟制作块设备并挂载 CSDN:Linux 下模拟制作块设备并挂载 环境 CentOS-7 下载地址:下载 Cen ...
- Mysql主从配置步骤与各种错误
测试环境: 2台腾讯云服务器.CentOS 7.2 64位,1G,lnmp. PHP:5.6:Mysql:5.5 两台干净的服务器 下面开始配置主服务器(master) 1.修改配置: log-bi ...
- 一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)
这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1.简述MySQL中索引类型对数据库的性能的影响 2.RDB和AOF机制 3.Redis的过期键的删除策略 4.Redis ...
- Tensorflow Lite从入门到精通
TensorFlow Lite 是 TensorFlow 在移动和 IoT 等边缘设备端的解决方案,提供了 Java.Python 和 C++ API 库,可以运行在 Android.iOS 和 Ra ...
- CSS 渐变锯齿消失术
在 CSS 中,渐变(Gradient)可谓是最为强大的一个属性之一. 但是,经常有同学在使用渐变的过程中会遇到渐变图形产生的锯齿问题. 何为渐变锯齿? 那么,什么是渐变图形产生的锯齿呢? 简单的一个 ...
- git 多个commit 如何合并
git 多个commit 如何合并 本篇主要介绍一下 git 中多个commit 如何合并, 因为commit 太多 会导致提交记录混乱, 所以有时候会把多个commit 合并成一个 保持提交记录干净 ...