[add] first
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { _decorator, Component, Node, Camera, v3, Color, renderer, debug, director } from 'cc';
|
||||
import { EDITOR } from 'cc/env';
|
||||
const { ccclass, property, executeInEditMode } = _decorator;
|
||||
|
||||
@ccclass('navigation_debugger')
|
||||
@executeInEditMode
|
||||
export class navigation_debugger extends Component {
|
||||
|
||||
start() {
|
||||
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
if(EDITOR) this.editorUpdate();
|
||||
}
|
||||
|
||||
editorUpdate() {
|
||||
const render = director.root?.pipeline.geometryRenderer;
|
||||
render?.addLine(v3(0, 0, 0), v3(3, 0, 3), Color.GREEN, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "8cffe35b-a6bd-4658-8562-5cb4bdc27bc5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import { _decorator, Component, Node, Renderer, Line, v3, Vec2, Vec3, Graphics, gfx, debug, geometry, randomRangeInt, path, IVec3Like, math } from 'cc';
|
||||
import { EDITOR } from 'cc/env';
|
||||
import { DataNavigationInst } from '../data/data-core';
|
||||
const { ccclass, property, executeInEditMode } = _decorator;
|
||||
|
||||
@ccclass('NavigationMap')
|
||||
@executeInEditMode
|
||||
export class NavigationMap extends Component {
|
||||
|
||||
points:Vec3[] = [];
|
||||
|
||||
__preload() {
|
||||
//Navigation.init(this.node);
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
|
||||
if (EDITOR) {
|
||||
//this.updateEditModel();
|
||||
}
|
||||
}
|
||||
|
||||
updateEditModel() {
|
||||
console.log('navigation edit');
|
||||
this.linkChildNode(this.node);
|
||||
}
|
||||
|
||||
linkChildNode(root:Node) {
|
||||
const children = root.children;
|
||||
|
||||
for(let i = 0; i < children.length - 1; i++) {
|
||||
const p0 = children[i];
|
||||
const p1 = children[i + 1];
|
||||
const pos0 = p0.position;
|
||||
const pos1 = p1.position;
|
||||
//let line = new geometry.Line(pos0.x, pos0.y, pos0.z, pos1.x, pos1.y, pos1.z);
|
||||
//const l0 = p0.getComponent(Line);
|
||||
//l0.positions[0] = pos0 as never;
|
||||
//l0.positions[1] = pos1 as never;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
export class Navigation {
|
||||
|
||||
public static node:Node;
|
||||
|
||||
public static init(_node:Node) {
|
||||
this.node = _node;
|
||||
}
|
||||
|
||||
static calculateRandomPoint(curPos:Vec3) {
|
||||
|
||||
// find closet point.
|
||||
var closet:Node | undefined = this.findChildren(this.node, curPos);
|
||||
|
||||
if (closet === undefined) {
|
||||
console.error('closet not find', curPos, this.node);
|
||||
return [];
|
||||
}
|
||||
|
||||
// random target node.
|
||||
const target:Node = this.randomChildren();
|
||||
|
||||
// go target node.
|
||||
let paths:Vec3[] = [];
|
||||
paths.push(curPos);
|
||||
paths.push(closet.worldPosition);
|
||||
this.findTargetNode(paths, closet, target);
|
||||
|
||||
return paths;
|
||||
|
||||
}
|
||||
|
||||
static findChildren(node:Node, curPos:Vec3) {
|
||||
let minDistance = Number.MAX_VALUE;
|
||||
const children = node.children;
|
||||
let minNode:Node | undefined = undefined;
|
||||
for(let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
if (node === child) continue;
|
||||
const distance = Vec3.distance(curPos, child.worldPosition);
|
||||
if (distance < minDistance) {
|
||||
minNode = child;
|
||||
minDistance = distance;
|
||||
}
|
||||
}
|
||||
return minNode;
|
||||
}
|
||||
|
||||
static randomChildren() {
|
||||
const randomIndex = randomRangeInt(0, this.node.children.length);
|
||||
return this.node.children[randomIndex];
|
||||
}
|
||||
|
||||
static findTargetNode(paths:Vec3[], node:Node, target:Node) {
|
||||
const children = node.parent?.children ?? undefined;
|
||||
if (children === undefined) return;
|
||||
for(let i = 0; i < children.length; i++) {
|
||||
paths.push(children[i].worldPosition);
|
||||
if (children[i] === node) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "79294abb-dae0-4b09-b446-95ea5027d0e0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { _decorator, Component, Node, debug, Camera, Vec3, v3, PhysicsSystem, geometry, IVec3Like, math, url, CCFloat } from 'cc';
|
||||
import { EDITOR } from 'cc/env';
|
||||
import { DebugUtil } from '../../core/util/debug-util';
|
||||
import { Gizmo, UtilVec3 } from '../../core/util/util';
|
||||
const { ccclass, property, executeInEditMode } = _decorator;
|
||||
|
||||
@ccclass('NavigationPoint')
|
||||
@executeInEditMode
|
||||
export class NavigationPoint extends Component {
|
||||
|
||||
@property([Node])
|
||||
linkNodes: Node[] = [];
|
||||
|
||||
@property([CCFloat])
|
||||
weights: number[] = [];
|
||||
|
||||
@property
|
||||
radius = 5;
|
||||
|
||||
@property
|
||||
showRay = false
|
||||
|
||||
@property
|
||||
segment = 20;
|
||||
|
||||
_rays: Array<Vec3> | undefined
|
||||
|
||||
onEnable () {
|
||||
// Calculate radius
|
||||
if (EDITOR) {
|
||||
const segment = this.segment;
|
||||
const ray = new geometry.Ray();
|
||||
let minDistance = 200;
|
||||
this._rays = Array(segment);
|
||||
//Ray test
|
||||
for (let i = 0; i < segment; i++) {
|
||||
const angle = 360 / segment * i;
|
||||
let direction = v3(0, this.node.worldPosition.y, -1);
|
||||
Vec3.rotateY(direction, direction, this.node.worldPosition, math.toRadian(angle));
|
||||
ray.o = this.node.worldPosition;
|
||||
ray.d = direction.subtract(this.node.worldPosition);
|
||||
this._rays[i] = direction;
|
||||
if (PhysicsSystem.instance.raycastClosest(ray, undefined, 100)) {
|
||||
const result = PhysicsSystem.instance.raycastClosestResult;
|
||||
const hitPoint = result.hitPoint;
|
||||
const currentDistance = Vec3.distance(this.node.worldPosition, hitPoint);
|
||||
if (currentDistance < minDistance) {
|
||||
minDistance = currentDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.radius = Number(minDistance.toFixed(3));
|
||||
}
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
if (EDITOR) {
|
||||
for (let i = 0; i < this.linkNodes.length; i++) {
|
||||
Gizmo.drawLine(this.node.worldPosition, this.linkNodes[i].worldPosition);
|
||||
}
|
||||
Gizmo.drawCircle(this.node.position, this.radius);
|
||||
if (this.showRay) {
|
||||
for (let i = 0; i < this._rays!.length; i++) {
|
||||
let target = v3(0, 0, 0);
|
||||
UtilVec3.copy(target, this._rays![i]);
|
||||
Gizmo.drawLine(this.node.worldPosition, target.add(this.node.worldPosition));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "54e2e98f-6d5c-485e-95b1-28e4623badf5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { _decorator, Component, Node } from 'cc';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('NavigationPoints')
|
||||
export class navigation_points extends Component {
|
||||
start() {
|
||||
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "8f3997de-73e7-49f7-a604-c2684a225478",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
import { _decorator, Component, Node, v3, Color, geometry, Vec3, PhysicsSystem, math, JsonAsset, IVec3Like, Input, input, EventKeyboard, KeyCode, color, game } from 'cc';
|
||||
import { EDITOR } from 'cc/env';
|
||||
import { JsonTool } from '../../core/io/json-tool';
|
||||
import { Gizmo, UtilVec3 } from '../../core/util/util';
|
||||
import { NavigationPoint } from './navigation-point';
|
||||
import { NavSystem } from './navigation-system';
|
||||
const { ccclass, property, executeInEditMode } = _decorator;
|
||||
|
||||
@ccclass('NavigationRegion')
|
||||
@executeInEditMode
|
||||
export class NavigationRegion extends Component {
|
||||
|
||||
@property
|
||||
maxDistance = 15;
|
||||
|
||||
@property
|
||||
height = 0.5;
|
||||
|
||||
@property
|
||||
maxHeight = 3;
|
||||
|
||||
@property
|
||||
slopHeight = 1;
|
||||
|
||||
@property
|
||||
slopDistance = 7;
|
||||
|
||||
@property
|
||||
mapBlockX = 15;
|
||||
|
||||
@property
|
||||
mapBlockY = 3;
|
||||
|
||||
@property
|
||||
mapBlockZ = 15;
|
||||
|
||||
@property
|
||||
testPath = false;
|
||||
|
||||
@property(Node)
|
||||
testNode:Node | undefined;
|
||||
|
||||
findPaths = Array<NavSystem.NavPointType>();
|
||||
|
||||
onEnable() {
|
||||
if(EDITOR) {
|
||||
this.refreshMapPoints();
|
||||
}
|
||||
}
|
||||
|
||||
refreshMapPoints() {
|
||||
const children = this.node.children;
|
||||
let data = {
|
||||
blockX: this.mapBlockX,
|
||||
blockY: this.mapBlockY,
|
||||
blockZ: this.mapBlockZ,
|
||||
count:children.length,
|
||||
nodeMap:{},
|
||||
nodes:[],
|
||||
links:[],
|
||||
weights:[]
|
||||
};
|
||||
for(let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
child.name = `point_${i}`;
|
||||
const navigationPoint = child.getComponent(NavigationPoint);
|
||||
if(!navigationPoint) {
|
||||
child.addComponent(NavigationPoint);
|
||||
}
|
||||
const worldPosition = child.worldPosition;
|
||||
const pos = {
|
||||
x:Number(worldPosition.x.toFixed(3)),
|
||||
y:Number(worldPosition.y.toFixed(3)),
|
||||
z:Number(worldPosition.z.toFixed(3)),
|
||||
id:child.getSiblingIndex(),
|
||||
radius:navigationPoint?.radius,
|
||||
};
|
||||
const keyX = Math.floor(pos.x / this.mapBlockX);
|
||||
const keyY = Math.floor(pos.y / this.mapBlockY);
|
||||
const keyZ = Math.floor(pos.z / this.mapBlockZ);
|
||||
const key = `${keyX},${keyY},${keyZ}`;
|
||||
if(data.nodeMap[key] === undefined) data.nodeMap[key] = [];
|
||||
data.nodeMap[key].push(child.getSiblingIndex());
|
||||
data.nodes.push(pos);
|
||||
const linkInfo = this.calculateCircleLink(child);
|
||||
data.links.push(linkInfo.links);
|
||||
data.weights.push(linkInfo.weights);
|
||||
}
|
||||
console.log(JsonTool.toJson(data));
|
||||
|
||||
if(this.testPath) {
|
||||
|
||||
NavSystem.Init(data);
|
||||
|
||||
// Test Random paths.
|
||||
//this.testRandomPath();
|
||||
|
||||
// Test Find paths.
|
||||
const times = 10000;
|
||||
let time = game.totalTime;
|
||||
for(let i = 0; i < times; i++) {
|
||||
this.testFindPath();
|
||||
}
|
||||
console.log('run ', times, ' time:', game.totalTime - time, ' ms');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
calculateCircleLink(node:Node):{ links:number[], weights:number[]} {
|
||||
const children = this.node.children;
|
||||
const origin = node.worldPosition;
|
||||
const ray = new geometry.Ray();
|
||||
ray.o = node.worldPosition;
|
||||
const position = v3(0, 0, 0);
|
||||
let link:Node[] = [];
|
||||
let weights:number[] = [];
|
||||
let linkIndex:number[] = [];
|
||||
for(let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
if (child === node) continue;
|
||||
UtilVec3.copy(position, child.worldPosition);
|
||||
ray.d = position.subtract(origin).normalize();
|
||||
const distance = Vec3.distance(origin, child.worldPosition);
|
||||
if (distance > this.maxDistance) continue;
|
||||
const heightDifference = Math.abs(origin.y - child.worldPosition.y);
|
||||
if (heightDifference > this.maxHeight) continue;
|
||||
if (heightDifference > this.slopHeight && distance > this.slopDistance) continue;
|
||||
if (!PhysicsSystem.instance.raycastClosest(ray, undefined, distance)){
|
||||
link.push(child);
|
||||
linkIndex.push(child.getSiblingIndex());
|
||||
weights.push(Number(distance.toFixed(3)));
|
||||
}
|
||||
}
|
||||
const navigationPoint = node.getComponent(NavigationPoint);
|
||||
navigationPoint!.linkNodes = link;
|
||||
navigationPoint!.weights = weights;
|
||||
return { links:linkIndex, weights:weights }
|
||||
}
|
||||
|
||||
update(deltaTime:number) {
|
||||
|
||||
if(EDITOR) {
|
||||
this.testFindPaths();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
testFindPaths() {
|
||||
|
||||
if(this.findPaths.length <= 0) return;
|
||||
|
||||
let p0 = v3(0, 0, 0);
|
||||
let p1 = v3(0, 0, 0);
|
||||
|
||||
//Gizmo.drawBox(p0, v3(10, 1, 10), Color.YELLOW);
|
||||
Gizmo.drawBox(this.findPaths[0] as IVec3Like, Vec3.ONE, Color.WHITE);
|
||||
|
||||
for (let i = 1; i < this.findPaths.length; i++) {
|
||||
const start = this.findPaths![i - 1] as IVec3Like;
|
||||
const end = this.findPaths![i] as IVec3Like;
|
||||
UtilVec3.copy(p0, start);
|
||||
UtilVec3.copy(p1, end);
|
||||
p0.y += 0.1;
|
||||
p1.y += 0.1;
|
||||
|
||||
const isLast = i === (this.findPaths.length - 1);
|
||||
if(isLast) {
|
||||
Gizmo.drawBox(p1, Vec3.ONE, Color.RED);
|
||||
}else{
|
||||
Gizmo.drawCircle(p1, 1, Color.RED);
|
||||
}
|
||||
Gizmo.drawLine(p0, p1, Color.RED);
|
||||
}
|
||||
}
|
||||
|
||||
testRandomPath() {
|
||||
// random point.
|
||||
const point = NavSystem.randomPoint();
|
||||
this.testNode?.setWorldPosition(point.position);
|
||||
NavSystem.randomPaths(this.findPaths, this.testNode!.worldPosition, 20, point.closestNavigationPon);
|
||||
}
|
||||
|
||||
testFindPath() {
|
||||
|
||||
// random start.
|
||||
const start = NavSystem.randomPoint();
|
||||
|
||||
// random end.
|
||||
const end = NavSystem.randomPoint();
|
||||
|
||||
NavSystem.findPaths(this.findPaths, start.position, -1, end.position);
|
||||
|
||||
//console.log('find paths:', this.findPaths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "5a2dc65c-594f-488a-bbbd-25cb17685ec5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
import { _decorator, Vec3, randomRangeInt, path, IVec3Like, math, v3, randomRange, Line, find } from 'cc';
|
||||
import { KeyAnyType } from '../data/game-type';
|
||||
const { ccclass, property, executeInEditMode } = _decorator;
|
||||
|
||||
|
||||
export namespace NavSystem {
|
||||
|
||||
export type NavPointType = {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
id: number,
|
||||
radius: number,
|
||||
}
|
||||
|
||||
let data: KeyAnyType;
|
||||
|
||||
export function Init (_data: any) {
|
||||
data = _data;
|
||||
}
|
||||
|
||||
export function nodePosition (nodeID: number) {
|
||||
return data.nodes[nodeID];
|
||||
}
|
||||
|
||||
export function randomPoint (size = 0.5) {
|
||||
const randomNode = randomRangeInt(0, data.count);
|
||||
const node = data.nodes[randomNode];
|
||||
const radius = node.radius - size;
|
||||
const position = v3(node.x + randomRange(-radius, radius), node.y, node.z + randomRange(-radius, radius));
|
||||
return { closestNavigationPon: randomNode, position: position };
|
||||
|
||||
}
|
||||
|
||||
export function randomPaths (paths: Array<NavPointType>, position: Vec3, count: number, nearest: number = -1): NavPointType[] {
|
||||
|
||||
paths.length = 0;
|
||||
|
||||
if (nearest === -1) {
|
||||
// find nearest point.
|
||||
nearest = findNearestPoint(position);
|
||||
}
|
||||
|
||||
if (nearest === -1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// search path.
|
||||
calculateRandomPaths(paths, nearest, count);
|
||||
|
||||
return paths;
|
||||
|
||||
}
|
||||
|
||||
export function randomFirePath (paths: Array<NavPointType>, node: number) {
|
||||
|
||||
paths.length = 0;
|
||||
const length = randomRangeInt(5, 11);
|
||||
const nodeData = data.nodes[node];
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
let point = v3(
|
||||
nodeData.x + randomRange(-nodeData.radius, nodeData.radius),
|
||||
nodeData.y,
|
||||
nodeData.z + randomRange(-nodeData.radius, nodeData.radius)
|
||||
)
|
||||
paths[i] = { x: point.x, y: point.y, z: point.z, id: nodeData.id, radius: nodeData.radius };
|
||||
|
||||
}
|
||||
|
||||
return paths;
|
||||
|
||||
}
|
||||
|
||||
export function findNearest (position: Vec3): number {
|
||||
|
||||
const length = data.nodes.length;
|
||||
let minlength = Number.MAX_VALUE;
|
||||
let index = -1;
|
||||
for (let i = 0; i < length; i++) {
|
||||
const node = data.nodes[i];
|
||||
const curLen = Vec3.distance(position, node);
|
||||
if (curLen < minlength) {
|
||||
index = i;
|
||||
minlength = curLen;
|
||||
}
|
||||
}
|
||||
|
||||
if (index === -1) {
|
||||
throw new Error(`'can not find target node.`);
|
||||
}
|
||||
|
||||
return index;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function findNearestPoint (position: Vec3): number {
|
||||
|
||||
if (data == undefined) {
|
||||
console.warn(' Navigation data not init.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
return findNearest(position);
|
||||
|
||||
/*
|
||||
|
||||
let closestNavigationPon = -1;
|
||||
const x = Math.floor(position.x/data.blockX);
|
||||
const y = Math.floor(position.y/data.blockY);
|
||||
const z = Math.floor(position.z/data.blockZ);
|
||||
|
||||
const key = `${x},${y},${z}`;
|
||||
const blockNodes = data.nodeMap[key];
|
||||
if(blockNodes === undefined) {
|
||||
console.warn(`Can not find block:${key}, position:${position}`)
|
||||
return -1;
|
||||
}
|
||||
|
||||
let minDistance = Number.MAX_VALUE;
|
||||
//console.log(blockNodes);
|
||||
for (let i = 0; i < blockNodes.length; i++) {
|
||||
const nodeID = blockNodes[i]
|
||||
const nodePosition = data.nodes[nodeID];
|
||||
const currentDistance = Vec3.distance(position, nodePosition);
|
||||
if(currentDistance < minDistance) {
|
||||
closestNavigationPon = nodeID;
|
||||
}
|
||||
}
|
||||
|
||||
return closestNavigationPon;
|
||||
*/
|
||||
}
|
||||
|
||||
function calculateRandomPaths (paths: Array<IVec3Like>, start: number, count: number) {
|
||||
|
||||
if (data == undefined) {
|
||||
console.warn(' Navigation data not init.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
paths[0] = data.nodes[start];
|
||||
//console.log('start node:', start, paths[0]);
|
||||
let currentNode = start;
|
||||
|
||||
for (let i = 1; i < count; i++) {
|
||||
// random children.
|
||||
const links = data.links[currentNode];
|
||||
const randomLinkIndex = randomRangeInt(0, links.length);
|
||||
currentNode = links[randomLinkIndex]
|
||||
paths[i] = (data.nodes[currentNode]);
|
||||
//console.log('point_', currentNode, links, randomLinkIndex, paths[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type PathPoint = {
|
||||
node: number,
|
||||
g: number,
|
||||
h: number,
|
||||
f: number,
|
||||
parent: PathPoint | undefined
|
||||
}
|
||||
|
||||
|
||||
export function findPaths (paths: Array<NavPointType>, start: Vec3, startNearest: number = -1, end: Vec3): NavPointType[] {
|
||||
|
||||
paths.length = 0;
|
||||
|
||||
// open table.
|
||||
let openTable: PathPoint[] = [];
|
||||
|
||||
// close table.
|
||||
let closeTable: PathPoint[] = [];
|
||||
|
||||
if (startNearest === -1) {
|
||||
// find nearest point.
|
||||
startNearest = findNearestPoint(start);
|
||||
openTable.push({ node: startNearest, g: 0, h: 0, f: 0, parent: undefined });
|
||||
}
|
||||
|
||||
// find nearest end point.
|
||||
const endNearest = findNearestPoint(end);
|
||||
//console.log('endNearest id', endNearest);
|
||||
|
||||
// check start equal end.
|
||||
if (startNearest === endNearest) {
|
||||
paths.push(data.nodes[startNearest]);
|
||||
return paths;
|
||||
}
|
||||
|
||||
const findMinCostPoint = function (): number {
|
||||
|
||||
if (openTable.length <= 0) return -1;
|
||||
|
||||
let cost = Number.MAX_VALUE;
|
||||
let minNode = -1;
|
||||
for (let i = 0; i < openTable.length; i++) {
|
||||
const current = openTable[i];
|
||||
if (current.f < cost) {
|
||||
minNode = i;
|
||||
cost = current.f;
|
||||
}
|
||||
}
|
||||
return minNode;
|
||||
}
|
||||
|
||||
const checkInOpenTable = function (node: number) {
|
||||
for (let openTableI = 0; openTableI < openTable.length; openTableI++) {
|
||||
if (openTable[openTableI].node === node) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const checkInCloseTable = function (node: number) {
|
||||
for (let closeTableI = 0; closeTableI < closeTable.length; closeTableI++) {
|
||||
if (closeTable[closeTableI].node === node) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const pushOpenTable = function (node: number, parent: PathPoint) {
|
||||
|
||||
const nodeData = data.nodes[node];
|
||||
|
||||
// find target.
|
||||
if (nodeData.id == endNearest) {
|
||||
//console.log('find target.', nodeData.id, ' target id:', endNearest);
|
||||
return { node, g: 0, h: 0, f: 0, parent };
|
||||
}
|
||||
|
||||
//console.log(start, nodeData);
|
||||
|
||||
const g = Vec3.distance(start, nodeData);
|
||||
|
||||
const h = Vec3.distance(nodeData, end);
|
||||
|
||||
const f = g + h;
|
||||
|
||||
//console.log('distances start:', distanceStart, 'distances target:', distanceTarget, 'f:', f);
|
||||
|
||||
openTable.push({ node, f, g, h, parent });
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
const searchNeighbor = function (parent: PathPoint) {
|
||||
|
||||
const links = data.links[parent.node];
|
||||
|
||||
//console.log('node:', node, 'neighbor links', links);
|
||||
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
|
||||
const linkNode = links[i];
|
||||
// find in close table.
|
||||
// console.log('neighbor:', links[i], 'close table:', closeTable, 'state:', inCloseTable);
|
||||
if (checkInCloseTable(linkNode)) continue;
|
||||
|
||||
// find in open table.
|
||||
if (checkInOpenTable(linkNode)) continue;
|
||||
|
||||
// push in open table.
|
||||
const findPathPoint = pushOpenTable(linkNode, parent);
|
||||
if (findPathPoint) return findPathPoint;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
const find = function () {
|
||||
|
||||
// find min cost point.
|
||||
const minNodeIndex = findMinCostPoint();
|
||||
|
||||
if (minNodeIndex == -1) {
|
||||
//console.log('can not find target.');
|
||||
return null;
|
||||
}
|
||||
|
||||
//console.log('open table:', openTable, 'minNode:', minNode);
|
||||
|
||||
// open table.
|
||||
const minNode = openTable[minNodeIndex];
|
||||
|
||||
// remove open table.
|
||||
openTable.splice(minNodeIndex, 1);
|
||||
|
||||
// insert close table.
|
||||
closeTable.push(minNode);
|
||||
|
||||
// search neighbors.
|
||||
const findPathPoint = searchNeighbor(minNode);
|
||||
if (findPathPoint) {
|
||||
//console.log('find node target:', findPathPoint);
|
||||
return findPathPoint;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
}
|
||||
|
||||
const calculateParent = function (targetNode: PathPoint) {
|
||||
|
||||
const parent = targetNode.parent;
|
||||
if (parent == undefined) return;
|
||||
const node = data.nodes[parent.node];
|
||||
paths.push(node);
|
||||
calculateParent(parent);
|
||||
|
||||
}
|
||||
|
||||
let index = -1;
|
||||
let max = 112;
|
||||
let findTargetPoint: PathPoint | undefined | null;
|
||||
|
||||
while (true) {
|
||||
index++;
|
||||
//console.log('index:', index, 'close table count:', closeTable.length, 'open table count:', openTable.length);
|
||||
if (index > max) break;
|
||||
findTargetPoint = find();
|
||||
if (findTargetPoint !== undefined) break;
|
||||
}
|
||||
|
||||
//calculate paths
|
||||
if (findTargetPoint) {
|
||||
|
||||
//console.log('find target point:', findTargetPoint);
|
||||
|
||||
// push end node.
|
||||
paths.push(data.nodes[endNearest]);
|
||||
|
||||
// get end to start list.
|
||||
calculateParent(findTargetPoint);
|
||||
|
||||
paths.reverse();
|
||||
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "6d9ee84f-460a-4d9e-96ac-0c5be5893ac2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user