(client) feat:右键菜单全局与UI分离,Building生成支持,双层地图重写
This commit is contained in:
183
Client/Assets/Scripts/Utils/Pathfinder.cs
Normal file
183
Client/Assets/Scripts/Utils/Pathfinder.cs
Normal file
@ -0,0 +1,183 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
|
||||
public static class Pathfinder
|
||||
{
|
||||
public static List<Vector2> FindPath(Entity.Entity entity, Vector2 target, float maxDistance)
|
||||
{
|
||||
Vector2 start = entity.Position;
|
||||
|
||||
// 计算起点和终点所在的瓦片坐标
|
||||
var startTile = GetTileCoord(start);
|
||||
var endTile = GetTileCoord(target);
|
||||
|
||||
// 如果超出最大距离,直接返回直线路径或空路径
|
||||
if (Vector2.Distance(start, target) > maxDistance)
|
||||
{
|
||||
return new List<Vector2> { start, target };
|
||||
}
|
||||
|
||||
// A*算法数据结构
|
||||
var cameFrom = new Dictionary<Vector2Int, Vector2Int>();
|
||||
var gScore = new Dictionary<Vector2Int, float>();
|
||||
var fScore = new Dictionary<Vector2Int, float>();
|
||||
var openSet = new List<Vector2Int>();
|
||||
|
||||
// 初始化
|
||||
gScore[startTile] = 0;
|
||||
fScore[startTile] = Heuristic(startTile, endTile);
|
||||
openSet.Add(startTile);
|
||||
|
||||
var closestNode = startTile;
|
||||
var closestDist = Vector2.Distance(start, target);
|
||||
|
||||
while (openSet.Count > 0)
|
||||
{
|
||||
// 获取fScore最小的节点
|
||||
var current = openSet[0];
|
||||
foreach (var node in openSet)
|
||||
{
|
||||
if (fScore.GetValueOrDefault(node, float.MaxValue) <
|
||||
fScore.GetValueOrDefault(current, float.MaxValue))
|
||||
{
|
||||
current = node;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否到达目标
|
||||
if (current == endTile)
|
||||
{
|
||||
return ReconstructPath(cameFrom, current, start, target);
|
||||
}
|
||||
|
||||
openSet.Remove(current);
|
||||
|
||||
// 检查最大距离限制
|
||||
var currentDist = Vector2.Distance(
|
||||
new Vector2(current.x, current.y),
|
||||
target);
|
||||
|
||||
if (currentDist < closestDist)
|
||||
{
|
||||
closestDist = currentDist;
|
||||
closestNode = current;
|
||||
}
|
||||
|
||||
if (gScore[current] > maxDistance)
|
||||
{
|
||||
return ReconstructPath(cameFrom, closestNode, start, target);
|
||||
}
|
||||
|
||||
// 遍历邻居(8方向)
|
||||
for (var dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
for (var dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
if (dx == 0 && dy == 0) continue;
|
||||
|
||||
var neighbor = new Vector2Int(current.x + dx, current.y + dy);
|
||||
|
||||
// 跳过不可通行区域
|
||||
if (!Map.MapGenerator.Instance.CanPassThrough(neighbor.x, neighbor.y))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算移动成本
|
||||
var moveCost = GetMovementCost(current, neighbor);
|
||||
var tentativeGScore = gScore[current] + moveCost;
|
||||
|
||||
// 跳过超出最大距离的路径
|
||||
if (tentativeGScore > maxDistance)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 发现新节点或找到更好路径
|
||||
if (tentativeGScore < gScore.GetValueOrDefault(neighbor, float.MaxValue))
|
||||
{
|
||||
cameFrom[neighbor] = current;
|
||||
gScore[neighbor] = tentativeGScore;
|
||||
fScore[neighbor] = tentativeGScore + Heuristic(neighbor, endTile);
|
||||
|
||||
if (!openSet.Contains(neighbor))
|
||||
{
|
||||
openSet.Add(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 无法找到完整路径时返回局部最优解
|
||||
return ReconstructPath(cameFrom, closestNode, start, target);
|
||||
}
|
||||
|
||||
// 获取瓦片坐标(每个瓦片覆盖±0.5范围)
|
||||
private static Vector2Int GetTileCoord(Vector2 position)
|
||||
{
|
||||
return new Vector2Int(
|
||||
Mathf.RoundToInt(position.x),
|
||||
Mathf.RoundToInt(position.y)
|
||||
);
|
||||
}
|
||||
|
||||
// 计算启发式估值(欧几里得距离)
|
||||
private static float Heuristic(Vector2Int a, Vector2Int b)
|
||||
{
|
||||
return Vector2.Distance(
|
||||
new Vector2(a.x, a.y),
|
||||
new Vector2(b.x, b.y)
|
||||
);
|
||||
}
|
||||
|
||||
// 获取移动成本
|
||||
private static float GetMovementCost(Vector2Int from, Vector2Int to)
|
||||
{
|
||||
// 计算基础距离(正交=1,对角=√2)
|
||||
var distance = (from.x == to.x || from.y == to.y) ? 1f : 1.4142f;
|
||||
|
||||
// 应用目标瓦片的速度削减率
|
||||
var costModifier = Map.MapGenerator.Instance.GetTileCost(to.x, to.y);
|
||||
|
||||
// 成本 = 距离 × (1 + 速度削减率)
|
||||
return distance * (1 + costModifier);
|
||||
}
|
||||
|
||||
// 重建路径
|
||||
private static List<Vector2> ReconstructPath(
|
||||
Dictionary<Vector2Int, Vector2Int> cameFrom,
|
||||
Vector2Int current,
|
||||
Vector2 start,
|
||||
Vector2 end)
|
||||
{
|
||||
// 构建瓦片路径
|
||||
var tilePath = new List<Vector2Int>();
|
||||
tilePath.Add(current);
|
||||
|
||||
while (cameFrom.ContainsKey(current))
|
||||
{
|
||||
current = cameFrom[current];
|
||||
tilePath.Add(current);
|
||||
}
|
||||
|
||||
tilePath.Reverse();
|
||||
|
||||
// 转换为实际坐标路径
|
||||
var path = new List<Vector2>();
|
||||
path.Add(start); // 添加精确起点
|
||||
|
||||
// 添加路径点(瓦片中心)
|
||||
foreach (var tile in tilePath)
|
||||
{
|
||||
path.Add(new Vector2(tile.x, tile.y));
|
||||
}
|
||||
|
||||
path.Add(end); // 添加精确终点
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user