2023-02-22 09:50:51 +08:00

197 lines
6.0 KiB
TypeScript

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);
}
}