Adding A* Algorythm and carpet roads
This commit is contained in:
@ -0,0 +1,361 @@
|
||||
using System.Collections.Generic;
|
||||
using Pathfinding.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Pathfinding {
|
||||
/// <summary>Node used for the GridGraph</summary>
|
||||
public class GridNode : GridNodeBase {
|
||||
public GridNode (AstarPath astar) : base(astar) {
|
||||
}
|
||||
|
||||
#if !ASTAR_NO_GRID_GRAPH
|
||||
private static GridGraph[] _gridGraphs = new GridGraph[0];
|
||||
public static GridGraph GetGridGraph (uint graphIndex) { return _gridGraphs[(int)graphIndex]; }
|
||||
|
||||
public static void SetGridGraph (int graphIndex, GridGraph graph) {
|
||||
if (_gridGraphs.Length <= graphIndex) {
|
||||
var gg = new GridGraph[graphIndex+1];
|
||||
for (int i = 0; i < _gridGraphs.Length; i++) gg[i] = _gridGraphs[i];
|
||||
_gridGraphs = gg;
|
||||
}
|
||||
|
||||
_gridGraphs[graphIndex] = graph;
|
||||
}
|
||||
|
||||
public static void ClearGridGraph (int graphIndex, GridGraph graph) {
|
||||
if (graphIndex < _gridGraphs.Length && _gridGraphs[graphIndex] == graph) {
|
||||
_gridGraphs[graphIndex] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Internal use only</summary>
|
||||
internal ushort InternalGridFlags {
|
||||
get { return gridFlags; }
|
||||
set { gridFlags = value; }
|
||||
}
|
||||
|
||||
const int GridFlagsConnectionOffset = 0;
|
||||
const int GridFlagsConnectionBit0 = 1 << GridFlagsConnectionOffset;
|
||||
const int GridFlagsConnectionMask = 0xFF << GridFlagsConnectionOffset;
|
||||
|
||||
const int GridFlagsEdgeNodeOffset = 10;
|
||||
const int GridFlagsEdgeNodeMask = 1 << GridFlagsEdgeNodeOffset;
|
||||
|
||||
public override bool HasConnectionsToAllEightNeighbours {
|
||||
get {
|
||||
return (InternalGridFlags & GridFlagsConnectionMask) == GridFlagsConnectionMask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the node has a connection in the specified direction.
|
||||
/// The dir parameter corresponds to directions in the grid as:
|
||||
/// <code>
|
||||
/// Z
|
||||
/// |
|
||||
/// |
|
||||
///
|
||||
/// 6 2 5
|
||||
/// \ | /
|
||||
/// -- 3 - X - 1 ----- X
|
||||
/// / | \
|
||||
/// 7 0 4
|
||||
///
|
||||
/// |
|
||||
/// |
|
||||
/// </code>
|
||||
///
|
||||
/// See: SetConnectionInternal
|
||||
/// </summary>
|
||||
public bool HasConnectionInDirection (int dir) {
|
||||
return (gridFlags >> dir & GridFlagsConnectionBit0) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the node has a connection in the specified direction.
|
||||
/// Deprecated: Use HasConnectionInDirection
|
||||
/// </summary>
|
||||
[System.Obsolete("Use HasConnectionInDirection")]
|
||||
public bool GetConnectionInternal (int dir) {
|
||||
return HasConnectionInDirection(dir);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables a connection in a specified direction on the graph.
|
||||
/// See: HasConnectionInDirection
|
||||
/// </summary>
|
||||
public void SetConnectionInternal (int dir, bool value) {
|
||||
// Set bit number #dir to 1 or 0 depending on #value
|
||||
unchecked { gridFlags = (ushort)(gridFlags & ~((ushort)1 << GridFlagsConnectionOffset << dir) | (value ? (ushort)1 : (ushort)0) << GridFlagsConnectionOffset << dir); }
|
||||
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the state of all grid connections.
|
||||
///
|
||||
/// See: SetConnectionInternal
|
||||
/// </summary>
|
||||
/// <param name="connections">a bitmask of the connections (bit 0 is the first connection, bit 1 the second connection, etc.).</param>
|
||||
public void SetAllConnectionInternal (int connections) {
|
||||
unchecked { gridFlags = (ushort)((gridFlags & ~GridFlagsConnectionMask) | (connections << GridFlagsConnectionOffset)); }
|
||||
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables all grid connections from this node.
|
||||
/// Note: Other nodes might still be able to get to this node.
|
||||
/// Therefore it is recommended to also disable the relevant connections on adjacent nodes.
|
||||
/// </summary>
|
||||
public void ResetConnectionsInternal () {
|
||||
unchecked {
|
||||
gridFlags = (ushort)(gridFlags & ~GridFlagsConnectionMask);
|
||||
}
|
||||
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Work in progress for a feature that required info about which nodes were at the border of the graph.
|
||||
/// Note: This property is not functional at the moment.
|
||||
/// </summary>
|
||||
public bool EdgeNode {
|
||||
get {
|
||||
return (gridFlags & GridFlagsEdgeNodeMask) != 0;
|
||||
}
|
||||
set {
|
||||
unchecked { gridFlags = (ushort)(gridFlags & ~GridFlagsEdgeNodeMask | (value ? GridFlagsEdgeNodeMask : 0)); }
|
||||
}
|
||||
}
|
||||
|
||||
public override GridNodeBase GetNeighbourAlongDirection (int direction) {
|
||||
if (HasConnectionInDirection(direction)) {
|
||||
GridGraph gg = GetGridGraph(GraphIndex);
|
||||
return gg.nodes[NodeInGridIndex+gg.neighbourOffsets[direction]];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void ClearConnections (bool alsoReverse) {
|
||||
if (alsoReverse) {
|
||||
// Note: This assumes that all connections are bidirectional
|
||||
// which should hold for all grid graphs unless some custom code has been added
|
||||
for (int i = 0; i < 8; i++) {
|
||||
var other = GetNeighbourAlongDirection(i) as GridNode;
|
||||
if (other != null) {
|
||||
// Remove reverse connection. See doc for GridGraph.neighbourOffsets to see which indices are used for what.
|
||||
other.SetConnectionInternal(i < 4 ? ((i + 2) % 4) : (((i-2) % 4) + 4), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResetConnectionsInternal();
|
||||
|
||||
#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
|
||||
base.ClearConnections(alsoReverse);
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void GetConnections (System.Action<GraphNode> action) {
|
||||
GridGraph gg = GetGridGraph(GraphIndex);
|
||||
|
||||
int[] neighbourOffsets = gg.neighbourOffsets;
|
||||
GridNode[] nodes = gg.nodes;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (HasConnectionInDirection(i)) {
|
||||
GridNode other = nodes[NodeInGridIndex + neighbourOffsets[i]];
|
||||
if (other != null) action(other);
|
||||
}
|
||||
}
|
||||
|
||||
#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
|
||||
base.GetConnections(action);
|
||||
#endif
|
||||
}
|
||||
|
||||
public override Vector3 ClosestPointOnNode (Vector3 p) {
|
||||
var gg = GetGridGraph(GraphIndex);
|
||||
|
||||
// Convert to graph space
|
||||
p = gg.transform.InverseTransform(p);
|
||||
|
||||
// Calculate graph position of this node
|
||||
int x = NodeInGridIndex % gg.width;
|
||||
int z = NodeInGridIndex / gg.width;
|
||||
|
||||
// Handle the y coordinate separately
|
||||
float y = gg.transform.InverseTransform((Vector3)position).y;
|
||||
|
||||
var closestInGraphSpace = new Vector3(Mathf.Clamp(p.x, x, x+1f), y, Mathf.Clamp(p.z, z, z+1f));
|
||||
|
||||
// Convert to world space
|
||||
return gg.transform.Transform(closestInGraphSpace);
|
||||
}
|
||||
|
||||
public override bool GetPortal (GraphNode other, List<Vector3> left, List<Vector3> right, bool backwards) {
|
||||
if (backwards) return true;
|
||||
|
||||
GridGraph gg = GetGridGraph(GraphIndex);
|
||||
int[] neighbourOffsets = gg.neighbourOffsets;
|
||||
GridNode[] nodes = gg.nodes;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (HasConnectionInDirection(i) && other == nodes[NodeInGridIndex + neighbourOffsets[i]]) {
|
||||
Vector3 middle = ((Vector3)(position + other.position))*0.5f;
|
||||
Vector3 cross = Vector3.Cross(gg.collision.up, (Vector3)(other.position-position));
|
||||
cross.Normalize();
|
||||
cross *= gg.nodeSize*0.5f;
|
||||
left.Add(middle - cross);
|
||||
right.Add(middle + cross);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 4; i < 8; i++) {
|
||||
if (HasConnectionInDirection(i) && other == nodes[NodeInGridIndex + neighbourOffsets[i]]) {
|
||||
bool rClear = false;
|
||||
bool lClear = false;
|
||||
if (HasConnectionInDirection(i-4)) {
|
||||
GridNode n2 = nodes[NodeInGridIndex + neighbourOffsets[i-4]];
|
||||
if (n2.Walkable && n2.HasConnectionInDirection((i-4+1)%4)) {
|
||||
rClear = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasConnectionInDirection((i-4+1)%4)) {
|
||||
GridNode n2 = nodes[NodeInGridIndex + neighbourOffsets[(i-4+1)%4]];
|
||||
if (n2.Walkable && n2.HasConnectionInDirection(i-4)) {
|
||||
lClear = true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 middle = ((Vector3)(position + other.position))*0.5f;
|
||||
Vector3 cross = Vector3.Cross(gg.collision.up, (Vector3)(other.position-position));
|
||||
cross.Normalize();
|
||||
cross *= gg.nodeSize*1.4142f;
|
||||
left.Add(middle - (lClear ? cross : Vector3.zero));
|
||||
right.Add(middle + (rClear ? cross : Vector3.zero));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
|
||||
GridGraph gg = GetGridGraph(GraphIndex);
|
||||
|
||||
int[] neighbourOffsets = gg.neighbourOffsets;
|
||||
GridNode[] nodes = gg.nodes;
|
||||
|
||||
pathNode.UpdateG(path);
|
||||
handler.heap.Add(pathNode);
|
||||
|
||||
ushort pid = handler.PathID;
|
||||
var index = NodeInGridIndex;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (HasConnectionInDirection(i)) {
|
||||
GridNode other = nodes[index + neighbourOffsets[i]];
|
||||
PathNode otherPN = handler.GetPathNode(other);
|
||||
if (otherPN.parent == pathNode && otherPN.pathID == pid) other.UpdateRecursiveG(path, otherPN, handler);
|
||||
}
|
||||
}
|
||||
|
||||
#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
|
||||
base.UpdateRecursiveG(path, pathNode, handler);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public override void Open (Path path, PathNode pathNode, PathHandler handler) {
|
||||
GridGraph gg = GetGridGraph(GraphIndex);
|
||||
|
||||
ushort pid = handler.PathID;
|
||||
|
||||
{
|
||||
int[] neighbourOffsets = gg.neighbourOffsets;
|
||||
uint[] neighbourCosts = gg.neighbourCosts;
|
||||
GridNode[] nodes = gg.nodes;
|
||||
var index = NodeInGridIndex;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (HasConnectionInDirection(i)) {
|
||||
GridNode other = nodes[index + neighbourOffsets[i]];
|
||||
if (!path.CanTraverse(other)) continue;
|
||||
|
||||
PathNode otherPN = handler.GetPathNode(other);
|
||||
|
||||
uint tmpCost = neighbourCosts[i];
|
||||
|
||||
// Check if the other node has not yet been visited by this path
|
||||
if (otherPN.pathID != pid) {
|
||||
otherPN.parent = pathNode;
|
||||
otherPN.pathID = pid;
|
||||
|
||||
otherPN.cost = tmpCost;
|
||||
|
||||
otherPN.H = path.CalculateHScore(other);
|
||||
otherPN.UpdateG(path);
|
||||
|
||||
handler.heap.Add(otherPN);
|
||||
} else {
|
||||
// Sorry for the huge number of #ifs
|
||||
|
||||
//If not we can test if the path from the current node to this one is a better one then the one already used
|
||||
|
||||
#if ASTAR_NO_TRAVERSAL_COST
|
||||
if (pathNode.G+tmpCost < otherPN.G)
|
||||
#else
|
||||
if (pathNode.G+tmpCost+path.GetTraversalCost(other) < otherPN.G)
|
||||
#endif
|
||||
{
|
||||
//Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G);
|
||||
otherPN.cost = tmpCost;
|
||||
|
||||
otherPN.parent = pathNode;
|
||||
|
||||
other.UpdateRecursiveG(path, otherPN, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
|
||||
base.Open(path, pathNode, handler);
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void SerializeNode (GraphSerializationContext ctx) {
|
||||
base.SerializeNode(ctx);
|
||||
ctx.SerializeInt3(position);
|
||||
ctx.writer.Write(gridFlags);
|
||||
}
|
||||
|
||||
public override void DeserializeNode (GraphSerializationContext ctx) {
|
||||
base.DeserializeNode(ctx);
|
||||
position = ctx.DeserializeInt3();
|
||||
gridFlags = ctx.reader.ReadUInt16();
|
||||
}
|
||||
#else
|
||||
public override void AddConnection (GraphNode node, uint cost) {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void ClearConnections (bool alsoReverse) {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void GetConnections (GraphNodeDelegate del) {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Open (Path path, PathNode pathNode, PathHandler handler) {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override void RemoveConnection (GraphNode node) {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
7
AR/Assets/AstarPathfindingProject/Generators/NodeClasses/GridNode.cs.meta
generated
Normal file
7
AR/Assets/AstarPathfindingProject/Generators/NodeClasses/GridNode.cs.meta
generated
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5291f1ec332d746138ac025aecb1e12d
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
@ -0,0 +1,362 @@
|
||||
using UnityEngine;
|
||||
using Pathfinding.Serialization;
|
||||
|
||||
namespace Pathfinding {
|
||||
/// <summary>Base class for GridNode and LevelGridNode</summary>
|
||||
public abstract class GridNodeBase : GraphNode {
|
||||
protected GridNodeBase (AstarPath astar) : base(astar) {
|
||||
}
|
||||
|
||||
const int GridFlagsWalkableErosionOffset = 8;
|
||||
const int GridFlagsWalkableErosionMask = 1 << GridFlagsWalkableErosionOffset;
|
||||
|
||||
const int GridFlagsWalkableTmpOffset = 9;
|
||||
const int GridFlagsWalkableTmpMask = 1 << GridFlagsWalkableTmpOffset;
|
||||
|
||||
protected const int NodeInGridIndexLayerOffset = 24;
|
||||
protected const int NodeInGridIndexMask = 0xFFFFFF;
|
||||
|
||||
/// <summary>
|
||||
/// Bitfield containing the x and z coordinates of the node as well as the layer (for layered grid graphs).
|
||||
/// See: NodeInGridIndex
|
||||
/// </summary>
|
||||
protected int nodeInGridIndex;
|
||||
protected ushort gridFlags;
|
||||
|
||||
#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
|
||||
/// <summary>
|
||||
/// Custon non-grid connections from this node.
|
||||
/// See: <see cref="AddConnection"/>
|
||||
/// See: <see cref="RemoveConnection"/>
|
||||
///
|
||||
/// This field is removed if the ASTAR_GRID_NO_CUSTOM_CONNECTIONS compiler directive is used.
|
||||
/// Removing it can save a tiny bit of memory. You can enable the define in the Optimizations tab in the A* inspector.
|
||||
/// See: compiler-directives (view in online documentation for working links)
|
||||
///
|
||||
/// Note: If you modify this array or the contents of it you must call <see cref="SetConnectivityDirty"/>.
|
||||
/// </summary>
|
||||
public Connection[] connections;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The index of the node in the grid.
|
||||
/// This is x + z*graph.width
|
||||
/// So you can get the X and Z indices using
|
||||
/// <code>
|
||||
/// int index = node.NodeInGridIndex;
|
||||
/// int x = index % graph.width;
|
||||
/// int z = index / graph.width;
|
||||
/// // where graph is GridNode.GetGridGraph (node.graphIndex), i.e the graph the nodes are contained in.
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public int NodeInGridIndex { get { return nodeInGridIndex & NodeInGridIndexMask; } set { nodeInGridIndex = (nodeInGridIndex & ~NodeInGridIndexMask) | value; } }
|
||||
|
||||
/// <summary>
|
||||
/// X coordinate of the node in the grid.
|
||||
/// The node in the bottom left corner has (x,z) = (0,0) and the one in the opposite
|
||||
/// corner has (x,z) = (width-1, depth-1)
|
||||
/// See: ZCoordInGrid
|
||||
/// See: NodeInGridIndex
|
||||
/// </summary>
|
||||
public int XCoordinateInGrid {
|
||||
get {
|
||||
return NodeInGridIndex % GridNode.GetGridGraph(GraphIndex).width;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Z coordinate of the node in the grid.
|
||||
/// The node in the bottom left corner has (x,z) = (0,0) and the one in the opposite
|
||||
/// corner has (x,z) = (width-1, depth-1)
|
||||
/// See: XCoordInGrid
|
||||
/// See: NodeInGridIndex
|
||||
/// </summary>
|
||||
public int ZCoordinateInGrid {
|
||||
get {
|
||||
return NodeInGridIndex / GridNode.GetGridGraph(GraphIndex).width;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores walkability before erosion is applied.
|
||||
/// Used internally when updating the graph.
|
||||
/// </summary>
|
||||
public bool WalkableErosion {
|
||||
get {
|
||||
return (gridFlags & GridFlagsWalkableErosionMask) != 0;
|
||||
}
|
||||
set {
|
||||
unchecked { gridFlags = (ushort)(gridFlags & ~GridFlagsWalkableErosionMask | (value ? (ushort)GridFlagsWalkableErosionMask : (ushort)0)); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Temporary variable used internally when updating the graph.</summary>
|
||||
public bool TmpWalkable {
|
||||
get {
|
||||
return (gridFlags & GridFlagsWalkableTmpMask) != 0;
|
||||
}
|
||||
set {
|
||||
unchecked { gridFlags = (ushort)(gridFlags & ~GridFlagsWalkableTmpMask | (value ? (ushort)GridFlagsWalkableTmpMask : (ushort)0)); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the node has grid connections to all its 8 neighbours.
|
||||
/// Note: This will always return false if GridGraph.neighbours is set to anything other than Eight.
|
||||
/// See: GetNeighbourAlongDirection
|
||||
/// </summary>
|
||||
public abstract bool HasConnectionsToAllEightNeighbours { get; }
|
||||
|
||||
public override float SurfaceArea () {
|
||||
GridGraph gg = GridNode.GetGridGraph(GraphIndex);
|
||||
|
||||
return gg.nodeSize*gg.nodeSize;
|
||||
}
|
||||
|
||||
public override Vector3 RandomPointOnSurface () {
|
||||
GridGraph gg = GridNode.GetGridGraph(GraphIndex);
|
||||
|
||||
var graphSpacePosition = gg.transform.InverseTransform((Vector3)position);
|
||||
|
||||
return gg.transform.Transform(graphSpacePosition + new Vector3(Random.value - 0.5f, 0, Random.value - 0.5f));
|
||||
}
|
||||
|
||||
public override int GetGizmoHashCode () {
|
||||
var hash = base.GetGizmoHashCode();
|
||||
|
||||
#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
|
||||
if (connections != null) {
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
hash ^= 17 * connections[i].GetHashCode();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
hash ^= 109 * gridFlags;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjacent grid node in the specified direction.
|
||||
/// This will return null if the node does not have a connection to a node
|
||||
/// in that direction.
|
||||
///
|
||||
/// The dir parameter corresponds to directions in the grid as:
|
||||
/// <code>
|
||||
/// Z
|
||||
/// |
|
||||
/// |
|
||||
///
|
||||
/// 6 2 5
|
||||
/// \ | /
|
||||
/// -- 3 - X - 1 ----- X
|
||||
/// / | \
|
||||
/// 7 0 4
|
||||
///
|
||||
/// |
|
||||
/// |
|
||||
/// </code>
|
||||
///
|
||||
/// See: GetConnections
|
||||
/// </summary>
|
||||
public abstract GridNodeBase GetNeighbourAlongDirection(int direction);
|
||||
|
||||
public override bool ContainsConnection (GraphNode node) {
|
||||
#if !ASTAR_GRID_NO_CUSTOM_CONNECTIONS
|
||||
if (connections != null) {
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
if (connections[i].node == node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (node == GetNeighbourAlongDirection(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if ASTAR_GRID_NO_CUSTOM_CONNECTIONS
|
||||
public override void AddConnection (GraphNode node, uint cost) {
|
||||
throw new System.NotImplementedException("GridNodes do not have support for adding manual connections with your current settings."+
|
||||
"\nPlease disable ASTAR_GRID_NO_CUSTOM_CONNECTIONS in the Optimizations tab in the A* Inspector");
|
||||
}
|
||||
|
||||
public override void RemoveConnection (GraphNode node) {
|
||||
throw new System.NotImplementedException("GridNodes do not have support for adding manual connections with your current settings."+
|
||||
"\nPlease disable ASTAR_GRID_NO_CUSTOM_CONNECTIONS in the Optimizations tab in the A* Inspector");
|
||||
}
|
||||
|
||||
public void ClearCustomConnections (bool alsoReverse) {
|
||||
}
|
||||
#else
|
||||
/// <summary>Same as <see cref="ClearConnections"/>, but does not clear grid connections, only custom ones (e.g added by <see cref="AddConnection"/> or a NodeLink component)</summary>
|
||||
public void ClearCustomConnections (bool alsoReverse) {
|
||||
if (connections != null) for (int i = 0; i < connections.Length; i++) connections[i].node.RemoveConnection(this);
|
||||
connections = null;
|
||||
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
|
||||
}
|
||||
|
||||
public override void ClearConnections (bool alsoReverse) {
|
||||
ClearCustomConnections(alsoReverse);
|
||||
}
|
||||
|
||||
public override void GetConnections (System.Action<GraphNode> action) {
|
||||
if (connections != null) for (int i = 0; i < connections.Length; i++) action(connections[i].node);
|
||||
}
|
||||
|
||||
public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
|
||||
ushort pid = handler.PathID;
|
||||
|
||||
if (connections != null) for (int i = 0; i < connections.Length; i++) {
|
||||
GraphNode other = connections[i].node;
|
||||
PathNode otherPN = handler.GetPathNode(other);
|
||||
if (otherPN.parent == pathNode && otherPN.pathID == pid) other.UpdateRecursiveG(path, otherPN, handler);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Open (Path path, PathNode pathNode, PathHandler handler) {
|
||||
ushort pid = handler.PathID;
|
||||
|
||||
if (connections != null) for (int i = 0; i < connections.Length; i++) {
|
||||
GraphNode other = connections[i].node;
|
||||
if (!path.CanTraverse(other)) continue;
|
||||
|
||||
PathNode otherPN = handler.GetPathNode(other);
|
||||
|
||||
uint tmpCost = connections[i].cost;
|
||||
|
||||
if (otherPN.pathID != pid) {
|
||||
otherPN.parent = pathNode;
|
||||
otherPN.pathID = pid;
|
||||
|
||||
otherPN.cost = tmpCost;
|
||||
|
||||
otherPN.H = path.CalculateHScore(other);
|
||||
otherPN.UpdateG(path);
|
||||
|
||||
//Debug.Log ("G " + otherPN.G + " F " + otherPN.F);
|
||||
handler.heap.Add(otherPN);
|
||||
//Debug.DrawRay ((Vector3)otherPN.node.Position, Vector3.up,Color.blue);
|
||||
} else {
|
||||
// Sorry for the huge number of #ifs
|
||||
|
||||
//If not we can test if the path from the current node to this one is a better one then the one already used
|
||||
|
||||
#if ASTAR_NO_TRAVERSAL_COST
|
||||
if (pathNode.G+tmpCost < otherPN.G)
|
||||
#else
|
||||
if (pathNode.G+tmpCost+path.GetTraversalCost(other) < otherPN.G)
|
||||
#endif
|
||||
{
|
||||
//Debug.Log ("Path better from " + NodeIndex + " to " + otherPN.node.NodeIndex + " " + (pathNode.G+tmpCost+path.GetTraversalCost(other)) + " < " + otherPN.G);
|
||||
otherPN.cost = tmpCost;
|
||||
|
||||
otherPN.parent = pathNode;
|
||||
|
||||
other.UpdateRecursiveG(path, otherPN, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a connection from this node to the specified node.
|
||||
/// If the connection already exists, the cost will simply be updated and
|
||||
/// no extra connection added.
|
||||
///
|
||||
/// Note: Only adds a one-way connection. Consider calling the same function on the other node
|
||||
/// to get a two-way connection.
|
||||
/// </summary>
|
||||
public override void AddConnection (GraphNode node, uint cost) {
|
||||
if (node == null) throw new System.ArgumentNullException();
|
||||
|
||||
if (connections != null) {
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
if (connections[i].node == node) {
|
||||
connections[i].cost = cost;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int connLength = connections != null ? connections.Length : 0;
|
||||
|
||||
var newconns = new Connection[connLength+1];
|
||||
for (int i = 0; i < connLength; i++) {
|
||||
newconns[i] = connections[i];
|
||||
}
|
||||
|
||||
newconns[connLength] = new Connection(node, cost);
|
||||
|
||||
connections = newconns;
|
||||
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any connection from this node to the specified node.
|
||||
/// If no such connection exists, nothing will be done.
|
||||
///
|
||||
/// Note: This only removes the connection from this node to the other node.
|
||||
/// You may want to call the same function on the other node to remove its eventual connection
|
||||
/// to this node.
|
||||
/// </summary>
|
||||
public override void RemoveConnection (GraphNode node) {
|
||||
if (connections == null) return;
|
||||
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
if (connections[i].node == node) {
|
||||
int connLength = connections.Length;
|
||||
|
||||
var newconns = new Connection[connLength-1];
|
||||
for (int j = 0; j < i; j++) {
|
||||
newconns[j] = connections[j];
|
||||
}
|
||||
for (int j = i+1; j < connLength; j++) {
|
||||
newconns[j-1] = connections[j];
|
||||
}
|
||||
|
||||
connections = newconns;
|
||||
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SerializeReferences (GraphSerializationContext ctx) {
|
||||
// TODO: Deduplicate code
|
||||
if (connections == null) {
|
||||
ctx.writer.Write(-1);
|
||||
} else {
|
||||
ctx.writer.Write(connections.Length);
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
ctx.SerializeNodeReference(connections[i].node);
|
||||
ctx.writer.Write(connections[i].cost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DeserializeReferences (GraphSerializationContext ctx) {
|
||||
// Grid nodes didn't serialize references before 3.8.3
|
||||
if (ctx.meta.version < AstarSerializer.V3_8_3)
|
||||
return;
|
||||
|
||||
int count = ctx.reader.ReadInt32();
|
||||
|
||||
if (count == -1) {
|
||||
connections = null;
|
||||
} else {
|
||||
connections = new Connection[count];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
connections[i] = new Connection(ctx.DeserializeNodeReference(), ctx.reader.ReadUInt32());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
12
AR/Assets/AstarPathfindingProject/Generators/NodeClasses/GridNodeBase.cs.meta
generated
Normal file
12
AR/Assets/AstarPathfindingProject/Generators/NodeClasses/GridNodeBase.cs.meta
generated
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b723ddfaa37b46eca23cd8d042ad3e9
|
||||
timeCreated: 1459629300
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,272 @@
|
||||
using UnityEngine;
|
||||
using Pathfinding.Serialization;
|
||||
|
||||
namespace Pathfinding {
|
||||
/// <summary>
|
||||
/// Node used for the PointGraph.
|
||||
/// This is just a simple point with a list of connections (and associated costs) to other nodes.
|
||||
/// It does not have any concept of a surface like many other node types.
|
||||
///
|
||||
/// See: PointGraph
|
||||
/// </summary>
|
||||
public class PointNode : GraphNode {
|
||||
/// <summary>
|
||||
/// All connections from this node.
|
||||
/// See: <see cref="AddConnection"/>
|
||||
/// See: <see cref="RemoveConnection"/>
|
||||
///
|
||||
/// Note: If you modify this array or the contents of it you must call <see cref="SetConnectivityDirty"/>.
|
||||
///
|
||||
/// Note: If you modify this array or the contents of it you must call <see cref="PointGraph.RegisterConnectionLength"/> with the length of the new connections.
|
||||
/// </summary>
|
||||
public Connection[] connections;
|
||||
|
||||
/// <summary>
|
||||
/// GameObject this node was created from (if any).
|
||||
/// Warning: When loading a graph from a saved file or from cache, this field will be null.
|
||||
///
|
||||
/// <code>
|
||||
/// var node = AstarPath.active.GetNearest(transform.position).node;
|
||||
/// var pointNode = node as PointNode;
|
||||
///
|
||||
/// if (pointNode != null) {
|
||||
/// Debug.Log("That node was created from the GameObject named " + pointNode.gameObject.name);
|
||||
/// } else {
|
||||
/// Debug.Log("That node is not a PointNode");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public GameObject gameObject;
|
||||
|
||||
public void SetPosition (Int3 value) {
|
||||
position = value;
|
||||
}
|
||||
|
||||
public PointNode (AstarPath astar) : base(astar) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closest point on the surface of this node to the point p.
|
||||
///
|
||||
/// For a point node this is always the node's <see cref="position"/> sicne it has no surface.
|
||||
/// </summary>
|
||||
public override Vector3 ClosestPointOnNode (Vector3 p) {
|
||||
return (Vector3)this.position;
|
||||
}
|
||||
|
||||
public override void GetConnections (System.Action<GraphNode> action) {
|
||||
if (connections == null) return;
|
||||
for (int i = 0; i < connections.Length; i++) action(connections[i].node);
|
||||
}
|
||||
|
||||
public override void ClearConnections (bool alsoReverse) {
|
||||
if (alsoReverse && connections != null) {
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
connections[i].node.RemoveConnection(this);
|
||||
}
|
||||
}
|
||||
|
||||
connections = null;
|
||||
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
|
||||
}
|
||||
|
||||
public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
|
||||
pathNode.UpdateG(path);
|
||||
|
||||
handler.heap.Add(pathNode);
|
||||
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
GraphNode other = connections[i].node;
|
||||
PathNode otherPN = handler.GetPathNode(other);
|
||||
if (otherPN.parent == pathNode && otherPN.pathID == handler.PathID) {
|
||||
other.UpdateRecursiveG(path, otherPN, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ContainsConnection (GraphNode node) {
|
||||
if (connections == null) return false;
|
||||
for (int i = 0; i < connections.Length; i++) if (connections[i].node == node) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a connection from this node to the specified node.
|
||||
/// If the connection already exists, the cost will simply be updated and
|
||||
/// no extra connection added.
|
||||
///
|
||||
/// Note: Only adds a one-way connection. Consider calling the same function on the other node
|
||||
/// to get a two-way connection.
|
||||
///
|
||||
/// <code>
|
||||
/// AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => {
|
||||
/// // Connect two nodes
|
||||
/// var node1 = AstarPath.active.GetNearest(transform.position, NNConstraint.None).node;
|
||||
/// var node2 = AstarPath.active.GetNearest(transform.position + Vector3.right, NNConstraint.None).node;
|
||||
/// var cost = (uint)(node2.position - node1.position).costMagnitude;
|
||||
/// node1.AddConnection(node2, cost);
|
||||
/// node2.AddConnection(node1, cost);
|
||||
///
|
||||
/// node1.ContainsConnection(node2); // True
|
||||
///
|
||||
/// node1.RemoveConnection(node2);
|
||||
/// node2.RemoveConnection(node1);
|
||||
/// }));
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public override void AddConnection (GraphNode node, uint cost) {
|
||||
if (node == null) throw new System.ArgumentNullException();
|
||||
|
||||
if (connections != null) {
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
if (connections[i].node == node) {
|
||||
connections[i].cost = cost;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int connLength = connections != null ? connections.Length : 0;
|
||||
|
||||
var newconns = new Connection[connLength+1];
|
||||
for (int i = 0; i < connLength; i++) {
|
||||
newconns[i] = connections[i];
|
||||
}
|
||||
|
||||
newconns[connLength] = new Connection(node, cost);
|
||||
|
||||
connections = newconns;
|
||||
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
|
||||
|
||||
// Make sure the graph knows that there exists a connection with this length
|
||||
(this.Graph as PointGraph).RegisterConnectionLength((node.position - position).sqrMagnitudeLong);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any connection from this node to the specified node.
|
||||
/// If no such connection exists, nothing will be done.
|
||||
///
|
||||
/// Note: This only removes the connection from this node to the other node.
|
||||
/// You may want to call the same function on the other node to remove its possible connection
|
||||
/// to this node.
|
||||
///
|
||||
/// <code>
|
||||
/// AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => {
|
||||
/// // Connect two nodes
|
||||
/// var node1 = AstarPath.active.GetNearest(transform.position, NNConstraint.None).node;
|
||||
/// var node2 = AstarPath.active.GetNearest(transform.position + Vector3.right, NNConstraint.None).node;
|
||||
/// var cost = (uint)(node2.position - node1.position).costMagnitude;
|
||||
/// node1.AddConnection(node2, cost);
|
||||
/// node2.AddConnection(node1, cost);
|
||||
///
|
||||
/// node1.ContainsConnection(node2); // True
|
||||
///
|
||||
/// node1.RemoveConnection(node2);
|
||||
/// node2.RemoveConnection(node1);
|
||||
/// }));
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public override void RemoveConnection (GraphNode node) {
|
||||
if (connections == null) return;
|
||||
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
if (connections[i].node == node) {
|
||||
int connLength = connections.Length;
|
||||
|
||||
var newconns = new Connection[connLength-1];
|
||||
for (int j = 0; j < i; j++) {
|
||||
newconns[j] = connections[j];
|
||||
}
|
||||
for (int j = i+1; j < connLength; j++) {
|
||||
newconns[j-1] = connections[j];
|
||||
}
|
||||
|
||||
connections = newconns;
|
||||
AstarPath.active.hierarchicalGraph.AddDirtyNode(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Open (Path path, PathNode pathNode, PathHandler handler) {
|
||||
if (connections == null) return;
|
||||
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
GraphNode other = connections[i].node;
|
||||
|
||||
if (path.CanTraverse(other)) {
|
||||
PathNode pathOther = handler.GetPathNode(other);
|
||||
|
||||
if (pathOther.pathID != handler.PathID) {
|
||||
pathOther.parent = pathNode;
|
||||
pathOther.pathID = handler.PathID;
|
||||
|
||||
pathOther.cost = connections[i].cost;
|
||||
|
||||
pathOther.H = path.CalculateHScore(other);
|
||||
pathOther.UpdateG(path);
|
||||
|
||||
handler.heap.Add(pathOther);
|
||||
} else {
|
||||
//If not we can test if the path from this node to the other one is a better one then the one already used
|
||||
uint tmpCost = connections[i].cost;
|
||||
|
||||
if (pathNode.G + tmpCost + path.GetTraversalCost(other) < pathOther.G) {
|
||||
pathOther.cost = tmpCost;
|
||||
pathOther.parent = pathNode;
|
||||
|
||||
other.UpdateRecursiveG(path, pathOther, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetGizmoHashCode () {
|
||||
var hash = base.GetGizmoHashCode();
|
||||
|
||||
if (connections != null) {
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
hash ^= 17 * connections[i].GetHashCode();
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override void SerializeNode (GraphSerializationContext ctx) {
|
||||
base.SerializeNode(ctx);
|
||||
ctx.SerializeInt3(position);
|
||||
}
|
||||
|
||||
public override void DeserializeNode (GraphSerializationContext ctx) {
|
||||
base.DeserializeNode(ctx);
|
||||
position = ctx.DeserializeInt3();
|
||||
}
|
||||
|
||||
public override void SerializeReferences (GraphSerializationContext ctx) {
|
||||
if (connections == null) {
|
||||
ctx.writer.Write(-1);
|
||||
} else {
|
||||
ctx.writer.Write(connections.Length);
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
ctx.SerializeNodeReference(connections[i].node);
|
||||
ctx.writer.Write(connections[i].cost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DeserializeReferences (GraphSerializationContext ctx) {
|
||||
int count = ctx.reader.ReadInt32();
|
||||
|
||||
if (count == -1) {
|
||||
connections = null;
|
||||
} else {
|
||||
connections = new Connection[count];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
connections[i] = new Connection(ctx.DeserializeNodeReference(), ctx.reader.ReadUInt32());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
AR/Assets/AstarPathfindingProject/Generators/NodeClasses/PointNode.cs.meta
generated
Normal file
8
AR/Assets/AstarPathfindingProject/Generators/NodeClasses/PointNode.cs.meta
generated
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 986ad6174b59e40068c715a916740ce9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@ -0,0 +1,460 @@
|
||||
using UnityEngine;
|
||||
using Pathfinding.Serialization;
|
||||
|
||||
namespace Pathfinding {
|
||||
/// <summary>Interface for something that holds a triangle based navmesh</summary>
|
||||
public interface INavmeshHolder : ITransformedGraph, INavmesh {
|
||||
/// <summary>Position of vertex number i in the world</summary>
|
||||
Int3 GetVertex(int i);
|
||||
|
||||
/// <summary>
|
||||
/// Position of vertex number i in coordinates local to the graph.
|
||||
/// The up direction is always the +Y axis for these coordinates.
|
||||
/// </summary>
|
||||
Int3 GetVertexInGraphSpace(int i);
|
||||
|
||||
int GetVertexArrayIndex(int index);
|
||||
|
||||
/// <summary>Transforms coordinates from graph space to world space</summary>
|
||||
void GetTileCoordinates(int tileIndex, out int x, out int z);
|
||||
}
|
||||
|
||||
/// <summary>Node represented by a triangle</summary>
|
||||
public class TriangleMeshNode : MeshNode {
|
||||
public TriangleMeshNode (AstarPath astar) : base(astar) {}
|
||||
|
||||
/// <summary>Internal vertex index for the first vertex</summary>
|
||||
public int v0;
|
||||
|
||||
/// <summary>Internal vertex index for the second vertex</summary>
|
||||
public int v1;
|
||||
|
||||
/// <summary>Internal vertex index for the third vertex</summary>
|
||||
public int v2;
|
||||
|
||||
/// <summary>Holds INavmeshHolder references for all graph indices to be able to access them in a performant manner</summary>
|
||||
protected static INavmeshHolder[] _navmeshHolders = new INavmeshHolder[0];
|
||||
|
||||
/// <summary>Used for synchronised access to the <see cref="_navmeshHolders"/> array</summary>
|
||||
protected static readonly System.Object lockObject = new System.Object();
|
||||
|
||||
public static INavmeshHolder GetNavmeshHolder (uint graphIndex) {
|
||||
return _navmeshHolders[(int)graphIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the internal navmesh holder for a given graph index.
|
||||
/// Warning: Internal method
|
||||
/// </summary>
|
||||
public static void SetNavmeshHolder (int graphIndex, INavmeshHolder graph) {
|
||||
// We need to lock to make sure that
|
||||
// the resize operation is thread safe
|
||||
lock (lockObject) {
|
||||
if (graphIndex >= _navmeshHolders.Length) {
|
||||
var gg = new INavmeshHolder[graphIndex+1];
|
||||
_navmeshHolders.CopyTo(gg, 0);
|
||||
_navmeshHolders = gg;
|
||||
}
|
||||
_navmeshHolders[graphIndex] = graph;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Set the position of this node to the average of its 3 vertices</summary>
|
||||
public void UpdatePositionFromVertices () {
|
||||
Int3 a, b, c;
|
||||
|
||||
GetVertices(out a, out b, out c);
|
||||
position = (a + b + c) * 0.333333f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a number identifying a vertex.
|
||||
/// This number does not necessarily need to be a index in an array but two different vertices (in the same graph) should
|
||||
/// not have the same vertex numbers.
|
||||
/// </summary>
|
||||
public int GetVertexIndex (int i) {
|
||||
return i == 0 ? v0 : (i == 1 ? v1 : v2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a number specifying an index in the source vertex array.
|
||||
/// The vertex array can for example be contained in a recast tile, or be a navmesh graph, that is graph dependant.
|
||||
/// This is slower than GetVertexIndex, if you only need to compare vertices, use GetVertexIndex.
|
||||
/// </summary>
|
||||
public int GetVertexArrayIndex (int i) {
|
||||
return GetNavmeshHolder(GraphIndex).GetVertexArrayIndex(i == 0 ? v0 : (i == 1 ? v1 : v2));
|
||||
}
|
||||
|
||||
/// <summary>Returns all 3 vertices of this node in world space</summary>
|
||||
public void GetVertices (out Int3 v0, out Int3 v1, out Int3 v2) {
|
||||
// Get the object holding the vertex data for this node
|
||||
// This is usually a graph or a recast graph tile
|
||||
var holder = GetNavmeshHolder(GraphIndex);
|
||||
|
||||
v0 = holder.GetVertex(this.v0);
|
||||
v1 = holder.GetVertex(this.v1);
|
||||
v2 = holder.GetVertex(this.v2);
|
||||
}
|
||||
|
||||
/// <summary>Returns all 3 vertices of this node in graph space</summary>
|
||||
public void GetVerticesInGraphSpace (out Int3 v0, out Int3 v1, out Int3 v2) {
|
||||
// Get the object holding the vertex data for this node
|
||||
// This is usually a graph or a recast graph tile
|
||||
var holder = GetNavmeshHolder(GraphIndex);
|
||||
|
||||
v0 = holder.GetVertexInGraphSpace(this.v0);
|
||||
v1 = holder.GetVertexInGraphSpace(this.v1);
|
||||
v2 = holder.GetVertexInGraphSpace(this.v2);
|
||||
}
|
||||
|
||||
public override Int3 GetVertex (int i) {
|
||||
return GetNavmeshHolder(GraphIndex).GetVertex(GetVertexIndex(i));
|
||||
}
|
||||
|
||||
public Int3 GetVertexInGraphSpace (int i) {
|
||||
return GetNavmeshHolder(GraphIndex).GetVertexInGraphSpace(GetVertexIndex(i));
|
||||
}
|
||||
|
||||
public override int GetVertexCount () {
|
||||
// A triangle has 3 vertices
|
||||
return 3;
|
||||
}
|
||||
|
||||
public override Vector3 ClosestPointOnNode (Vector3 p) {
|
||||
Int3 a, b, c;
|
||||
|
||||
GetVertices(out a, out b, out c);
|
||||
return Pathfinding.Polygon.ClosestPointOnTriangle((Vector3)a, (Vector3)b, (Vector3)c, p);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closest point on the node when seen from above.
|
||||
/// This method is mostly for internal use as the <see cref="Pathfinding.NavmeshBase.Linecast"/> methods use it.
|
||||
///
|
||||
/// - The returned point is the closest one on the node to p when seen from above (relative to the graph).
|
||||
/// This is important mostly for sloped surfaces.
|
||||
/// - The returned point is an Int3 point in graph space.
|
||||
/// - It is guaranteed to be inside the node, so if you call <see cref="ContainsPointInGraphSpace"/> with the return value from this method the result is guaranteed to be true.
|
||||
///
|
||||
/// This method is slower than e.g <see cref="ClosestPointOnNode"/> or <see cref="ClosestPointOnNodeXZ"/>.
|
||||
/// However they do not have the same guarantees as this method has.
|
||||
/// </summary>
|
||||
internal Int3 ClosestPointOnNodeXZInGraphSpace (Vector3 p) {
|
||||
// Get the vertices that make up the triangle
|
||||
Int3 a, b, c;
|
||||
|
||||
GetVerticesInGraphSpace(out a, out b, out c);
|
||||
|
||||
// Convert p to graph space
|
||||
p = GetNavmeshHolder(GraphIndex).transform.InverseTransform(p);
|
||||
|
||||
// Find the closest point on the triangle to p when looking at the triangle from above (relative to the graph)
|
||||
var closest = Pathfinding.Polygon.ClosestPointOnTriangleXZ((Vector3)a, (Vector3)b, (Vector3)c, p);
|
||||
|
||||
// Make sure the point is actually inside the node
|
||||
var i3closest = (Int3)closest;
|
||||
if (ContainsPointInGraphSpace(i3closest)) {
|
||||
// Common case
|
||||
return i3closest;
|
||||
} else {
|
||||
// Annoying...
|
||||
// The closest point when converted from floating point coordinates to integer coordinates
|
||||
// is not actually inside the node. It needs to be inside the node for some methods
|
||||
// (like for example Linecast) to work properly.
|
||||
|
||||
// Try the 8 integer coordinates around the closest point
|
||||
// and check if any one of them are completely inside the node.
|
||||
// This will most likely succeed as it should be very close.
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
for (int dz = -1; dz <= 1; dz++) {
|
||||
if ((dx != 0 || dz != 0)) {
|
||||
var candidate = new Int3(i3closest.x + dx, i3closest.y, i3closest.z + dz);
|
||||
if (ContainsPointInGraphSpace(candidate)) return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Happens veery rarely.
|
||||
// Pick the closest vertex of the triangle.
|
||||
// The vertex is guaranteed to be inside the triangle.
|
||||
var da = (a - i3closest).sqrMagnitudeLong;
|
||||
var db = (b - i3closest).sqrMagnitudeLong;
|
||||
var dc = (c - i3closest).sqrMagnitudeLong;
|
||||
return da < db ? (da < dc ? a : c) : (db < dc ? b : c);
|
||||
}
|
||||
}
|
||||
|
||||
public override Vector3 ClosestPointOnNodeXZ (Vector3 p) {
|
||||
// Get all 3 vertices for this node
|
||||
Int3 tp1, tp2, tp3;
|
||||
|
||||
GetVertices(out tp1, out tp2, out tp3);
|
||||
return Polygon.ClosestPointOnTriangleXZ((Vector3)tp1, (Vector3)tp2, (Vector3)tp3, p);
|
||||
}
|
||||
|
||||
public override bool ContainsPoint (Vector3 p) {
|
||||
return ContainsPointInGraphSpace((Int3)GetNavmeshHolder(GraphIndex).transform.InverseTransform(p));
|
||||
}
|
||||
|
||||
public override bool ContainsPointInGraphSpace (Int3 p) {
|
||||
// Get all 3 vertices for this node
|
||||
Int3 a, b, c;
|
||||
|
||||
GetVerticesInGraphSpace(out a, out b, out c);
|
||||
|
||||
if ((long)(b.x - a.x) * (long)(p.z - a.z) - (long)(p.x - a.x) * (long)(b.z - a.z) > 0) return false;
|
||||
|
||||
if ((long)(c.x - b.x) * (long)(p.z - b.z) - (long)(p.x - b.x) * (long)(c.z - b.z) > 0) return false;
|
||||
|
||||
if ((long)(a.x - c.x) * (long)(p.z - c.z) - (long)(p.x - c.x) * (long)(a.z - c.z) > 0) return false;
|
||||
|
||||
return true;
|
||||
// Equivalent code, but the above code is faster
|
||||
//return Polygon.IsClockwiseMargin (a,b, p) && Polygon.IsClockwiseMargin (b,c, p) && Polygon.IsClockwiseMargin (c,a, p);
|
||||
|
||||
//return Polygon.ContainsPoint(g.GetVertex(v0),g.GetVertex(v1),g.GetVertex(v2),p);
|
||||
}
|
||||
|
||||
public override void UpdateRecursiveG (Path path, PathNode pathNode, PathHandler handler) {
|
||||
pathNode.UpdateG(path);
|
||||
|
||||
handler.heap.Add(pathNode);
|
||||
|
||||
if (connections == null) return;
|
||||
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
GraphNode other = connections[i].node;
|
||||
PathNode otherPN = handler.GetPathNode(other);
|
||||
if (otherPN.parent == pathNode && otherPN.pathID == handler.PathID) other.UpdateRecursiveG(path, otherPN, handler);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Open (Path path, PathNode pathNode, PathHandler handler) {
|
||||
if (connections == null) return;
|
||||
|
||||
// Flag2 indicates if this node needs special treatment
|
||||
// with regard to connection costs
|
||||
bool flag2 = pathNode.flag2;
|
||||
|
||||
// Loop through all connections
|
||||
for (int i = connections.Length-1; i >= 0; i--) {
|
||||
var conn = connections[i];
|
||||
var other = conn.node;
|
||||
|
||||
// Make sure we can traverse the neighbour
|
||||
if (path.CanTraverse(conn.node)) {
|
||||
PathNode pathOther = handler.GetPathNode(conn.node);
|
||||
|
||||
// Fast path out, worth it for triangle mesh nodes since they usually have degree 2 or 3
|
||||
if (pathOther == pathNode.parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint cost = conn.cost;
|
||||
|
||||
if (flag2 || pathOther.flag2) {
|
||||
// Get special connection cost from the path
|
||||
// This is used by the start and end nodes
|
||||
cost = path.GetConnectionSpecialCost(this, conn.node, cost);
|
||||
}
|
||||
|
||||
// Test if we have seen the other node before
|
||||
if (pathOther.pathID != handler.PathID) {
|
||||
// We have not seen the other node before
|
||||
// So the path from the start through this node to the other node
|
||||
// must be the shortest one so far
|
||||
|
||||
// Might not be assigned
|
||||
pathOther.node = conn.node;
|
||||
|
||||
pathOther.parent = pathNode;
|
||||
pathOther.pathID = handler.PathID;
|
||||
|
||||
pathOther.cost = cost;
|
||||
|
||||
pathOther.H = path.CalculateHScore(other);
|
||||
pathOther.UpdateG(path);
|
||||
|
||||
handler.heap.Add(pathOther);
|
||||
} else {
|
||||
// If not we can test if the path from this node to the other one is a better one than the one already used
|
||||
if (pathNode.G + cost + path.GetTraversalCost(other) < pathOther.G) {
|
||||
pathOther.cost = cost;
|
||||
pathOther.parent = pathNode;
|
||||
|
||||
other.UpdateRecursiveG(path, pathOther, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the edge which is shared with other.
|
||||
/// If no edge is shared, -1 is returned.
|
||||
/// If there is a connection with the other node, but the connection is not marked as using a particular edge of the shape of the node
|
||||
/// then 0xFF will be returned.
|
||||
///
|
||||
/// The vertices in the edge can be retrieved using
|
||||
/// <code>
|
||||
/// var edge = node.SharedEdge(other);
|
||||
/// var a = node.GetVertex(edge);
|
||||
/// var b = node.GetVertex((edge+1) % node.GetVertexCount());
|
||||
/// </code>
|
||||
///
|
||||
/// See: <see cref="GetPortal"/> which also handles edges that are shared over tile borders and some types of node links
|
||||
/// </summary>
|
||||
public int SharedEdge (GraphNode other) {
|
||||
var edge = -1;
|
||||
|
||||
if (connections != null) {
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
if (connections[i].node == other) edge = connections[i].shapeEdge;
|
||||
}
|
||||
}
|
||||
return edge;
|
||||
}
|
||||
|
||||
public override bool GetPortal (GraphNode toNode, System.Collections.Generic.List<Vector3> left, System.Collections.Generic.List<Vector3> right, bool backwards) {
|
||||
int aIndex, bIndex;
|
||||
|
||||
return GetPortal(toNode, left, right, backwards, out aIndex, out bIndex);
|
||||
}
|
||||
|
||||
public bool GetPortal (GraphNode toNode, System.Collections.Generic.List<Vector3> left, System.Collections.Generic.List<Vector3> right, bool backwards, out int aIndex, out int bIndex) {
|
||||
aIndex = -1;
|
||||
bIndex = -1;
|
||||
|
||||
//If the nodes are in different graphs, this function has no idea on how to find a shared edge.
|
||||
if (backwards || toNode.GraphIndex != GraphIndex) return false;
|
||||
|
||||
// Since the nodes are in the same graph, they are both TriangleMeshNodes
|
||||
// So we don't need to care about other types of nodes
|
||||
var toTriNode = toNode as TriangleMeshNode;
|
||||
var edge = SharedEdge(toTriNode);
|
||||
|
||||
// A connection was found, but it specifically didn't use an edge
|
||||
if (edge == 0xFF) return false;
|
||||
|
||||
// No connection was found between the nodes
|
||||
// Check if there is a node link that connects them
|
||||
if (edge == -1) {
|
||||
#if !ASTAR_NO_POINT_GRAPH
|
||||
if (connections != null) {
|
||||
for (int i = 0; i < connections.Length; i++) {
|
||||
if (connections[i].node.GraphIndex != GraphIndex) {
|
||||
var mid = connections[i].node as NodeLink3Node;
|
||||
if (mid != null && mid.GetOther(this) == toTriNode) {
|
||||
// We have found a node which is connected through a NodeLink3Node
|
||||
mid.GetPortal(toTriNode, left, right, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
aIndex = edge;
|
||||
bIndex = (edge + 1) % GetVertexCount();
|
||||
|
||||
// Get the vertices of the shared edge for the first node
|
||||
Int3 v1a = GetVertex(edge);
|
||||
Int3 v1b = GetVertex((edge+1) % GetVertexCount());
|
||||
|
||||
// Get tile indices
|
||||
int tileIndex1 = (GetVertexIndex(0) >> NavmeshBase.TileIndexOffset) & NavmeshBase.TileIndexMask;
|
||||
int tileIndex2 = (toTriNode.GetVertexIndex(0) >> NavmeshBase.TileIndexOffset) & NavmeshBase.TileIndexMask;
|
||||
|
||||
if (tileIndex1 != tileIndex2) {
|
||||
// When the nodes are in different tiles, the edges might not be completely identical
|
||||
// so another technique is needed.
|
||||
|
||||
// Get the tile coordinates, from them we can figure out which edge is going to be shared
|
||||
int x1, x2, z1, z2, coord;
|
||||
INavmeshHolder nm = GetNavmeshHolder(GraphIndex);
|
||||
nm.GetTileCoordinates(tileIndex1, out x1, out z1);
|
||||
nm.GetTileCoordinates(tileIndex2, out x2, out z2);
|
||||
|
||||
if (System.Math.Abs(x1-x2) == 1) coord = 2;
|
||||
else if (System.Math.Abs(z1-z2) == 1) coord = 0;
|
||||
else return false; // Tiles are not adjacent. This is likely a custom connection between two nodes.
|
||||
|
||||
var otherEdge = toTriNode.SharedEdge(this);
|
||||
|
||||
// A connection was found, but it specifically didn't use an edge. This is odd since the connection in the other direction did use an edge
|
||||
if (otherEdge == 0xFF) throw new System.Exception("Connection used edge in one direction, but not in the other direction. Has the wrong overload of AddConnection been used?");
|
||||
|
||||
// If it is -1 then it must be a one-way connection. Fall back to using the whole edge
|
||||
if (otherEdge != -1) {
|
||||
// When the nodes are in different tiles, they might not share exactly the same edge
|
||||
// so we clamp the portal to the segment of the edges which they both have.
|
||||
int mincoord = System.Math.Min(v1a[coord], v1b[coord]);
|
||||
int maxcoord = System.Math.Max(v1a[coord], v1b[coord]);
|
||||
|
||||
// Get the vertices of the shared edge for the second node
|
||||
Int3 v2a = toTriNode.GetVertex(otherEdge);
|
||||
Int3 v2b = toTriNode.GetVertex((otherEdge+1) % toTriNode.GetVertexCount());
|
||||
|
||||
mincoord = System.Math.Max(mincoord, System.Math.Min(v2a[coord], v2b[coord]));
|
||||
maxcoord = System.Math.Min(maxcoord, System.Math.Max(v2a[coord], v2b[coord]));
|
||||
|
||||
if (v1a[coord] < v1b[coord]) {
|
||||
v1a[coord] = mincoord;
|
||||
v1b[coord] = maxcoord;
|
||||
} else {
|
||||
v1a[coord] = maxcoord;
|
||||
v1b[coord] = mincoord;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (left != null) {
|
||||
// All triangles should be laid out in clockwise order so v1b is the rightmost vertex (seen from this node)
|
||||
left.Add((Vector3)v1a);
|
||||
right.Add((Vector3)v1b);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>TODO: This is the area in XZ space, use full 3D space for higher correctness maybe?</summary>
|
||||
public override float SurfaceArea () {
|
||||
var holder = GetNavmeshHolder(GraphIndex);
|
||||
|
||||
return System.Math.Abs(VectorMath.SignedTriangleAreaTimes2XZ(holder.GetVertex(v0), holder.GetVertex(v1), holder.GetVertex(v2))) * 0.5f;
|
||||
}
|
||||
|
||||
public override Vector3 RandomPointOnSurface () {
|
||||
// Find a random point inside the triangle
|
||||
// This generates uniformly distributed trilinear coordinates
|
||||
// See http://mathworld.wolfram.com/TrianglePointPicking.html
|
||||
float r1;
|
||||
float r2;
|
||||
|
||||
do {
|
||||
r1 = Random.value;
|
||||
r2 = Random.value;
|
||||
} while (r1+r2 > 1);
|
||||
|
||||
var holder = GetNavmeshHolder(GraphIndex);
|
||||
// Pick the point corresponding to the trilinear coordinate
|
||||
return ((Vector3)(holder.GetVertex(v1)-holder.GetVertex(v0)))*r1 + ((Vector3)(holder.GetVertex(v2)-holder.GetVertex(v0)))*r2 + (Vector3)holder.GetVertex(v0);
|
||||
}
|
||||
|
||||
public override void SerializeNode (GraphSerializationContext ctx) {
|
||||
base.SerializeNode(ctx);
|
||||
ctx.writer.Write(v0);
|
||||
ctx.writer.Write(v1);
|
||||
ctx.writer.Write(v2);
|
||||
}
|
||||
|
||||
public override void DeserializeNode (GraphSerializationContext ctx) {
|
||||
base.DeserializeNode(ctx);
|
||||
v0 = ctx.reader.ReadInt32();
|
||||
v1 = ctx.reader.ReadInt32();
|
||||
v2 = ctx.reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
8
AR/Assets/AstarPathfindingProject/Generators/NodeClasses/TriangleMeshNode.cs.meta
generated
Normal file
8
AR/Assets/AstarPathfindingProject/Generators/NodeClasses/TriangleMeshNode.cs.meta
generated
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54908f58720324c048a5b475a27077fa
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
Reference in New Issue
Block a user