主要参考:unity3D 简单实现A*算法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SimpleFrame;
public class GridManager : MonoSingleton<GridManager>
{
GridController gridController;
MapData curMapData;
GridItem[,] curGrids;
AStarNode[,] curNodes;
//可连接直线数量(直线行走,转弯次数)
int curLinkCount = 3;
GridItem firstGrid;
void Start()
{
gridController = new GridController();
}
//开始游戏
public void GameStart(int mapSize, int spriteStyle)
{
if(curMapData != null)
{
Debug.Log("Map Data Is Already Has");
return;
}
//获取地图数据
curMapData = DataManager.Instance.GetMapData(mapSize, spriteStyle);
//创建地图格子
curGrids = gridController.CreatGrids(curMapData);
//根据地图格子信息,创建A*算法所需的每个格子对应的数据
curNodes = new AStarNode[curGrids.GetLength(0), curGrids.GetLength(1)];
for (int i = 0; i < curGrids.GetLength(0); i++)
{
for (int j = 0; j < curGrids.GetLength(1); j++)
{
if (curGrids[i, j].data != null)
curNodes[i, j] = new AStarNode(curGrids[i, j].isActive, curGrids[i, j].data.index);
else
curNodes[i, j] = null;
}
}
firstGrid = null;
}
//结束游戏
public void GameEnd()
{
//回收地图格子
gridController.ResetGrids();
//清空数据
curMapData = null;
curGrids = null;
curNodes = null;
}
//格子点击时调用
public void OnClickGrid(GridItem clickGrid)
{
if (!clickGrid.isActive)
return;
//第一次点击
if (firstGrid == null)
{
firstGrid = clickGrid;
//高亮显示选中格子
firstGrid.HighLight();
return;
}
//两次点击同一个格子
if (firstGrid == clickGrid)
{
//取消高亮显示, 取消选中
firstGrid.CancelHighLight();
}
//点击到匹配的格子, 判断是否可消除
else if (CheckMoveToGrid(firstGrid, clickGrid))
{
//可移动
firstGrid.OnClickTrue();
clickGrid.OnClickTrue();
}
else
{
//不可移动
firstGrid.OnClickFalse();
clickGrid.OnClickFalse();
}
firstGrid = null;
}
// ------
bool CheckMoveToGrid(GridItem startGrid, GridItem endGrid)
{
//刷新当前格子节点数据
for (int i = 0; i < curGrids.GetLength(0); i++)
{
for (int j = 0; j < curGrids.GetLength(1); j++)
{
if (curNodes[i, j] != null)
curNodes[i, j].isWall = curGrids[i, j].isActive;
}
}
//计算路径
List<AStarNode> pathNodes = FindNodePath(curNodes[startGrid.data.index.x, startGrid.data.index.y], curNodes[endGrid.data.index.x, endGrid.data.index.y], curNodes);
if (pathNodes == null || pathNodes.Count == 0)
return false;
//计算路径折点
List<Vector2Int> pathPoints = GetPathPoint(pathNodes);
//计算折点的格子的世界坐标
List<Vector3> pathLines = new List<Vector3>();
for (int i = 0; i < pathPoints.Count; i++)
{
pathLines.Add(curGrids[pathPoints[i].x, pathPoints[i].y].transform.position);
}
//路径折点多余指定数量, 不可移动
if (pathPoints.Count - 1 > curLinkCount)
{
StartCoroutine(ShowClickLine(pathLines, false));
return false;
}
//可移动
StartCoroutine(ShowClickLine(pathLines, true));
return true;
}
//连线提示
IEnumerator ShowClickLine(List<Vector3> pathLines, bool success)
{
yield return null;
//effectController.ShowLinkLine(pathLines, success);
}
//计算路径折点
List<Vector2Int> tmpPointList = new List<Vector2Int>();
List<Vector2Int> GetPathPoint(List<AStarNode> path)
{
tmpPointList.Clear();
if (path.Count == 1)
return tmpPointList;
tmpPointList.Add(path[0].pos);
bool lastDirIsX = path[1].pos.x == path[0].pos.x;
for (int i = 2; i < path.Count; i++)
{
if (path[i].pos.x == path[i - 1].pos.x)
{
if (!lastDirIsX)
{
tmpPointList.Add(path[i - 1].pos);
lastDirIsX = true;
}
}
else
{
//(path[i].pos.x == path[i - 1].pos.x)
if (lastDirIsX)
{
tmpPointList.Add(path[i - 1].pos);
lastDirIsX = false;
}
}
}
tmpPointList.Add(path[path.Count - 1].pos);
return tmpPointList;
}
#region --- A*算法 ---
//计算最短有效路径
List<AStarNode> openList = new List<AStarNode>();
List<AStarNode> closeList = new List<AStarNode>();
List<AStarNode> aroundNodes;
List<AStarNode> FindNodePath(AStarNode startNode, AStarNode endNode, AStarNode[,] allNodes)
{
//计算范围内的节点
openList.Clear();
//不在计算范围内的节点
closeList.Clear();
//添加起点
openList.Add(startNode);
AStarNode curNode;
//从起点开始循环判断
while (openList.Count > 0)
{
//初始当前位置
curNode = openList[0];
//计算最优当前位置
for (int i = 0; i < openList.Count; i++)
{
//F:从起点到目标点的移动步数
//H:从当前位置到目标位置的移动步数
if (openList[i].CostF < curNode.CostF && openList[i].costH < curNode.costH)
{
curNode = openList[i];
}
}
//锁定当前位置节点
openList.Remove(curNode);
closeList.Add(curNode);
//已经计算到目标点
if (curNode.Equals(endNode))
{
//返回最优路径
return GetPathWithNode(startNode, endNode);
}
//未计算到目标点, 继续
//获取当前点的周围节点, 在周围节点中查找下一步的最优节点
aroundNodes = GetAroundNodes(curNode, allNodes);
for (int i = 0; i < aroundNodes.Count; i++)
{
计算到目标点
//if (aroundNodes[i].Equals(endNode))
//{
// //设置上级节点
// aroundNodes[i].lastNode = curNode;
// //返回最优路径
// return GetPathWithNode(startNode, endNode);
//}
//不是目标点, 继续计算, 剔除周围节点不可通过、在不可计算范围内的节点
if (!aroundNodes[i].isWall && !closeList.Contains(aroundNodes[i]))
{
//计算 G H F
//F:从起点到目标点的移动步数
//G:从起点到当前位置的移动步数
int newCostG = curNode.costG + GetNodesDistance(curNode, aroundNodes[i]);
if (newCostG <= aroundNodes[i].costG || !openList.Contains(aroundNodes[i]))
{
//刷新赋值
aroundNodes[i].costG = newCostG;
//H:从当前位置到目标位置的移动步数
aroundNodes[i].costH = GetNodesDistance(aroundNodes[i], endNode);
//设置上级节点
aroundNodes[i].lastNode = curNode;
//添加到计算范围内
if (!openList.Contains(aroundNodes[i]))
{
openList.Add(aroundNodes[i]);
}
}
}
}
}
return null;
}
//计算距离
int GetNodesDistance(AStarNode startNode, AStarNode endNode)
{
return Mathf.Abs(startNode.pos.x - endNode.pos.x) + Mathf.Abs(startNode.pos.y - endNode.pos.y);
}
//周围节点只取上下左右四个, 不取对角线
Vector2Int[] aroundPos = { new Vector2Int(0, 1), new Vector2Int(0, -1), new Vector2Int(1, 0), new Vector2Int(-1, 0) };
//获取周围Node
List<AStarNode> tmpAroundList = new List<AStarNode>();
List<AStarNode> GetAroundNodes(AStarNode curNode, AStarNode[,] allNodes)
{
tmpAroundList.Clear();
for (int i = 0; i < aroundPos.Length; i++)
{
int x = curNode.pos.x + aroundPos[i].x;
int y = curNode.pos.y + aroundPos[i].y;
if (x >= 0 && x < allNodes.GetLength(0) && y >= 0 && y < allNodes.GetLength(1))
{
if (allNodes[x, y] != null)
tmpAroundList.Add(allNodes[x, y]);
}
}
return tmpAroundList;
}
//获取路径(包含起点)
List<AStarNode> tmpNodePath = new List<AStarNode>();
List<AStarNode> GetPathWithNode(AStarNode startNode, AStarNode endNode)
{
tmpNodePath.Clear();
if (endNode != null)
{
AStarNode temp = endNode;
while (temp != startNode)
{
tmpNodePath.Add(temp);
temp = temp.lastNode;
}
tmpNodePath.Add(startNode);
tmpNodePath.Reverse();
}
return tmpNodePath;
}
#endregion
}
public class AStarNode
{
//A*算法节点类
//是否能通过
public bool isWall;
//位置坐标
public Vector2Int pos;
//上级节点
public AStarNode lastNode;
//从起点到当前位置的移动步数
public int costG;
//从当前位置到目标位置的移动步数
public int costH;
//从起点到目标点的移动步数
public int CostF
{
get { return costG + costH; }
}
public AStarNode(bool _isWall, Vector2Int _pos)
{
isWall = _isWall;
pos = _pos;
}
//重写Equals
public override bool Equals(object obj)
{
if (obj is AStarNode)
{
AStarNode objNode = (AStarNode)obj;
return objNode.pos == pos;
}
return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
本文来自博客园,作者:萧然CS,转载请注明原文链接:https://www.cnblogs.com/z-c-s/p/15112938.html
浙公网安备 33010602011771号