优化寻路算法少走弯路
主要修改getCost方法实现
using System;using System.Collections.Generic;
using UnityEngine;
public class GridNode
{
public int x = 0;
public int y = 0;
public float weight = 0;
public float h = -1f;
public float g = 0;
public float f = 0;
public bool closed = false;
public bool visited = false;
public GridNode parent = null;
public GridNode(int x, int y, float weight)
{
this.x = x;
this.y = y;
this.weight = weight;
}
public string toString()
{
return "[" + this.x.ToString() + " " + this.y.ToString() + "]";
}
public float getCost(GridNode fromNeighbor)
{
float weight = this.weight;
// Take diagonal weight into consideration.
if (fromNeighbor != null)
{
if (fromNeighbor.x != this.x && fromNeighbor.y != this.y)
weight *= 1.41421f;
if (fromNeighbor.parent != null)
{
if (fromNeighbor.x - fromNeighbor.parent.x == this.x - fromNeighbor.x
&& fromNeighbor.y - fromNeighbor.parent.y == this.y - fromNeighbor.y)
{
weight *= 0.5f;
}
}
}
return weight;
}
public bool isWall()
{
return this.weight == 0;
}
}
class BinaryHeap
{
public static BinaryHeap getHeap()
{
return new BinaryHeap(node => { return node.f; });
}
public List<GridNode> content = null;
public delegate float ScoreFunction(GridNode node);
private ScoreFunction scoreFunction = null;
public BinaryHeap(ScoreFunction scoreFunction)
{
this.content = new List<GridNode>();
this.scoreFunction = scoreFunction;
}
public void push(GridNode element)
{
// Add the new element to the end of the array.
this.content.Add(element);
// Allow it to sink down.
this.sinkDown(this.content.Count - 1);
}
public GridNode pop()
{
// Store the first element so we can return it later.
var result = this.content[0];
// Get the element at the end of the array.
var endIndex = this.content.Count - 1;
var end = this.content[endIndex];
this.content.RemoveAt(endIndex);
// If there are any elements left, put the end element at the
// start, and let it bubble up.
if (this.content.Count > 0)
{
this.content[0] = end;
this.bubbleUp(0);
}
return result;
}
public void remove(GridNode node)
{
var i = this.content.IndexOf(node);
// When it is found, the process seen in 'pop' is repeated
// to fill up the hole.
var endIndex = this.content.Count - 1;
var end = this.content[endIndex];
this.content.RemoveAt(endIndex);
if (i != this.content.Count - 1)
{
this.content[i] = end;
if (this.scoreFunction(end) < this.scoreFunction(node))
{
this.sinkDown(i);
}
else
{
this.bubbleUp(i);
}
}
}
public int size()
{
return this.content.Count;
}
public void rescoreElement(GridNode node)
{
this.sinkDown(this.content.IndexOf(node));
}
public void sinkDown(int n)
{
// Fetch the element that has to be sunk.
var element = this.content[n];
// When at 0, an element can not sink any further.
while (n > 0)
{
// Compute the parent element's index, and fetch it.
var parentN = ((n + 1) >> 1) - 1;
var parent = this.content[parentN];
// Swap the elements if the parent is greater.
if (this.scoreFunction(element) < this.scoreFunction(parent))
{
this.content[parentN] = element;
this.content[n] = parent;
// Update 'n' to continue at the new position.
n = parentN;
}
// Found a parent that is less, no need to sink any further.
else
{
break;
}
}
}
public void bubbleUp(int n)
{
// Look up the target element and its score.
var length = this.content.Count;
var element = this.content[n];
var elemScore = this.scoreFunction(element);
while (true)
{
// Compute the indices of the child elements.
var child2N = (n + 1) << 1;
var child1N = child2N - 1;
// This is used to store the new position of the element, if any.
int swap = int.MinValue;
float child1Score = float.NaN;
// If the first child exists (is inside the array)...
if (child1N < length)
{
// Look it up and compute its score.
var child1 = this.content[child1N];
child1Score = this.scoreFunction(child1);
// If the score is less than our element's, we need to swap.
if (child1Score < elemScore)
{
swap = child1N;
}
}
// Do the same checks for the other child.
if (child2N < length)
{
var child2 = this.content[child2N];
var child2Score = this.scoreFunction(child2);
if (child2Score < (swap < 0 ? elemScore : child1Score))
{
swap = child2N;
}
}
// If the element needs to be moved, swap it, and continue.
if (swap >= 0)
{
this.content[n] = this.content[swap];
this.content[swap] = element;
n = swap;
}
// Otherwise, we are done.
else
{
break;
}
}
}
}
class AstarGraph
{
List<GridNode> nodes = null;
bool diagonal = true;
GridNode[][] grid = null;
List<GridNode> dirtyNodes = new List<GridNode>();
public class GraphOption
{
public bool diagonal = true;
public bool closest = false;
public heuristics.HeuristicFunc heuristic = null;
}
/**
* A graph memory structure
* @param {Array} gridIn 2D array of input weights
* @param {Object} [options]
* @param {bool} [options.diagonal] Specifies whether diagonal moves are allowed
*/
public AstarGraph(int[][] gridIn, GraphOption options)
{
options = options == null ? new GraphOption() : options;
this.diagonal = options.diagonal;
this.init(gridIn);
}
private void init(int[][] gridIn)
{
if (this.nodes == null)
{
this.nodes = new List<GridNode>();
}
this.grid = new GridNode[gridIn.Length][];
for (var x = 0; x < gridIn.Length; x++)
{
var row = gridIn[x];
this.grid[x] = new GridNode[row.Length];
for (var y = 0; y < row.Length; y++)
{
var index = x * row.Length + y;
GridNode node = null;
if (index < this.nodes.Count)
{
node = this.nodes[index];
node.x = x;
node.y = y;
node.weight = row[y];
}
else
{
node = new GridNode(x, y, row[y]);
this.nodes.Add(node);
}
this.grid[x][y] = node;
}
}
this.dirtyNodes.Clear();
for (var i = 0; i < this.nodes.Count; i++)
{
Astar.cleanNode(this.nodes[i]);
}
}
public void cleanDirty()
{
for (var i = 0; i < this.dirtyNodes.Count; i++)
{
Astar.cleanNode(this.dirtyNodes[i]);
}
this.dirtyNodes.Clear();
}
public void markDirty(GridNode node)
{
this.dirtyNodes.Add(node);
}
public List<GridNode> neighbors(GridNode node)
{
List<GridNode> ret = new List<GridNode>();
var x = node.x;
var y = node.y;
var grid = this.grid;
// West
if (x > 1 && grid[x - 1] != null && grid[x - 1][y] != null)
{
ret.Add(grid[x - 1][y]);
}
// East
if (x < grid.Length - 1 && grid[x + 1] != null && grid[x + 1][y] != null)
{
ret.Add(grid[x + 1][y]);
}
// South
if (grid[x] != null && y > 1 && grid[x][y - 1] != null)
{
ret.Add(grid[x][y - 1]);
}
// North
if (grid[x] != null && y < grid[x].Length - 1 && grid[x][y + 1] != null)
{
ret.Add(grid[x][y + 1]);
}
if (this.diagonal)
{
// Southwest
if (x > 1 && grid[x - 1] != null && y > 1 && grid[x - 1][y - 1] != null)
{
ret.Add(grid[x - 1][y - 1]);
}
// Southeast
if (x < grid.Length - 1 && grid[x + 1] != null && y > 1 && grid[x + 1][y - 1] != null)
{
ret.Add(grid[x + 1][y - 1]);
}
// Northwest
if (x > 1 && grid[x - 1] != null && y < grid[x - 1].Length - 1 && grid[x - 1][y + 1] != null)
{
ret.Add(grid[x - 1][y + 1]);
}
// Northeast
if (x < grid.Length - 1 && grid[x + 1] != null && y < grid[x + 1].Length - 1 && grid[x + 1][y + 1] != null)
{
ret.Add(grid[x + 1][y + 1]);
}
}
return ret;
}
// public toString() {
// var graphString = [];
// var nodes = this.grid;
// for (var x = 0; x < nodes.length; x++) {
// var rowDebug = [];
// var row = nodes[x];
// for (var y = 0; y < row.length; y++) {
// rowDebug.push(row[y].weight);
// }
// graphString.push(rowDebug.join(" "));
// }
// return graphString.join("\n");
// };
}
// See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
class heuristics
{
public delegate float HeuristicFunc(GridNode pos0, GridNode pos1);
public static float manhattan(GridNode pos0, GridNode pos1)
{
var d1 = Mathf.Abs(pos1.x - pos0.x);
var d2 = Mathf.Abs(pos1.y - pos0.y);
return d1 + d2;
}
public static float diagonal(GridNode pos0, GridNode pos1)
{
float D = 1;
float D2 = Mathf.Sqrt(2);
float d1 = Mathf.Abs(pos1.x - pos0.x);
float d2 = Mathf.Abs(pos1.y - pos0.y);
return (D * (d1 + d2)) + ((D2 - (2 * D)) * Math.Min(d1, d2));
}
}
class Astar
{
public static int GRIEWEIGHT_WALL = 0;
public static int GRIDWEIGHT_ROAD = 50;
public static int GRIDWEIGHT_FREE = 100;
private static List<GridNode> pathTo(GridNode node)
{
var curr = node;
var path = new List<GridNode>();
while (curr.parent != null)
{
path.Insert(0, curr);
curr = curr.parent;
}
return path;
}
/**
* Perform an A* Search on a graph given a start and end node.
* @param {AstarGraph} graph
* @param {GridNode} start
* @param {GridNode} end
* @param {Object} [options]
* @param {bool} [options.closest] Specifies whether to return the
path to the closest node if the target is unreachable.
* @param {Function} [options.heuristic] Heuristic function (see
* astar.heuristics).
*/
public static List<GridNode> search(AstarGraph graph, GridNode start, GridNode end, AstarGraph.GraphOption options)
{
graph.cleanDirty();
options = options == null ? new AstarGraph.GraphOption() : options;
var heuristic = options.heuristic != null ? options.heuristic : heuristics.manhattan;
var closest = options.closest || false;
var openHeap = BinaryHeap.getHeap();
var closestNode = start; // set the start node to be the closest if required
start.h = heuristic(start, end);
graph.markDirty(start);
openHeap.push(start);
while (openHeap.size() > 0)
{
// Grab the lowest f(x) to process next. Heap keeps this sorted for us.
var currentNode = openHeap.pop();
// End case -- result has been found, return the traced path.
// if (currentNode === end) {
// return pathTo(currentNode);
// }
if (currentNode.x == end.x && currentNode.y == end.y)
{
return pathTo(currentNode);
}
// Normal case -- move currentNode from open to closed, process each of its neighbors.
currentNode.closed = true;
// Find all neighbors for the current node.
var neighbors = graph.neighbors(currentNode);
for (int i = 0, il = neighbors.Count; i < il; ++i)
{
var neighbor = neighbors[i];
if (neighbor.closed || neighbor.isWall())
{
// Not a valid node to process, skip to next neighbor.
continue;
}
// The g score is the shortest distance from start to current node.
// We need to check if the path we have arrived at this neighbor is the shortest one we have seen yet.
var gScore = currentNode.g + neighbor.getCost(currentNode);
var beenVisited = neighbor.visited;
if (!beenVisited || gScore < neighbor.g)
{
// Found an optimal (so far) path to this node. Take score for node to see how good it is.
neighbor.visited = true;
neighbor.parent = currentNode;
neighbor.h = neighbor.h < 0 ? heuristic(neighbor, end) : neighbor.h;
neighbor.g = gScore;
neighbor.f = neighbor.g + neighbor.h;
graph.markDirty(neighbor);
if (closest)
{
// If the neighbour is closer than the current closestNode or if it's equally close but has
// a cheaper path than the current closest node then it becomes the closest node
if (neighbor.h < closestNode.h || (!(neighbor.h > closestNode.h) && neighbor.g < closestNode.g))
{
closestNode = neighbor;
}
}
if (!beenVisited)
{
// Pushing to heap will put it in proper place based on the 'f' value.
openHeap.push(neighbor);
}
else
{
// Already seen the node, but since it has been rescored we need to reorder it in the heap
openHeap.rescoreElement(neighbor);
}
}
}
}
if (closest)
{
return pathTo(closestNode);
}
// No result was found - empty array signifies failure to find path.
return null;
}
public static void cleanNode(GridNode node)
{
node.f = 0;
node.g = 0;
node.h = 0;
node.visited = false;
node.closed = false;
node.parent = null;
}
}
以上是 优化寻路算法少走弯路 的全部内容, 来源链接: utcz.com/z/509403.html