新增a*寻路
This commit is contained in:
60
demo/libs/framework/framework.d.ts
vendored
60
demo/libs/framework/framework.d.ts
vendored
@@ -17,6 +17,61 @@ declare interface Array<T> {
|
|||||||
groupBy(keySelector: Function): Array<T>;
|
groupBy(keySelector: Function): Array<T>;
|
||||||
sum(selector: any): any;
|
sum(selector: any): any;
|
||||||
}
|
}
|
||||||
|
declare class PriorityQueueNode {
|
||||||
|
priority: number;
|
||||||
|
insertionIndex: number;
|
||||||
|
queueIndex: number;
|
||||||
|
}
|
||||||
|
declare class AStarPathfinder {
|
||||||
|
static search<T>(graph: IAstarGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
private static getKey;
|
||||||
|
static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[];
|
||||||
|
}
|
||||||
|
declare class AStarNode<T> extends PriorityQueueNode {
|
||||||
|
data: T;
|
||||||
|
constructor(data: T);
|
||||||
|
}
|
||||||
|
declare class AstarGridGraph implements IAstarGraph<Point> {
|
||||||
|
dirs: Point[];
|
||||||
|
walls: Point[];
|
||||||
|
weightedNodes: Point[];
|
||||||
|
defaultWeight: number;
|
||||||
|
weightedNodeWeight: number;
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
cost(from: Point, to: Point): number;
|
||||||
|
heuristic(node: Point, goal: Point): number;
|
||||||
|
}
|
||||||
|
interface IAstarGraph<T> {
|
||||||
|
getNeighbors(node: T): Array<T>;
|
||||||
|
cost(from: T, to: T): number;
|
||||||
|
heuristic(node: T, goal: T): any;
|
||||||
|
}
|
||||||
|
declare class PriorityQueue<T extends PriorityQueueNode> {
|
||||||
|
private _numNodes;
|
||||||
|
private _nodes;
|
||||||
|
private _numNodesEverEnqueued;
|
||||||
|
constructor(maxNodes: number);
|
||||||
|
clear(): void;
|
||||||
|
readonly count: number;
|
||||||
|
contains(node: T): boolean;
|
||||||
|
enqueue(node: T, priority: number): void;
|
||||||
|
dequeue(): T;
|
||||||
|
remove(node: T): void;
|
||||||
|
isValidQueue(): boolean;
|
||||||
|
private onNodeUpdated;
|
||||||
|
private cascadeDown;
|
||||||
|
private cascadeUp;
|
||||||
|
private swap;
|
||||||
|
private hasHigherPriority;
|
||||||
|
}
|
||||||
declare abstract class Component {
|
declare abstract class Component {
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
displayRender: egret.DisplayObject;
|
displayRender: egret.DisplayObject;
|
||||||
@@ -338,6 +393,11 @@ declare class Matrix2D {
|
|||||||
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
||||||
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
||||||
}
|
}
|
||||||
|
declare class Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
constructor(x: number, y: number);
|
||||||
|
}
|
||||||
declare class Vector2 {
|
declare class Vector2 {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
|||||||
@@ -238,6 +238,262 @@ Array.prototype.sum = function (selector) {
|
|||||||
}
|
}
|
||||||
return sum(this, selector);
|
return sum(this, selector);
|
||||||
};
|
};
|
||||||
|
var PriorityQueueNode = (function () {
|
||||||
|
function PriorityQueueNode() {
|
||||||
|
this.priority = 0;
|
||||||
|
this.insertionIndex = 0;
|
||||||
|
this.queueIndex = 0;
|
||||||
|
}
|
||||||
|
return PriorityQueueNode;
|
||||||
|
}());
|
||||||
|
var AStarPathfinder = (function () {
|
||||||
|
function AStarPathfinder() {
|
||||||
|
}
|
||||||
|
AStarPathfinder.search = function (graph, start, goal) {
|
||||||
|
var _this = this;
|
||||||
|
var foundPath = false;
|
||||||
|
var cameFrom = new Map();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
var costSoFar = new Map();
|
||||||
|
var frontier = new PriorityQueue(1000);
|
||||||
|
frontier.enqueue(new AStarNode(start), 0);
|
||||||
|
costSoFar.set(start, 0);
|
||||||
|
var _loop_2 = function () {
|
||||||
|
var current = frontier.dequeue();
|
||||||
|
if (JSON.stringify(current.data) == JSON.stringify(goal)) {
|
||||||
|
foundPath = true;
|
||||||
|
return "break";
|
||||||
|
}
|
||||||
|
graph.getNeighbors(current.data).forEach(function (next) {
|
||||||
|
var newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||||
|
if (!_this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)) {
|
||||||
|
costSoFar.set(next, newCost);
|
||||||
|
var priority = newCost + graph.heuristic(next, goal);
|
||||||
|
frontier.enqueue(new AStarNode(next), priority);
|
||||||
|
cameFrom.set(next, current.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
while (frontier.count > 0) {
|
||||||
|
var state_1 = _loop_2();
|
||||||
|
if (state_1 === "break")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
};
|
||||||
|
AStarPathfinder.hasKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var r;
|
||||||
|
while (r = iterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
AStarPathfinder.getKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var valueIterator = map.values();
|
||||||
|
var r;
|
||||||
|
var v;
|
||||||
|
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return v.value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
AStarPathfinder.recontructPath = function (cameFrom, start, goal) {
|
||||||
|
var path = [];
|
||||||
|
var current = goal;
|
||||||
|
path.push(goal);
|
||||||
|
while (current != start) {
|
||||||
|
current = this.getKey(cameFrom, current);
|
||||||
|
path.push(current);
|
||||||
|
}
|
||||||
|
path.reverse();
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
return AStarPathfinder;
|
||||||
|
}());
|
||||||
|
var AStarNode = (function (_super) {
|
||||||
|
__extends(AStarNode, _super);
|
||||||
|
function AStarNode(data) {
|
||||||
|
var _this = _super.call(this) || this;
|
||||||
|
_this.data = data;
|
||||||
|
return _this;
|
||||||
|
}
|
||||||
|
return AStarNode;
|
||||||
|
}(PriorityQueueNode));
|
||||||
|
var AstarGridGraph = (function () {
|
||||||
|
function AstarGridGraph(width, height) {
|
||||||
|
this.dirs = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, 1)
|
||||||
|
];
|
||||||
|
this.walls = [];
|
||||||
|
this.weightedNodes = [];
|
||||||
|
this.defaultWeight = 1;
|
||||||
|
this.weightedNodeWeight = 5;
|
||||||
|
this._neighbors = new Array(4);
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
}
|
||||||
|
AstarGridGraph.prototype.isNodeInBounds = function (node) {
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.isNodePassable = function (node) {
|
||||||
|
return !this.walls.contains(node);
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.search = function (start, goal) {
|
||||||
|
return AStarPathfinder.search(this, start, goal);
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.getNeighbors = function (node) {
|
||||||
|
var _this = this;
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
this.dirs.forEach(function (dir) {
|
||||||
|
var next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (_this.isNodeInBounds(next) && _this.isNodePassable(next))
|
||||||
|
_this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
return this._neighbors;
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.cost = function (from, to) {
|
||||||
|
return this.weightedNodes.find(function (p) { return JSON.stringify(p) == JSON.stringify(to); }) ? this.weightedNodeWeight : this.defaultWeight;
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.heuristic = function (node, goal) {
|
||||||
|
return Math.abs(node.x - goal.x) + Math.abs(node.y - goal.y);
|
||||||
|
};
|
||||||
|
return AstarGridGraph;
|
||||||
|
}());
|
||||||
|
var PriorityQueue = (function () {
|
||||||
|
function PriorityQueue(maxNodes) {
|
||||||
|
this._numNodes = 0;
|
||||||
|
this._nodes = new Array(maxNodes + 1);
|
||||||
|
this._numNodesEverEnqueued = 0;
|
||||||
|
}
|
||||||
|
PriorityQueue.prototype.clear = function () {
|
||||||
|
this._nodes.splice(1, this._numNodes);
|
||||||
|
this._numNodes = 0;
|
||||||
|
};
|
||||||
|
Object.defineProperty(PriorityQueue.prototype, "count", {
|
||||||
|
get: function () {
|
||||||
|
return this._numNodes;
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
PriorityQueue.prototype.contains = function (node) {
|
||||||
|
return (this._nodes[node.queueIndex] == node);
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.enqueue = function (node, priority) {
|
||||||
|
node.priority = priority;
|
||||||
|
this._numNodes++;
|
||||||
|
this._nodes[this._numNodes] = node;
|
||||||
|
node.queueIndex = this._numNodes;
|
||||||
|
node.insertionIndex = this._numNodesEverEnqueued++;
|
||||||
|
this.cascadeUp(this._nodes[this._numNodes]);
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.dequeue = function () {
|
||||||
|
var returnMe = this._nodes[1];
|
||||||
|
this.remove(returnMe);
|
||||||
|
return returnMe;
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.remove = function (node) {
|
||||||
|
if (node.queueIndex == this._numNodes) {
|
||||||
|
this._nodes[this._numNodes] = null;
|
||||||
|
this._numNodes--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var formerLastNode = this._nodes[this._numNodes];
|
||||||
|
this.swap(node, formerLastNode);
|
||||||
|
delete this._nodes[this._numNodes];
|
||||||
|
this._numNodes--;
|
||||||
|
this.onNodeUpdated(formerLastNode);
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.isValidQueue = function () {
|
||||||
|
for (var i = 1; i < this._nodes.length; i++) {
|
||||||
|
if (this._nodes[i]) {
|
||||||
|
var childLeftIndex = 2 * i;
|
||||||
|
if (childLeftIndex < this._nodes.length && this._nodes[childLeftIndex] &&
|
||||||
|
this.hasHigherPriority(this._nodes[childLeftIndex], this._nodes[i]))
|
||||||
|
return false;
|
||||||
|
var childRightIndex = childLeftIndex + 1;
|
||||||
|
if (childRightIndex < this._nodes.length && this._nodes[childRightIndex] &&
|
||||||
|
this.hasHigherPriority(this._nodes[childRightIndex], this._nodes[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.onNodeUpdated = function (node) {
|
||||||
|
var parentIndex = Math.floor(node.queueIndex / 2);
|
||||||
|
var parentNode = this._nodes[parentIndex];
|
||||||
|
if (parentIndex > 0 && this.hasHigherPriority(node, parentNode)) {
|
||||||
|
this.cascadeUp(node);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.cascadeDown(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.cascadeDown = function (node) {
|
||||||
|
var newParent;
|
||||||
|
var finalQueueIndex = node.queueIndex;
|
||||||
|
while (true) {
|
||||||
|
newParent = node;
|
||||||
|
var childLeftIndex = 2 * finalQueueIndex;
|
||||||
|
if (childLeftIndex > this._numNodes) {
|
||||||
|
node.queueIndex = finalQueueIndex;
|
||||||
|
this._nodes[finalQueueIndex] = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var childLeft = this._nodes[childLeftIndex];
|
||||||
|
if (this.hasHigherPriority(childLeft, newParent)) {
|
||||||
|
newParent = childLeft;
|
||||||
|
}
|
||||||
|
var childRightIndex = childLeftIndex + 1;
|
||||||
|
if (childRightIndex <= this._numNodes) {
|
||||||
|
var childRight = this._nodes[childRightIndex];
|
||||||
|
if (this.hasHigherPriority(childRight, newParent)) {
|
||||||
|
newParent = childRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newParent != node) {
|
||||||
|
this._nodes[finalQueueIndex] = newParent;
|
||||||
|
var temp = newParent.queueIndex;
|
||||||
|
newParent.queueIndex = finalQueueIndex;
|
||||||
|
finalQueueIndex = temp;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node.queueIndex = finalQueueIndex;
|
||||||
|
this._nodes[finalQueueIndex] = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.cascadeUp = function (node) {
|
||||||
|
var parent = Math.floor(node.queueIndex / 2);
|
||||||
|
while (parent >= 1) {
|
||||||
|
var parentNode = this._nodes[parent];
|
||||||
|
if (this.hasHigherPriority(parentNode, node))
|
||||||
|
break;
|
||||||
|
this.swap(node, parentNode);
|
||||||
|
parent = Math.floor(node.queueIndex / 2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.swap = function (node1, node2) {
|
||||||
|
this._nodes[node1.queueIndex] = node2;
|
||||||
|
this._nodes[node2.queueIndex] = node1;
|
||||||
|
var temp = node1.queueIndex;
|
||||||
|
node1.queueIndex = node2.queueIndex;
|
||||||
|
node2.queueIndex = temp;
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.hasHigherPriority = function (higher, lower) {
|
||||||
|
return (higher.priority < lower.priority ||
|
||||||
|
(higher.priority == lower.priority && higher.insertionIndex < lower.insertionIndex));
|
||||||
|
};
|
||||||
|
return PriorityQueue;
|
||||||
|
}());
|
||||||
var Component = (function () {
|
var Component = (function () {
|
||||||
function Component() {
|
function Component() {
|
||||||
this._enabled = true;
|
this._enabled = true;
|
||||||
@@ -1763,6 +2019,13 @@ var Matrix2D = (function () {
|
|||||||
Matrix2D._identity = new Matrix2D(1, 0, 0, 1, 0, 0);
|
Matrix2D._identity = new Matrix2D(1, 0, 0, 1, 0, 0);
|
||||||
return Matrix2D;
|
return Matrix2D;
|
||||||
}());
|
}());
|
||||||
|
var Point = (function () {
|
||||||
|
function Point(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
return Point;
|
||||||
|
}());
|
||||||
var Vector2 = (function () {
|
var Vector2 = (function () {
|
||||||
function Vector2(x, y) {
|
function Vector2(x, y) {
|
||||||
this.x = 0;
|
this.x = 0;
|
||||||
|
|||||||
2
demo/libs/framework/framework.min.js
vendored
2
demo/libs/framework/framework.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -3,5 +3,18 @@ class MainScene extends Scene {
|
|||||||
super(displayContent);
|
super(displayContent);
|
||||||
|
|
||||||
this.addEntityProcessor(new SpawnerSystem(new Matcher()));
|
this.addEntityProcessor(new SpawnerSystem(new Matcher()));
|
||||||
|
this.astarTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public astarTest(){
|
||||||
|
let graph = new AstarGridGraph(20, 20);
|
||||||
|
|
||||||
|
graph.weightedNodes.push(new Point(3, 3));
|
||||||
|
graph.weightedNodes.push(new Point(3, 4));
|
||||||
|
graph.weightedNodes.push(new Point(4, 3));
|
||||||
|
graph.weightedNodes.push(new Point(4, 4));
|
||||||
|
|
||||||
|
let path = graph.search(new Point(3, 4), new Point(15, 17));
|
||||||
|
console.log(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,12 @@
|
|||||||
"target": "es5",
|
"target": "es5",
|
||||||
"outDir": "bin-debug",
|
"outDir": "bin-debug",
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"es5",
|
"es5",
|
||||||
"dom",
|
"dom",
|
||||||
"es2015.promise"
|
"es2015.promise",
|
||||||
|
"es6"
|
||||||
],
|
],
|
||||||
"types": []
|
"types": []
|
||||||
},
|
},
|
||||||
|
|||||||
60
source/bin/framework.d.ts
vendored
60
source/bin/framework.d.ts
vendored
@@ -17,6 +17,61 @@ declare interface Array<T> {
|
|||||||
groupBy(keySelector: Function): Array<T>;
|
groupBy(keySelector: Function): Array<T>;
|
||||||
sum(selector: any): any;
|
sum(selector: any): any;
|
||||||
}
|
}
|
||||||
|
declare class PriorityQueueNode {
|
||||||
|
priority: number;
|
||||||
|
insertionIndex: number;
|
||||||
|
queueIndex: number;
|
||||||
|
}
|
||||||
|
declare class AStarPathfinder {
|
||||||
|
static search<T>(graph: IAstarGraph<T>, start: T, goal: T): T[];
|
||||||
|
private static hasKey;
|
||||||
|
private static getKey;
|
||||||
|
static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[];
|
||||||
|
}
|
||||||
|
declare class AStarNode<T> extends PriorityQueueNode {
|
||||||
|
data: T;
|
||||||
|
constructor(data: T);
|
||||||
|
}
|
||||||
|
declare class AstarGridGraph implements IAstarGraph<Point> {
|
||||||
|
dirs: Point[];
|
||||||
|
walls: Point[];
|
||||||
|
weightedNodes: Point[];
|
||||||
|
defaultWeight: number;
|
||||||
|
weightedNodeWeight: number;
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
private _neighbors;
|
||||||
|
constructor(width: number, height: number);
|
||||||
|
isNodeInBounds(node: Point): boolean;
|
||||||
|
isNodePassable(node: Point): boolean;
|
||||||
|
search(start: Point, goal: Point): Point[];
|
||||||
|
getNeighbors(node: Point): Point[];
|
||||||
|
cost(from: Point, to: Point): number;
|
||||||
|
heuristic(node: Point, goal: Point): number;
|
||||||
|
}
|
||||||
|
interface IAstarGraph<T> {
|
||||||
|
getNeighbors(node: T): Array<T>;
|
||||||
|
cost(from: T, to: T): number;
|
||||||
|
heuristic(node: T, goal: T): any;
|
||||||
|
}
|
||||||
|
declare class PriorityQueue<T extends PriorityQueueNode> {
|
||||||
|
private _numNodes;
|
||||||
|
private _nodes;
|
||||||
|
private _numNodesEverEnqueued;
|
||||||
|
constructor(maxNodes: number);
|
||||||
|
clear(): void;
|
||||||
|
readonly count: number;
|
||||||
|
contains(node: T): boolean;
|
||||||
|
enqueue(node: T, priority: number): void;
|
||||||
|
dequeue(): T;
|
||||||
|
remove(node: T): void;
|
||||||
|
isValidQueue(): boolean;
|
||||||
|
private onNodeUpdated;
|
||||||
|
private cascadeDown;
|
||||||
|
private cascadeUp;
|
||||||
|
private swap;
|
||||||
|
private hasHigherPriority;
|
||||||
|
}
|
||||||
declare abstract class Component {
|
declare abstract class Component {
|
||||||
entity: Entity;
|
entity: Entity;
|
||||||
displayRender: egret.DisplayObject;
|
displayRender: egret.DisplayObject;
|
||||||
@@ -338,6 +393,11 @@ declare class Matrix2D {
|
|||||||
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
static createRotation(radians: number, result?: Matrix2D): Matrix2D;
|
||||||
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
static createScale(xScale: number, yScale: number, result?: Matrix2D): Matrix2D;
|
||||||
}
|
}
|
||||||
|
declare class Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
constructor(x: number, y: number);
|
||||||
|
}
|
||||||
declare class Vector2 {
|
declare class Vector2 {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
|||||||
@@ -238,6 +238,262 @@ Array.prototype.sum = function (selector) {
|
|||||||
}
|
}
|
||||||
return sum(this, selector);
|
return sum(this, selector);
|
||||||
};
|
};
|
||||||
|
var PriorityQueueNode = (function () {
|
||||||
|
function PriorityQueueNode() {
|
||||||
|
this.priority = 0;
|
||||||
|
this.insertionIndex = 0;
|
||||||
|
this.queueIndex = 0;
|
||||||
|
}
|
||||||
|
return PriorityQueueNode;
|
||||||
|
}());
|
||||||
|
var AStarPathfinder = (function () {
|
||||||
|
function AStarPathfinder() {
|
||||||
|
}
|
||||||
|
AStarPathfinder.search = function (graph, start, goal) {
|
||||||
|
var _this = this;
|
||||||
|
var foundPath = false;
|
||||||
|
var cameFrom = new Map();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
var costSoFar = new Map();
|
||||||
|
var frontier = new PriorityQueue(1000);
|
||||||
|
frontier.enqueue(new AStarNode(start), 0);
|
||||||
|
costSoFar.set(start, 0);
|
||||||
|
var _loop_2 = function () {
|
||||||
|
var current = frontier.dequeue();
|
||||||
|
if (JSON.stringify(current.data) == JSON.stringify(goal)) {
|
||||||
|
foundPath = true;
|
||||||
|
return "break";
|
||||||
|
}
|
||||||
|
graph.getNeighbors(current.data).forEach(function (next) {
|
||||||
|
var newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||||
|
if (!_this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)) {
|
||||||
|
costSoFar.set(next, newCost);
|
||||||
|
var priority = newCost + graph.heuristic(next, goal);
|
||||||
|
frontier.enqueue(new AStarNode(next), priority);
|
||||||
|
cameFrom.set(next, current.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
while (frontier.count > 0) {
|
||||||
|
var state_1 = _loop_2();
|
||||||
|
if (state_1 === "break")
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
};
|
||||||
|
AStarPathfinder.hasKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var r;
|
||||||
|
while (r = iterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
AStarPathfinder.getKey = function (map, compareKey) {
|
||||||
|
var iterator = map.keys();
|
||||||
|
var valueIterator = map.values();
|
||||||
|
var r;
|
||||||
|
var v;
|
||||||
|
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return v.value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
AStarPathfinder.recontructPath = function (cameFrom, start, goal) {
|
||||||
|
var path = [];
|
||||||
|
var current = goal;
|
||||||
|
path.push(goal);
|
||||||
|
while (current != start) {
|
||||||
|
current = this.getKey(cameFrom, current);
|
||||||
|
path.push(current);
|
||||||
|
}
|
||||||
|
path.reverse();
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
return AStarPathfinder;
|
||||||
|
}());
|
||||||
|
var AStarNode = (function (_super) {
|
||||||
|
__extends(AStarNode, _super);
|
||||||
|
function AStarNode(data) {
|
||||||
|
var _this = _super.call(this) || this;
|
||||||
|
_this.data = data;
|
||||||
|
return _this;
|
||||||
|
}
|
||||||
|
return AStarNode;
|
||||||
|
}(PriorityQueueNode));
|
||||||
|
var AstarGridGraph = (function () {
|
||||||
|
function AstarGridGraph(width, height) {
|
||||||
|
this.dirs = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, 1)
|
||||||
|
];
|
||||||
|
this.walls = [];
|
||||||
|
this.weightedNodes = [];
|
||||||
|
this.defaultWeight = 1;
|
||||||
|
this.weightedNodeWeight = 5;
|
||||||
|
this._neighbors = new Array(4);
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
}
|
||||||
|
AstarGridGraph.prototype.isNodeInBounds = function (node) {
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.isNodePassable = function (node) {
|
||||||
|
return !this.walls.contains(node);
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.search = function (start, goal) {
|
||||||
|
return AStarPathfinder.search(this, start, goal);
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.getNeighbors = function (node) {
|
||||||
|
var _this = this;
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
this.dirs.forEach(function (dir) {
|
||||||
|
var next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (_this.isNodeInBounds(next) && _this.isNodePassable(next))
|
||||||
|
_this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
return this._neighbors;
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.cost = function (from, to) {
|
||||||
|
return this.weightedNodes.find(function (p) { return JSON.stringify(p) == JSON.stringify(to); }) ? this.weightedNodeWeight : this.defaultWeight;
|
||||||
|
};
|
||||||
|
AstarGridGraph.prototype.heuristic = function (node, goal) {
|
||||||
|
return Math.abs(node.x - goal.x) + Math.abs(node.y - goal.y);
|
||||||
|
};
|
||||||
|
return AstarGridGraph;
|
||||||
|
}());
|
||||||
|
var PriorityQueue = (function () {
|
||||||
|
function PriorityQueue(maxNodes) {
|
||||||
|
this._numNodes = 0;
|
||||||
|
this._nodes = new Array(maxNodes + 1);
|
||||||
|
this._numNodesEverEnqueued = 0;
|
||||||
|
}
|
||||||
|
PriorityQueue.prototype.clear = function () {
|
||||||
|
this._nodes.splice(1, this._numNodes);
|
||||||
|
this._numNodes = 0;
|
||||||
|
};
|
||||||
|
Object.defineProperty(PriorityQueue.prototype, "count", {
|
||||||
|
get: function () {
|
||||||
|
return this._numNodes;
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
PriorityQueue.prototype.contains = function (node) {
|
||||||
|
return (this._nodes[node.queueIndex] == node);
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.enqueue = function (node, priority) {
|
||||||
|
node.priority = priority;
|
||||||
|
this._numNodes++;
|
||||||
|
this._nodes[this._numNodes] = node;
|
||||||
|
node.queueIndex = this._numNodes;
|
||||||
|
node.insertionIndex = this._numNodesEverEnqueued++;
|
||||||
|
this.cascadeUp(this._nodes[this._numNodes]);
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.dequeue = function () {
|
||||||
|
var returnMe = this._nodes[1];
|
||||||
|
this.remove(returnMe);
|
||||||
|
return returnMe;
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.remove = function (node) {
|
||||||
|
if (node.queueIndex == this._numNodes) {
|
||||||
|
this._nodes[this._numNodes] = null;
|
||||||
|
this._numNodes--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var formerLastNode = this._nodes[this._numNodes];
|
||||||
|
this.swap(node, formerLastNode);
|
||||||
|
delete this._nodes[this._numNodes];
|
||||||
|
this._numNodes--;
|
||||||
|
this.onNodeUpdated(formerLastNode);
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.isValidQueue = function () {
|
||||||
|
for (var i = 1; i < this._nodes.length; i++) {
|
||||||
|
if (this._nodes[i]) {
|
||||||
|
var childLeftIndex = 2 * i;
|
||||||
|
if (childLeftIndex < this._nodes.length && this._nodes[childLeftIndex] &&
|
||||||
|
this.hasHigherPriority(this._nodes[childLeftIndex], this._nodes[i]))
|
||||||
|
return false;
|
||||||
|
var childRightIndex = childLeftIndex + 1;
|
||||||
|
if (childRightIndex < this._nodes.length && this._nodes[childRightIndex] &&
|
||||||
|
this.hasHigherPriority(this._nodes[childRightIndex], this._nodes[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.onNodeUpdated = function (node) {
|
||||||
|
var parentIndex = Math.floor(node.queueIndex / 2);
|
||||||
|
var parentNode = this._nodes[parentIndex];
|
||||||
|
if (parentIndex > 0 && this.hasHigherPriority(node, parentNode)) {
|
||||||
|
this.cascadeUp(node);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.cascadeDown(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.cascadeDown = function (node) {
|
||||||
|
var newParent;
|
||||||
|
var finalQueueIndex = node.queueIndex;
|
||||||
|
while (true) {
|
||||||
|
newParent = node;
|
||||||
|
var childLeftIndex = 2 * finalQueueIndex;
|
||||||
|
if (childLeftIndex > this._numNodes) {
|
||||||
|
node.queueIndex = finalQueueIndex;
|
||||||
|
this._nodes[finalQueueIndex] = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var childLeft = this._nodes[childLeftIndex];
|
||||||
|
if (this.hasHigherPriority(childLeft, newParent)) {
|
||||||
|
newParent = childLeft;
|
||||||
|
}
|
||||||
|
var childRightIndex = childLeftIndex + 1;
|
||||||
|
if (childRightIndex <= this._numNodes) {
|
||||||
|
var childRight = this._nodes[childRightIndex];
|
||||||
|
if (this.hasHigherPriority(childRight, newParent)) {
|
||||||
|
newParent = childRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newParent != node) {
|
||||||
|
this._nodes[finalQueueIndex] = newParent;
|
||||||
|
var temp = newParent.queueIndex;
|
||||||
|
newParent.queueIndex = finalQueueIndex;
|
||||||
|
finalQueueIndex = temp;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node.queueIndex = finalQueueIndex;
|
||||||
|
this._nodes[finalQueueIndex] = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.cascadeUp = function (node) {
|
||||||
|
var parent = Math.floor(node.queueIndex / 2);
|
||||||
|
while (parent >= 1) {
|
||||||
|
var parentNode = this._nodes[parent];
|
||||||
|
if (this.hasHigherPriority(parentNode, node))
|
||||||
|
break;
|
||||||
|
this.swap(node, parentNode);
|
||||||
|
parent = Math.floor(node.queueIndex / 2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.swap = function (node1, node2) {
|
||||||
|
this._nodes[node1.queueIndex] = node2;
|
||||||
|
this._nodes[node2.queueIndex] = node1;
|
||||||
|
var temp = node1.queueIndex;
|
||||||
|
node1.queueIndex = node2.queueIndex;
|
||||||
|
node2.queueIndex = temp;
|
||||||
|
};
|
||||||
|
PriorityQueue.prototype.hasHigherPriority = function (higher, lower) {
|
||||||
|
return (higher.priority < lower.priority ||
|
||||||
|
(higher.priority == lower.priority && higher.insertionIndex < lower.insertionIndex));
|
||||||
|
};
|
||||||
|
return PriorityQueue;
|
||||||
|
}());
|
||||||
var Component = (function () {
|
var Component = (function () {
|
||||||
function Component() {
|
function Component() {
|
||||||
this._enabled = true;
|
this._enabled = true;
|
||||||
@@ -1763,6 +2019,13 @@ var Matrix2D = (function () {
|
|||||||
Matrix2D._identity = new Matrix2D(1, 0, 0, 1, 0, 0);
|
Matrix2D._identity = new Matrix2D(1, 0, 0, 1, 0, 0);
|
||||||
return Matrix2D;
|
return Matrix2D;
|
||||||
}());
|
}());
|
||||||
|
var Point = (function () {
|
||||||
|
function Point(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
return Point;
|
||||||
|
}());
|
||||||
var Vector2 = (function () {
|
var Vector2 = (function () {
|
||||||
function Vector2(x, y) {
|
function Vector2(x, y) {
|
||||||
this.x = 0;
|
this.x = 0;
|
||||||
|
|||||||
2
source/bin/framework.min.js
vendored
2
source/bin/framework.min.js
vendored
File diff suppressed because one or more lines are too long
89
source/src/AI/Pathfinding/AStar/AStarPathfinder.ts
Normal file
89
source/src/AI/Pathfinding/AStar/AStarPathfinder.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
///<reference path="./PriorityQueueNode.ts" />
|
||||||
|
/**
|
||||||
|
* 计算路径给定的IAstarGraph和开始/目标位置
|
||||||
|
*/
|
||||||
|
class AStarPathfinder {
|
||||||
|
public static search<T>(graph: IAstarGraph<T>, start: T, goal: T){
|
||||||
|
let foundPath = false;
|
||||||
|
let cameFrom = new Map<T, T>();
|
||||||
|
cameFrom.set(start, start);
|
||||||
|
|
||||||
|
let costSoFar = new Map<T, number>();
|
||||||
|
let frontier = new PriorityQueue<AStarNode<T>>(1000);
|
||||||
|
frontier.enqueue(new AStarNode<T>(start), 0);
|
||||||
|
|
||||||
|
costSoFar.set(start, 0);
|
||||||
|
|
||||||
|
while (frontier.count > 0){
|
||||||
|
let current = frontier.dequeue();
|
||||||
|
|
||||||
|
if (JSON.stringify(current.data) == JSON.stringify(goal)){
|
||||||
|
foundPath = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.getNeighbors(current.data).forEach(next => {
|
||||||
|
let newCost = costSoFar.get(current.data) + graph.cost(current.data, next);
|
||||||
|
if (!this.hasKey(costSoFar, next) || newCost < costSoFar.get(next)){
|
||||||
|
costSoFar.set(next, newCost);
|
||||||
|
let priority = newCost + graph.heuristic(next, goal);
|
||||||
|
frontier.enqueue(new AStarNode<T>(next), priority);
|
||||||
|
cameFrom.set(next, current.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundPath ? this.recontructPath(cameFrom, start, goal) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static hasKey<T>(map: Map<T, number>, compareKey: T){
|
||||||
|
let iterator = map.keys();
|
||||||
|
let r: IteratorResult<T>;
|
||||||
|
while (r = iterator.next() , !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getKey<T>(map: Map<T, T>, compareKey: T){
|
||||||
|
let iterator = map.keys();
|
||||||
|
let valueIterator = map.values();
|
||||||
|
let r: IteratorResult<T>;
|
||||||
|
let v: IteratorResult<T>;
|
||||||
|
while (r = iterator.next(), v = valueIterator.next(), !r.done) {
|
||||||
|
if (JSON.stringify(r.value) == JSON.stringify(compareKey))
|
||||||
|
return v.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static recontructPath<T>(cameFrom: Map<T, T>, start: T, goal: T): T[]{
|
||||||
|
let path = [];
|
||||||
|
let current = goal;
|
||||||
|
path.push(goal);
|
||||||
|
|
||||||
|
while (current != start){
|
||||||
|
current = this.getKey(cameFrom, current);
|
||||||
|
path.push(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.reverse();
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用PriorityQueue需要的额外字段将原始数据封装在一个小类中
|
||||||
|
*/
|
||||||
|
class AStarNode<T> extends PriorityQueueNode {
|
||||||
|
public data: T;
|
||||||
|
|
||||||
|
constructor(data: T){
|
||||||
|
super();
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
source/src/AI/Pathfinding/AStar/AstarGridGraph.ts
Normal file
67
source/src/AI/Pathfinding/AStar/AstarGridGraph.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* 基本静态网格图与A*一起使用
|
||||||
|
* 将walls添加到walls HashSet,并将加权节点添加到weightedNodes
|
||||||
|
*/
|
||||||
|
class AstarGridGraph implements IAstarGraph<Point> {
|
||||||
|
public dirs: Point[] = [
|
||||||
|
new Point(1, 0),
|
||||||
|
new Point(0, -1),
|
||||||
|
new Point(-1, 0),
|
||||||
|
new Point(0, 1)
|
||||||
|
];
|
||||||
|
|
||||||
|
public walls: Point[] = [];
|
||||||
|
public weightedNodes: Point[] = [];
|
||||||
|
public defaultWeight: number = 1;
|
||||||
|
public weightedNodeWeight = 5;
|
||||||
|
|
||||||
|
private _width;
|
||||||
|
private _height;
|
||||||
|
private _neighbors: Point[] = new Array(4);
|
||||||
|
|
||||||
|
constructor(width: number, height: number){
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保节点在网格图的边界内
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
public isNodeInBounds(node: Point): boolean {
|
||||||
|
return 0 <= node.x && node.x < this._width && 0 <= node.y && node.y < this._height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查节点是否可以通过。墙壁是不可逾越的。
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
public isNodePassable(node: Point): boolean {
|
||||||
|
return !this.walls.contains(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public search(start: Point, goal: Point){
|
||||||
|
return AStarPathfinder.search(this, start, goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNeighbors(node: Point): Point[] {
|
||||||
|
this._neighbors.length = 0;
|
||||||
|
|
||||||
|
this.dirs.forEach(dir => {
|
||||||
|
let next = new Point(node.x + dir.x, node.y + dir.y);
|
||||||
|
if (this.isNodeInBounds(next) && this.isNodePassable(next))
|
||||||
|
this._neighbors.push(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public cost(from: Point, to: Point): number {
|
||||||
|
return this.weightedNodes.find((p)=> JSON.stringify(p) == JSON.stringify(to)) ? this.weightedNodeWeight : this.defaultWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public heuristic(node: Point, goal: Point) {
|
||||||
|
return Math.abs(node.x - goal.x) + Math.abs(node.y - goal.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
5
source/src/AI/Pathfinding/AStar/IAstarGraph.ts
Normal file
5
source/src/AI/Pathfinding/AStar/IAstarGraph.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
interface IAstarGraph<T> {
|
||||||
|
getNeighbors(node: T): Array<T>;
|
||||||
|
cost(from: T, to: T): number;
|
||||||
|
heuristic(node: T, goal: T);
|
||||||
|
}
|
||||||
150
source/src/AI/Pathfinding/AStar/PriorityQueue.ts
Normal file
150
source/src/AI/Pathfinding/AStar/PriorityQueue.ts
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
class PriorityQueue<T extends PriorityQueueNode> {
|
||||||
|
private _numNodes: number;
|
||||||
|
private _nodes: T[];
|
||||||
|
private _numNodesEverEnqueued;
|
||||||
|
|
||||||
|
constructor(maxNodes: number) {
|
||||||
|
this._numNodes = 0;
|
||||||
|
this._nodes = new Array(maxNodes + 1);
|
||||||
|
this._numNodesEverEnqueued = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this._nodes.splice(1, this._numNodes);
|
||||||
|
this._numNodes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get count() {
|
||||||
|
return this._numNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public contains(node: T): boolean {
|
||||||
|
return (this._nodes[node.queueIndex] == node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enqueue(node: T, priority: number) {
|
||||||
|
node.priority = priority;
|
||||||
|
this._numNodes++;
|
||||||
|
this._nodes[this._numNodes] = node;
|
||||||
|
node.queueIndex = this._numNodes;
|
||||||
|
node.insertionIndex = this._numNodesEverEnqueued++;
|
||||||
|
this.cascadeUp(this._nodes[this._numNodes]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public dequeue(): T {
|
||||||
|
let returnMe = this._nodes[1];
|
||||||
|
this.remove(returnMe);
|
||||||
|
return returnMe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(node: T) {
|
||||||
|
if (node.queueIndex == this._numNodes) {
|
||||||
|
this._nodes[this._numNodes] = null;
|
||||||
|
this._numNodes--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let formerLastNode = this._nodes[this._numNodes];
|
||||||
|
this.swap(node, formerLastNode);
|
||||||
|
delete this._nodes[this._numNodes];
|
||||||
|
this._numNodes--;
|
||||||
|
|
||||||
|
this.onNodeUpdated(formerLastNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isValidQueue(): boolean {
|
||||||
|
for (let i = 1; i < this._nodes.length; i++) {
|
||||||
|
if (this._nodes[i]) {
|
||||||
|
let childLeftIndex = 2 * i;
|
||||||
|
if (childLeftIndex < this._nodes.length && this._nodes[childLeftIndex] &&
|
||||||
|
this.hasHigherPriority(this._nodes[childLeftIndex], this._nodes[i]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let childRightIndex = childLeftIndex + 1;
|
||||||
|
if (childRightIndex < this._nodes.length && this._nodes[childRightIndex] &&
|
||||||
|
this.hasHigherPriority(this._nodes[childRightIndex], this._nodes[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onNodeUpdated(node: T) {
|
||||||
|
let parentIndex = Math.floor(node.queueIndex / 2);
|
||||||
|
let parentNode = this._nodes[parentIndex];
|
||||||
|
|
||||||
|
if (parentIndex > 0 && this.hasHigherPriority(node, parentNode)) {
|
||||||
|
this.cascadeUp(node);
|
||||||
|
} else {
|
||||||
|
this.cascadeDown(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private cascadeDown(node: T) {
|
||||||
|
let newParent: T;
|
||||||
|
let finalQueueIndex = node.queueIndex;
|
||||||
|
while (true) {
|
||||||
|
newParent = node;
|
||||||
|
let childLeftIndex = 2 * finalQueueIndex;
|
||||||
|
|
||||||
|
if (childLeftIndex > this._numNodes) {
|
||||||
|
node.queueIndex = finalQueueIndex;
|
||||||
|
this._nodes[finalQueueIndex] = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childLeft = this._nodes[childLeftIndex];
|
||||||
|
if (this.hasHigherPriority(childLeft, newParent)) {
|
||||||
|
newParent = childLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childRightIndex = childLeftIndex + 1;
|
||||||
|
if (childRightIndex <= this._numNodes) {
|
||||||
|
let childRight = this._nodes[childRightIndex];
|
||||||
|
if (this.hasHigherPriority(childRight, newParent)) {
|
||||||
|
newParent = childRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newParent != node) {
|
||||||
|
this._nodes[finalQueueIndex] = newParent;
|
||||||
|
|
||||||
|
let temp = newParent.queueIndex;
|
||||||
|
newParent.queueIndex = finalQueueIndex;
|
||||||
|
finalQueueIndex = temp;
|
||||||
|
} else {
|
||||||
|
node.queueIndex = finalQueueIndex;
|
||||||
|
this._nodes[finalQueueIndex] = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private cascadeUp(node: T) {
|
||||||
|
let parent = Math.floor(node.queueIndex / 2);
|
||||||
|
while (parent >= 1) {
|
||||||
|
let parentNode = this._nodes[parent];
|
||||||
|
if (this.hasHigherPriority(parentNode, node))
|
||||||
|
break;
|
||||||
|
|
||||||
|
this.swap(node, parentNode);
|
||||||
|
|
||||||
|
parent = Math.floor(node.queueIndex / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private swap(node1: T, node2: T) {
|
||||||
|
this._nodes[node1.queueIndex] = node2;
|
||||||
|
this._nodes[node2.queueIndex] = node1;
|
||||||
|
|
||||||
|
let temp = node1.queueIndex;
|
||||||
|
node1.queueIndex = node2.queueIndex;
|
||||||
|
node2.queueIndex = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasHigherPriority(higher: T, lower: T) {
|
||||||
|
return (higher.priority < lower.priority ||
|
||||||
|
(higher.priority == lower.priority && higher.insertionIndex < lower.insertionIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
14
source/src/AI/Pathfinding/AStar/PriorityQueueNode.ts
Normal file
14
source/src/AI/Pathfinding/AStar/PriorityQueueNode.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
class PriorityQueueNode {
|
||||||
|
/**
|
||||||
|
* 插入此节点的优先级。在将节点添加到队列之前必须设置
|
||||||
|
*/
|
||||||
|
public priority: number = 0;
|
||||||
|
/**
|
||||||
|
* 由优先级队列使用-不要编辑此值。表示插入节点的顺序
|
||||||
|
*/
|
||||||
|
public insertionIndex: number = 0;
|
||||||
|
/**
|
||||||
|
* 由优先级队列使用-不要编辑此值。表示队列中的当前位置
|
||||||
|
*/
|
||||||
|
public queueIndex: number = 0;
|
||||||
|
}
|
||||||
9
source/src/Math/Point.ts
Normal file
9
source/src/Math/Point.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
class Point {
|
||||||
|
public x: number;
|
||||||
|
public y: number;
|
||||||
|
|
||||||
|
constructor(x: number, y: number){
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user