Code to create nodes and connections size is (2,2,2) for testing
using System;
using System.Collections.Generic;
using Pathfinding;
using Pathfinding.Util;
using UnityEngine;
using Pathfinding.Serialization;
[Pathfinding.Util.Preserve]
public class PathGraph2D : PointGraph {
[NonSerialized]
public PointNode[,,] nodeArray;
[JsonMember]
public GraphTransform transform;
[JsonMember]
public Vector3Int size3;
public void Init(Vector3Int aSize,Vector3 pos,float scale) {
size3 = aSize;
nodeArray = new PointNode[size3.x, size3.y,size3.z];
transform = new GraphTransform(Matrix4x4.TRS(pos, Quaternion.identity, Vector3.one*scale));
Scan();
}
class PathGraph2DScanPromise : IGraphUpdatePromise {
public PathGraph2D graph;
public PointNode[,,] nodeArray;
public GraphTransform transform;
public Vector3Int size3;
// In this method you may run async calculations required for updating the graph.
public IEnumerator<Unity.Jobs.JobHandle> Prepare() => null;
public void Apply(IGraphUpdateContext ctx) {
// Destroy previous nodes (if any)
graph.DestroyAllNodes();
List<PointNode> allNodes = new();
for (int x = 0; x < size3.x; x++) {
for (int y = 0; y < size3.y; y++) {
for (int z = 0; z < size3.z; z++) {
var pos = (Int3) CalculateNodePosition(x, y, z, transform);
var node = graph.AddNode(new PointNode(AstarPath.active), pos);
nodeArray[x, y, z] = node;
allNodes.Add(node);
}
}
}
ConnectNodes(size3);
graph.nodes = allNodes.ToArray();
}
public void ConnectNodes(Vector3 size3) {
//Debug.Log($"ConnectNodes size {size3} node " +
// $"{nodeArray.GetLength(0)} {nodeArray.GetLength(1)} {nodeArray.GetLength(2)} ");
for (int x = 0; x < size3.x; x++) {
for (int y = 0; y < size3.y; y++) {
for (int z = 0; z < size3.z; z++) {
//Debug.Log($"connect A {x} {y} {z} {nodeArray[x, y, z].NodeIndex}");
if (x + 1 < size3.x) {GraphNode.Connect(nodeArray[x, y, z], nodeArray[x + 1, y, z], 100);}
if (x - 1 >= 0) {GraphNode.Connect(nodeArray[x, y, z], nodeArray[x - 1, y, z], 100);}
if (y + 1 < size3.y) {GraphNode.Connect(nodeArray[x, y, z], nodeArray[x , y+1, z], 100);}
if (y - 1 >= 0){ GraphNode.Connect(nodeArray[x, y, z], nodeArray[x, y-1, z], 100);}
if (z + 1 < size3.z) {GraphNode.Connect(nodeArray[x, y, z], nodeArray[x, y, z + 1], 100);}
if (z - 1 >= 0){ GraphNode.Connect(nodeArray[x, y, z], nodeArray[x, y, z - 1], 100);}
// Debug.Log($"connect B {x} {y} {z}");
}
}
}
}
}
protected override IGraphUpdatePromise ScanInternal () => new PathGraph2DScanPromise {
graph = this,
nodeArray = nodeArray,
transform = transform,
size3 = size3
};
public PointNode AddCreateNode(int x, int y) {
var pos = (Int3) CalculateNodePosition(x, 0, y, transform);
var node = AddNode(new PointNode(AstarPath.active),pos);
return node;
}
public static Vector3 CalculateNodePosition (int x, int y, int z , GraphTransform transform) {
var pos = new Vector3(x, y, z);
// Multiply it with the matrix to get the node position in world space
pos = transform.Transform(pos);
//Debug.Log($"CalculateNodePosition {x},{y},{z}=> {pos} ");
return pos;
}
public void DebugPath(Path path) {
List<GraphNode> longestNodePath = new();
var longestPathLength = 0;
MultiTargetPath mp = path as MultiTargetPath;
if (mp != null) {
foreach (var vp in mp.nodePaths) {
if (vp != null && vp.Count > longestPathLength) {
longestPathLength = vp.Count;
longestNodePath = vp;
}
}
}
string output =$"l = {longestPathLength} :: ";
foreach(var node in longestNodePath ) {
for (int x = 0; x < size3.x; x++) {
for (int y = 0; y < size3.y; y++) {
for (int z = 0; z < size3.z; z++) {
if (nodeArray[x, y, z] == node) {
output += $", {new Vector3Int(x, y, z)}";
}
}
}
}
}
Debug.Log($"Debug Path {output}");
}
public void DebugConnectNodes() {
Debug.Log($"Debug size {size3} node " +
$"{nodeArray.GetLength(0)} {nodeArray.GetLength(1)} {nodeArray.GetLength(2)} ");
for (int x = 0; x < size3.x; x++) {
for (int y = 0; y < size3.y; y++) {
for (int z = 0; z < size3.z; z++) {
foreach (var c in nodeArray[x, y, z].connections) {
Debug.Log($"Debug connect {x} {y} {z} {nodeArray[x, y, z].NodeIndex} to {c.node.NodeIndex}" );
}
}
}
}
}
}
Code to check for the longest shortest path between any pair of nodes
public void CheckLongestPath() {
if (!PuzzleManager.inst.savePuzzle.usePathLength) return;
longestPathV3 = null;
longestPathLength = 0;
pathsCalculated = 0;
//pathPairs = OpenPairsList();
pathPairs = OpenPairsList2();
//Debug.Log($"CheckLongestPath {pathPairs.Count}");
pathsToCalculate = pathPairs.Count;
StartCoroutine(StartNPaths());
}
public IEnumerator StartNPaths() {
while(pathPairs.Count > 0){
for (int i = 0; i < maxPathStart; i++) {
if (pathPairs.Count > 0) {
CheckMultiPath(pathPairs[0]);
pathPairs.RemoveAt(0);
}
}
yield return null;
}
}
public void CheckMultiPath(MultiTarget mt) {
var p = MultiTargetPath.Construct(mt.start, mt.targets.ToArray(), null, LayMultiPath);
p.pathsForAll = true;
AstarPath.StartPath(p);
}
To create the parameters for use in MultiTargetPath.
OpenPairsList has multiple targets for each start, – produces a path wich too long because it does not use a connection
OpenPairsList2 has a single target for each start and works. – works fine
Walkable is not changed in testing
public class MultiTarget {
public Vector3 start;
public List<Vector3> targets;
}
public List<MultiTarget> OpenPairsList() {
List<PointNode> openNodes = new();
foreach (var node in pathGraph.nodes) {
if (node.Walkable) {
openNodes.Add(node);
}
}
List<MultiTarget> result = new () ;
for (int i = 0; i < openNodes.Count; i++) {
MultiTarget subList = new() {
start =(Vector3) openNodes[i].position,
targets = new()
};
result.Add(subList);
for (int j = i + 1; j < openNodes.Count; j++) {
subList.targets.Add((Vector3)openNodes[j].position);
}
}
return result;
}
public List<MultiTarget> OpenPairsList2() {
List<PointNode> openNodes = new();
foreach (var node in pathGraph.nodes) {
if (node.Walkable) {
openNodes.Add(node);
}
}
List<MultiTarget> result = new () ;
for (int i = 0; i < openNodes.Count; i++) {
for (int j = i + 1; j < openNodes.Count; j++) {
MultiTarget subList = new() {
start = (Vector3) openNodes[i].position,
targets = new()
};
result.Add(subList);
subList.targets.Add((Vector3) openNodes[j].position);
}
}
return result;
}