481 lines
18 KiB
C#
481 lines
18 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using Thousandto.Core.Base;
|
||
using SimpleAI.Planning;
|
||
using SceneEditor.Proxy.Plugin;
|
||
using Thousandto.Plugins.Common.UniScene;
|
||
|
||
namespace Thousandto.Plugins.PathGrid
|
||
{
|
||
//陆地寻路系统
|
||
public class PathLandSystem : PathGridSystem
|
||
{
|
||
#region 私有变量
|
||
private List<Vector2> _pathPoints2d = new List<Vector2>();
|
||
//A星寻路
|
||
private AStarPlanner wayPointPlanner = null;
|
||
//wayPoint数据
|
||
private WayPointPlanData wayPointWorld = null;
|
||
#endregion
|
||
|
||
#region 私有函数
|
||
//在没有wayPoint情况下的纯A星寻路
|
||
private List<Vector2> GetPathNoWayPoint(Vector2 startPosition, Vector2 endPosition)
|
||
{
|
||
_pathPoints2d.Clear();
|
||
var grid = _pathWorld;
|
||
int startNodeIndex = grid.GetPathNodeIndex(startPosition);
|
||
if (grid.IsBlocked(startNodeIndex))
|
||
{
|
||
return null;
|
||
}
|
||
int endNodeIndex = grid.GetPathNodeIndex(endPosition);
|
||
if (grid.IsBlocked(endNodeIndex))
|
||
{
|
||
return null;
|
||
}
|
||
float dx = MathLib.FastAbs(startPosition.x - endPosition.x);
|
||
float dz = MathLib.FastAbs(startPosition.y - endPosition.y);
|
||
float cdx = dx * grid.InvCellSize + 1;
|
||
float cdz = dz * grid.InvCellSize + 1;
|
||
float fdd = cdx * cdz;
|
||
bool result = false;
|
||
if (fdd <= PreSearchSize)
|
||
{
|
||
_pathPlannder.StartANewPlan(startNodeIndex, endNodeIndex, true);
|
||
while (!_pathPlannder.HasPlanCompleted())
|
||
{
|
||
_pathPlannder.Update(50);
|
||
}
|
||
result = _pathPlannder.HasPlanSucceeded();
|
||
if (!result)
|
||
{
|
||
if (_pathPlannder.GetAvailableSearchNodeCount() != 0)
|
||
{
|
||
// search node pool size is ok, add node in openTable has been fully tested.
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
if (!result)
|
||
{
|
||
_pathPlannder.StartANewPlan(startNodeIndex, endNodeIndex);
|
||
while (!_pathPlannder.HasPlanCompleted())
|
||
{
|
||
_pathPlannder.Update(50);
|
||
}
|
||
}
|
||
result = _pathPlannder.HasPlanSucceeded();
|
||
if (_pathPlannder.Solution.Count > 0)
|
||
{
|
||
Vector2 last = startPosition;
|
||
_pathPoints2d.Add(startPosition);
|
||
var solution = _pathPlannder.Solution;
|
||
for (int i = solution.Count - 1; i >= 0; --i)
|
||
{
|
||
var pt = MathLib.ToVector2_XOZ(grid.GetPathNodePos(solution[i].Index));
|
||
if ((pt - last).sqrMagnitude > Vector2.kEpsilon)
|
||
{
|
||
_pathPoints2d.Add(pt);
|
||
last = pt;
|
||
}
|
||
}
|
||
if ((endPosition - last).sqrMagnitude > Vector2.kEpsilon)
|
||
{
|
||
_pathPoints2d.Add(endPosition);
|
||
}
|
||
var finalPath = SmoothPath(_pathPoints2d, endPosition);
|
||
if (finalPath.Count >= 2)
|
||
{
|
||
return finalPath;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
//获取点到waypoint的寻路路径
|
||
private bool GetGridPath(Vector2 startPosition, Vector2 endPosition, List<Vector2> pathPosition)
|
||
{
|
||
var grid = _pathWorld;
|
||
int startNodeIndex = grid.GetPathNodeIndex(startPosition);
|
||
int endNodeIndex = grid.GetPathNodeIndex(endPosition);
|
||
float dx = MathLib.FastAbs(startPosition.x - endPosition.x);
|
||
float dz = MathLib.FastAbs(startPosition.y - endPosition.y);
|
||
float cdx = dx * grid.InvCellSize + 1;
|
||
float cdz = dz * grid.InvCellSize + 1;
|
||
float fdd = cdx * cdz;
|
||
bool result = false;
|
||
if (fdd <= PreSearchSize)
|
||
{
|
||
_pathPlannder.StartANewPlan(startNodeIndex, endNodeIndex, true);
|
||
while (!_pathPlannder.HasPlanCompleted())
|
||
{
|
||
_pathPlannder.Update(50);
|
||
}
|
||
result = _pathPlannder.HasPlanSucceeded();
|
||
if (!result)
|
||
{
|
||
if (_pathPlannder.GetAvailableSearchNodeCount() != 0)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
if (!result)
|
||
{
|
||
_pathPlannder.StartANewPlan(startNodeIndex, endNodeIndex);
|
||
while (!_pathPlannder.HasPlanCompleted())
|
||
{
|
||
_pathPlannder.Update(50);
|
||
}
|
||
}
|
||
result = _pathPlannder.HasPlanSucceeded();
|
||
if (_pathPlannder.Solution.Count > 0)
|
||
{
|
||
Vector2 last = startPosition;
|
||
pathPosition.Add(startPosition);
|
||
var solution = _pathPlannder.Solution;
|
||
for (int i = solution.Count - 1; i >= 0; --i)
|
||
{
|
||
var pt = MathLib.ToVector2_XOZ(grid.GetPathNodePos(solution[i].Index));
|
||
if ((pt - last).sqrMagnitude > Vector2.kEpsilon)
|
||
{
|
||
pathPosition.Add(pt);
|
||
last = pt;
|
||
}
|
||
}
|
||
if ((endPosition - last).sqrMagnitude > Vector2.kEpsilon)
|
||
{
|
||
pathPosition.Add(endPosition);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
//寻出waypoint的路径
|
||
public bool searchWayPointPath(int startIndex, int endIndex, List<Vector2> pathPosition)
|
||
{
|
||
if (wayPointWorld.IsNodeBlocked(startIndex))
|
||
{
|
||
WayPointNode node = wayPointWorld.GetNode(startIndex);
|
||
//Debug.LogError(string.Format("{0}这个点放到了阻挡里面",node.name));
|
||
return false;
|
||
}
|
||
if (wayPointWorld.IsNodeBlocked(endIndex))
|
||
{
|
||
WayPointNode node = wayPointWorld.GetNode(endIndex);
|
||
//Debug.LogError(string.Format("{0}这个点放到了阻挡里面", node.name));
|
||
return false;
|
||
}
|
||
FProfiler.Begin("PathLandSystem.searchWayPointPath");
|
||
wayPointPlanner.StartANewPlan(startIndex, endIndex);
|
||
while (!wayPointPlanner.HasPlanCompleted())
|
||
wayPointPlanner.Update(100);
|
||
FProfiler.End();
|
||
if (!wayPointPlanner.HasPlanSucceeded())
|
||
{
|
||
//Debug.LogError(string.Format("MapId:{0} 路点编辑有问题,起始路点和终点路点不连通!!!", Owner.Cfg.MapId));
|
||
return false;
|
||
}
|
||
Vector2 tempPos = Vector2.zero;
|
||
for (int i = wayPointPlanner.Solution.Count - 1; i >= 0; --i)
|
||
{
|
||
Vector3 pos = wayPointWorld.m_pointInfo[wayPointPlanner.Solution[i].Index].pos;
|
||
tempPos.x = pos.x;
|
||
tempPos.y = pos.z;
|
||
pathPosition.Add(tempPos);
|
||
}
|
||
return true;
|
||
}
|
||
private List<Vector2> GetPath(Vector2 startPosition, Vector2 endPosition)
|
||
{
|
||
if (wayPointWorld == null)
|
||
{
|
||
//Debug.LogError(string.Format("MapId:{0} 场景中找不到[WayPoint] WayPointWorld为空",Owner.Cfg.MapId));
|
||
return GetPathNoWayPoint(startPosition, endPosition);
|
||
}
|
||
int startWayPoint = wayPointWorld.GetNearestIndexBypos(ref startPosition);
|
||
int endWaypoint = wayPointWorld.GetNearestIndexBypos(ref endPosition);
|
||
if (startWayPoint == -1 || endWaypoint == -1)
|
||
{
|
||
//Debug.LogError(string.Format("MapId:{0} 场景中找不到[WayPoint]",Owner.Cfg.MapId));
|
||
return GetPathNoWayPoint(startPosition, endPosition);
|
||
}
|
||
|
||
_pathPoints2d.Clear();
|
||
int startNodeIndex = _pathWorld.GetPathNodeIndex(startPosition);
|
||
int endNodeIndex = _pathWorld.GetPathNodeIndex(endPosition);
|
||
Vector3 startWayPointPos = wayPointWorld.m_pointInfo[startWayPoint].pos;
|
||
Vector3 endWayPointPos = wayPointWorld.m_pointInfo[endWaypoint].pos;
|
||
Vector2 startWayPoint2d = new Vector2(startWayPointPos.x, startWayPointPos.z);
|
||
Vector2 endWayPoint2d = new Vector2(endWayPointPos.x, endWayPointPos.z);
|
||
|
||
_pathPoints2d.Add(startPosition);
|
||
if(!GetGridPath(startPosition, startWayPoint2d, _pathPoints2d))
|
||
return null;
|
||
if(!searchWayPointPath(startWayPoint, endWaypoint, _pathPoints2d))
|
||
return null;
|
||
if(!GetGridPath(endWayPoint2d, endPosition, _pathPoints2d))
|
||
return null;
|
||
var finalPath = SmoothPath(_pathPoints2d, endPosition);
|
||
if (finalPath.Count >= 2)
|
||
return finalPath;
|
||
return null;
|
||
}
|
||
//优化寻路出来的寻路点,把可以去掉的寻路点去掉
|
||
private List<Vector2> SmoothPath(List<Vector2> input, Vector2 endPosition)
|
||
{
|
||
var ret = Path2dPool.sharedInstance.Allocate(input.Count >> 1);
|
||
int count = input.Count;
|
||
if (count > 1)
|
||
{
|
||
ret.Add(input[0]); // add start point
|
||
Vector2 s, e, hit;
|
||
int curEndIndex = -1;
|
||
for (int j = 0; j < count - 1;)
|
||
{
|
||
s = input[j];
|
||
int i = j + 1;
|
||
int si = j;
|
||
int c = 0;
|
||
for (; i < count && c < int.MaxValue; ++i)
|
||
{
|
||
e = input[i];
|
||
if ((i - si) > 1 && Raycast2dSafe(s, e, out hit))
|
||
{
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
curEndIndex = i;
|
||
j = i;
|
||
}
|
||
++c;
|
||
}
|
||
ret.Add(input[curEndIndex]);
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
#endregion
|
||
|
||
#region 公共方法
|
||
public override void Initialize(BaseScene scene)
|
||
{
|
||
base.Initialize(scene);
|
||
}
|
||
public override void Uninitialize()
|
||
{
|
||
base.Uninitialize();
|
||
}
|
||
public bool LoadWayPointData(BaseScene owner)
|
||
{
|
||
Transform trans = owner.SceneObjectsRoot.Find(BaseScene.WayPointRootName);
|
||
if (trans != null)
|
||
wayPointWorld = trans.GetComponent<WayPointPlanData>();
|
||
if (wayPointWorld != null && wayPointWorld.m_pointInfo != null && wayPointWorld.m_maxNeighborNumber > 0)
|
||
{
|
||
wayPointPlanner = new AStarPlanner(wayPointWorld.m_pointInfo.Length + 1, 0);
|
||
wayPointPlanner.Awake();
|
||
wayPointPlanner.Start(wayPointWorld);
|
||
}
|
||
return true;
|
||
}
|
||
//根据目标的半径范围,从起点到终点寻路获取寻路点
|
||
public override List<Vector2> SearchTargetPath(Vector3 startPos, Vector3 endPos, float targetRadius)
|
||
{
|
||
var startIsBlock = IsBlocked(startPos);
|
||
if(startIsBlock)
|
||
{
|
||
//Debug.Log("起点在阻挡里");
|
||
return null;
|
||
}
|
||
|
||
var endIsSafe = IsSafe(endPos);
|
||
if (endIsSafe)
|
||
{
|
||
//Debug.Log("终点在传送点里");
|
||
}
|
||
|
||
List<Vector2> outPath = Path2dPool.sharedInstance.Allocate(1);
|
||
|
||
//如果玩家处于跟目标位置一样的点
|
||
if (startPos == endPos)
|
||
{
|
||
return outPath;
|
||
}
|
||
|
||
//如果寻路到的目标点有半径
|
||
if (targetRadius > 0.0f)
|
||
{
|
||
if (Vector3.Distance(startPos, endPos) <= targetRadius)
|
||
{
|
||
return outPath;
|
||
}
|
||
}
|
||
|
||
Vector2 start2d = MathLib.ToVector2_XOZ(startPos);
|
||
Vector2 end2d = MathLib.ToVector2_XOZ(endPos);
|
||
|
||
var blocked = IsBlocked(endPos);
|
||
if (blocked)
|
||
{
|
||
//Debug.Log("目标点在阻挡里");
|
||
Vector2 dir = (end2d - start2d).normalized;
|
||
Vector2 newPos = end2d - dir * Mathf.Min(targetRadius, CellSize * 0.5f);
|
||
if(IsBlocked(newPos))
|
||
{
|
||
//Debug.Log("在半径边缘的目标点还是在阻挡里");
|
||
return null;
|
||
}
|
||
if (GetCellIndex(start2d) != GetCellIndex(newPos))
|
||
{
|
||
end2d = newPos;
|
||
}
|
||
else
|
||
{
|
||
return outPath;
|
||
}
|
||
}
|
||
|
||
Math2d.Circle circle;
|
||
circle.Center = end2d;
|
||
circle.Radius = targetRadius;
|
||
bool goDirect = false;
|
||
if(endIsSafe)
|
||
{
|
||
goDirect = CanGoDirect(start2d, end2d);
|
||
}
|
||
else
|
||
{
|
||
goDirect = CanGoDirectSafe(start2d, end2d);
|
||
}
|
||
if (goDirect)
|
||
{
|
||
if (targetRadius > 0)
|
||
{
|
||
Math2d.LineSegment line;
|
||
line.Start = start2d;
|
||
line.End = end2d;
|
||
var solves = Math2d.Intersect_LineSegment_Circle(circle, line);
|
||
if (solves != null && solves.Length > 0 && solves[0] != end2d)
|
||
{
|
||
end2d = solves[0];
|
||
}
|
||
else
|
||
{
|
||
return outPath;
|
||
}
|
||
}
|
||
if (start2d != end2d)
|
||
{
|
||
outPath = Path2dPool.sharedInstance.Allocate(2);
|
||
outPath.Add(start2d);
|
||
outPath.Add(end2d);
|
||
}
|
||
else
|
||
{
|
||
return outPath;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
var ptlist = GetPath(start2d, end2d);
|
||
if (ptlist == null || ptlist.Count < 2)
|
||
{
|
||
//Debug.Log("通过waypoint寻路没有寻到");
|
||
return null;
|
||
}
|
||
Vector2 currPoint = ptlist.Count > 0 ? ptlist[0] : Vector2.zero;
|
||
var _ptlist = Path2dPool.sharedInstance.Allocate(ptlist.Count);
|
||
bool earlyBreak = false;
|
||
_ptlist.Add(currPoint);
|
||
for (int i = 1; i < ptlist.Count; ++i)
|
||
{
|
||
Vector2 nextPoint = ptlist[i];
|
||
if (targetRadius > 0)
|
||
{
|
||
Math2d.LineSegment line;
|
||
line.Start = currPoint;
|
||
line.End = nextPoint;
|
||
var solves = Math2d.Intersect_LineSegment_Circle(circle, line);
|
||
if (solves != null && solves.Length > 0)
|
||
{
|
||
nextPoint = solves[0];
|
||
earlyBreak = true;
|
||
}
|
||
}
|
||
_ptlist.Add(nextPoint);
|
||
if (earlyBreak)
|
||
{
|
||
break;
|
||
}
|
||
currPoint = nextPoint;
|
||
}
|
||
outPath = _ptlist;
|
||
}
|
||
return outPath;
|
||
}
|
||
|
||
public override List<Vector2> SearchPlayerPath(Vector3 startPosition, Vector3 endPosition)
|
||
{
|
||
if (startPosition == endPosition)
|
||
{
|
||
return null;
|
||
}
|
||
else
|
||
{
|
||
Vector2 hit;
|
||
if (!Raycast2d(startPosition, endPosition, out hit))
|
||
{
|
||
return GetPath(startPosition, endPosition);
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
public override List<Vector2> SearchOtherPath(Vector3 startPosition, Vector3 endPosition)
|
||
{
|
||
if (startPosition == endPosition)
|
||
{
|
||
return null;
|
||
}
|
||
else
|
||
{
|
||
List<Vector2> ret = Path2dPool.sharedInstance.Allocate(2);
|
||
ret.Add(startPosition);
|
||
ret.Add(endPosition);
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
|
||
//评价路径,返回-1表示无法寻路到达,返回距离表示到目标点的实际距离
|
||
public override float EvaluatePath(Vector3 startPosition, Vector3 endPosition)
|
||
{
|
||
List<Vector2> outPath = null;
|
||
outPath = SearchTargetPath(startPosition, endPosition, 0f);
|
||
if (outPath == null || outPath.Count < 2)
|
||
{
|
||
//寻路失败,无法到达
|
||
return -1f;
|
||
}
|
||
|
||
var result = 0f;
|
||
for(int i = 0; i < outPath.Count - 1; ++i)
|
||
{
|
||
if (IsNotBlockRayCast2d(outPath[i], outPath[i + 1]))
|
||
{
|
||
result += Vector2.Distance(outPath[i], outPath[i + 1]);
|
||
}
|
||
else
|
||
{
|
||
//有阻断,无法到达
|
||
return -1f;
|
||
}
|
||
}
|
||
//寻路失败无法到达
|
||
return result;
|
||
}
|
||
#endregion
|
||
}
|
||
}
|