[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++代码实现的,源码 ...
随机推荐
- 【java】注释
一.注释 1. 注释类型 [a]. 单行注释 // 单行注释 [b]. 多行注释 /* 多行注释 */ [c]. 文档注释 /** 文档注释 */ 一般情况下,需求,实现方式用多行注释,类和方法上 ...
- FastReport问题整理(http://129.sqdj.gov.cn/?p=77)
1.FastReport中如果访问报表中的对象?可以使用FindObject方法.TfrxMemoView(frxReport1.FindObject(’memo1′)).Text:=’FastRep ...
- PerformEraseBackground 擦除背景(ThemeServices)
PerformEraseBackground 擦除背景的简单方法(外带ThemeServices例子) 在查这个函数的时候,顺便看到了有趣的代码. 怎么使用 Themes . unit Unit2; ...
- 《Java并发编程实战》笔记-非阻塞算法
如果在某种算法中,一个线程的失败或挂起不会导致其他线程也失败和挂起,那么这种算法就被称为非阻塞算法.如果在算法的每个步骤中都存在某个线程能够执行下去,那么这种算法也被称为无锁(Lock-Free)算法 ...
- 面向对象php 接口 抽象类
1.定义类和实例化对象: 使用关键字class定义类,使用new实例化对象: 2.类成员的添加和访问: 类成员:有属性,方法,常量(常量名不带$符): 访问属性的时候,变量名不带$符 添加属性需要使用 ...
- MySQL binlog 企业案例升级版
需求:1.创建一个数据库 oldboy2.在oldboy下创建一张表t13.插入5行任意数据4.全备5.插入两行数据,任意修改3行数据,删除1行数据6.删除所有数据7.再t1中又插入5行新数据,修改3 ...
- [UE4]显示队友
- 关于Jedis连接Linux上的redis出现 DENIED Redis is running in protected mode问题的解决方案
redis 添加了requirepass 123456后还是报错,原来是重新启动的时候./redis-cli 没有指定配置文件. https://blog.csdn.net/a532672728/a ...
- bootstraptable学习(2)分页
1.分页需要配置一些参数 function init() { $('#bootstrapModel').bootstrapTable({ url: "../Listing.ashx" ...
- 游戏设计思考:对COK的理解和思考
转自:http://www.gameres.com/804983.html 一.前言 发此文的起因是最近加入了一个游戏研究群,受到大家对游戏研究热情的感染,也想将自己对游戏的理解和感悟发出来和大家一起 ...