2024-04-04 23:51:14 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using JetBrains.Annotations;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
namespace Game.Pathfinding;
|
|
|
|
|
|
|
|
|
|
public class Node<T>
|
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public int index { get; private set; }
|
|
|
|
|
public T data { get; private set; }
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public Node(int index, T data)
|
|
|
|
|
{
|
|
|
|
|
this.index = index;
|
|
|
|
|
this.data = data;
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
class Edge<T>
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public Node<T> from { get; private set; }
|
|
|
|
|
public Node<T> to { get; private set; }
|
|
|
|
|
public bool canTranslate { get; private set; }
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public Edge(Node<T> from, Node<T> to, bool canTranslate = true)
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
this.from = from;
|
|
|
|
|
this.to = to;
|
|
|
|
|
this.canTranslate = canTranslate;
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public Node<T> GetAnotherNode(Node<T> node)
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
return node == this.from ? to : to == node ? from : throw new InvalidOperationException();
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
2024-04-05 11:59:11 +08:00
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public class WayPointGraph : Graph<WayPoint>
|
|
|
|
|
{
|
|
|
|
|
readonly struct UniqueWayPoint : IEquatable<UniqueWayPoint>
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
private readonly WayPoint _wayPoint;
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public UniqueWayPoint(WayPoint wayPoint)
|
|
|
|
|
{
|
|
|
|
|
this._wayPoint = wayPoint;
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public bool Equals(UniqueWayPoint other)
|
|
|
|
|
{
|
|
|
|
|
return _wayPoint.position == other._wayPoint.position;
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
|
{
|
|
|
|
|
return obj is UniqueWayPoint other && Equals(other);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
{
|
|
|
|
|
return _wayPoint.position.GetHashCode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static implicit operator WayPoint(UniqueWayPoint uniqueWayPoint)
|
|
|
|
|
{
|
|
|
|
|
return uniqueWayPoint._wayPoint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static implicit operator UniqueWayPoint(WayPoint wayPoint)
|
|
|
|
|
{
|
|
|
|
|
return new UniqueWayPoint(wayPoint);
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public void Initialize(GameGlobalConfig.NodeMap[] nodeMaps)
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
HashSet<UniqueWayPoint> wayPoints = new HashSet<UniqueWayPoint>();
|
|
|
|
|
foreach (var nodeMap in nodeMaps)
|
|
|
|
|
{
|
|
|
|
|
wayPoints.Add(new WayPoint(nodeMap.node1));
|
|
|
|
|
wayPoints.Add(new WayPoint(nodeMap.node2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Dictionary<Vector3, Node<WayPoint>> position2NodeMap = new Dictionary<Vector3, Node<WayPoint>>();
|
|
|
|
|
_nodes = new Node<WayPoint>[wayPoints.Count];
|
|
|
|
|
using var enumerator = wayPoints.GetEnumerator();
|
|
|
|
|
int index = 0;
|
|
|
|
|
while (enumerator.MoveNext())
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
var enumeratorCurrent = (WayPoint)enumerator.Current;
|
|
|
|
|
var node = new Node<WayPoint>(index, enumeratorCurrent);
|
|
|
|
|
this._nodes[index] = node;
|
|
|
|
|
position2NodeMap[enumeratorCurrent.position] = node;
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_edges = new List<Edge<WayPoint>>(nodeMaps.Length);
|
|
|
|
|
this._connection = new Dictionary<Node<WayPoint>, List<Edge<WayPoint>>>(_edges.Count);
|
|
|
|
|
|
|
|
|
|
foreach (var nodeMap in nodeMaps)
|
|
|
|
|
{
|
|
|
|
|
if (position2NodeMap.TryGetValue(nodeMap.node1, out var node1))
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
if (position2NodeMap.TryGetValue(nodeMap.node2, out var node2))
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
var edge = new Edge<WayPoint>(node1, node2);
|
|
|
|
|
_edges.Add(edge);
|
|
|
|
|
if (!this._connection.TryGetValue(node1, out var list1))
|
|
|
|
|
{
|
|
|
|
|
this._connection[node1] = list1 = new List<Edge<WayPoint>>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list1.Add(edge);
|
|
|
|
|
if (!this._connection.TryGetValue(node2, out var list2))
|
|
|
|
|
{
|
|
|
|
|
this._connection[node2] = list2 = new List<Edge<WayPoint>>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list2.Add(edge);
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public class Graph<T>
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
private protected Node<T>[] _nodes;
|
|
|
|
|
private protected List<Edge<T>> _edges;
|
|
|
|
|
private protected Dictionary<Node<T>, List<Edge<T>>> _connection;
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
internal Node<T> GetNode(int index)
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
if (index < 0 || index >= this._nodes.Length - 1)
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
throw new IndexOutOfRangeException($"Index is {index}");
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
return this._nodes[index];
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
[CanBeNull]
|
|
|
|
|
internal Node<T> GetNode(Predicate<Node<T>> predicate)
|
|
|
|
|
{
|
|
|
|
|
foreach (var node in this._nodes)
|
|
|
|
|
{
|
|
|
|
|
if (predicate(node))
|
|
|
|
|
{
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
return null;
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
internal IReadOnlyList<Edge<T>> GetNeighbours(Node<T> node)
|
|
|
|
|
{
|
|
|
|
|
this._connection.TryGetValue(node, out var edges);
|
|
|
|
|
return edges;
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public class BFS<T>
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
private Graph<T> _graph;
|
|
|
|
|
private List<int> _closeList = new List<int>();
|
|
|
|
|
private Queue<int> _openList = new Queue<int>();
|
|
|
|
|
private Dictionary<Node<T>, Node<T>> _nodeMap = new Dictionary<Node<T>, Node<T>>();
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public BFS(Graph<T> graph)
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
this._graph = graph;
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
[CanBeNull]
|
|
|
|
|
public Node<T> GetNode(Predicate<Node<T>> predicate)
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
return _graph.GetNode(predicate);
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public Path<T> FindPath(Node<T> begin, Node<T> end)
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
_openList.Clear();
|
|
|
|
|
_closeList.Clear();
|
|
|
|
|
|
|
|
|
|
_openList.Enqueue(begin.index);
|
|
|
|
|
while (this._openList.Count > 0)
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
var nodeIndex = this._openList.Dequeue();
|
|
|
|
|
var currNode = this._graph.GetNode(nodeIndex);
|
|
|
|
|
if (currNode.index == end.index)
|
|
|
|
|
break;
|
|
|
|
|
_closeList.Add(currNode.index);
|
|
|
|
|
Search(currNode);
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
Path<T> path = new Path<T>();
|
|
|
|
|
Node<T> curr = end;
|
|
|
|
|
path.AddNode(new PathNode<T>(curr.index, curr.data));
|
|
|
|
|
while (_nodeMap.TryGetValue(curr, out curr))
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
path.AddNode(new PathNode<T>(curr.index, curr.data));
|
|
|
|
|
if (curr == begin)
|
|
|
|
|
break;
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
2024-04-05 11:59:11 +08:00
|
|
|
|
|
|
|
|
|
path.Reverse();
|
|
|
|
|
return path;
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
private void Search(Node<T> node)
|
|
|
|
|
{
|
|
|
|
|
var readOnlyList = this._graph.GetNeighbours(node);
|
|
|
|
|
foreach (var edge in readOnlyList)
|
|
|
|
|
{
|
|
|
|
|
if (!edge.canTranslate)
|
|
|
|
|
continue;
|
|
|
|
|
Node<T> another = edge.GetAnotherNode(node);
|
|
|
|
|
if (!this._nodeMap.ContainsKey(another))
|
|
|
|
|
{
|
|
|
|
|
_nodeMap[another] = node;
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
if (!this._closeList.Contains(another.index))
|
|
|
|
|
{
|
|
|
|
|
_openList.Enqueue(another.index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public class Path<T>
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
private readonly List<PathNode<T>> _pathNodes = new List<PathNode<T>>();
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public void Reverse()
|
2024-04-04 23:51:14 +08:00
|
|
|
|
{
|
2024-04-05 11:59:11 +08:00
|
|
|
|
this._pathNodes.Reverse();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void AddNode(PathNode<T> node)
|
|
|
|
|
{
|
|
|
|
|
this._pathNodes.Add(node);
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public IReadOnlyList<PathNode<T>> GetNodes()
|
|
|
|
|
{
|
|
|
|
|
return this._pathNodes;
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
|
2024-04-05 11:59:11 +08:00
|
|
|
|
public void GetDatas(IList<T> result)
|
|
|
|
|
{
|
|
|
|
|
foreach (var pathNode in this._pathNodes)
|
|
|
|
|
{
|
|
|
|
|
result.Add(pathNode.data);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-04 23:51:14 +08:00
|
|
|
|
}
|
2024-04-05 11:59:11 +08:00
|
|
|
|
|
|
|
|
|
public class PathNode<T>
|
|
|
|
|
{
|
|
|
|
|
public int index { get; private set; }
|
|
|
|
|
public T data { get; private set; }
|
|
|
|
|
|
|
|
|
|
public PathNode(int index, T data)
|
|
|
|
|
{
|
|
|
|
|
this.index = index;
|
|
|
|
|
this.data = data;
|
|
|
|
|
}
|
|
|
|
|
}
|