There is another problem. I want to realize that the coordinate position of the pathfinding object will immediately switch to the starting point after reaching the target and continue to move forward to the currently specified target point. However, after reaching the target point, the pathfinding object does switch to the vertex, but the object does not continue to move forward to the destination as before. What is the reason?
The following is the specific source code
import React from "react";
import {
Engine,
Scene,
Vector3,
HemisphericLight,
Mesh,
MeshBuilder,
StandardMaterial,
Color3,
ArcRotateCamera,
ArcRotateCameraPointersInput,
ArcRotateCameraMouseWheelInput,
RecastJSPlugin,
ICrowd,
TransformNode,
LinesMesh,
} from "babylonjs";
import Recast from "recast-detour";
interface TargetPathState{
boxNumber:number;
endPoint:Vector3;
startPoint:Vector3;
keydown:boolean;
size:number;
agents:any[];
}
export default class TargetPath extends React.Component<any,TargetPathState>{
private canvasRef = React.createRef<HTMLCanvasElement>();
private pathLine:LinesMesh | null = null;
private pathLines: LinesMesh[] = [];
private _crowd:ICrowd;
private _plugin;
constructor(props:TargetPathState) {
super(props);
this.state={
startPoint:new Vector3(-4,0.1,-3.5),
endPoint:new Vector3(5,2.6,2),
boxNumber:8,
keydown:false,
size:0.2,
agents:[],
}
}
componentDidMount() {
const canvas = this.canvasRef.current;
if (canvas){
const engine = new Engine(canvas,true)
const scene = new Scene(engine);
const camera = new ArcRotateCamera("camera", -Math.PI / 4, Math.PI / 3, 10, Vector3.Zero(), scene);
this.customCameraInputHandler(camera,canvas)
const ligths = new HemisphericLight("ligth",new Vector3(2,1,0),scene)
const staticMesh = this.CreateScene(scene)
engine.runRenderLoop(()=>{
scene.render()
})
window.addEventListener("resize",()=>{
engine.resize()
})
window.addEventListener("keypress",(event)=>{
if(event.key === "1" && !this.state.keydown){
if (!this.state.keydown){
this.setState({
keydown:true,
},()=>{
if (this.pathLines.length > 0){
this.pathLines.forEach(line=>{
scene.removeMesh(line);
})
this.pathLines = [];
}
this.state.agents.forEach((ag)=>{
const pos = this._crowd.getAgentPosition(ag.idx)
const vel = this._crowd.getAgentVelocity(ag.idx)
const target = ag.target;
const endrandomPos = this._plugin.getRandomPointAround(target, 0.1);
this._crowd.agentGoto(ag.idx,this._plugin.getClosestPoint(target));
const pathPoint = this._plugin.computePath(this._crowd.getAgentPosition(ag),this._plugin.getClosestPoint(target))
let line2 = null;
line2 = MeshBuilder.CreateDashedLines("ribbon",{points:pathPoint,updatable:true,instance:this.pathLine},scene)
this.pathLines.push(line2)
})
})
}
}
if (event.key === '2' && this.state.keydown){
if (this.state.keydown){
const start = this.state.startPoint
const end = this.state.endPoint
this.setState({
startPoint:end,
endPoint:start,
keydown:false,
},()=>{
})
}
}
})
const mat = new StandardMaterial("pos",scene)
mat.diffuseColor = Color3.Blue()
const startSphere = MeshBuilder.CreateSphere("start",{diameter:0.1},scene)
startSphere.position = this.state.startPoint;
startSphere.material = mat
const endSphere = MeshBuilder.CreateSphere("end",{diameter:0.1},scene)
endSphere.position = this.state.endPoint
endSphere.material = mat
const _recast = this.CreatePlugin();
_recast.then((plugin)=>{
if (plugin !== null){
this._plugin = plugin;
const navmesh = this.CreateNavMesh(plugin,staticMesh,scene)
this.CreateCrowd(plugin,scene)
}
})
}
}
customCameraInputHandler = (camera:ArcRotateCamera,canvas:HTMLCanvasElement) =>{
camera.inputs.clear()
const mouseinput = new ArcRotateCameraPointersInput()
mouseinput.buttons = [2];
camera.inputs.add(mouseinput)
const wheelInput = new ArcRotateCameraMouseWheelInput();
camera.inputs.add(wheelInput);
camera.panningSensibility = 0;
camera.attachControl(canvas,true)
}
CreateScene = (scene:Scene):Mesh[] =>{
const box = MeshBuilder.CreateBox("box",{},scene)
box.position = new Vector3(0,0,0);
box.rotation = new Vector3(0,0,120);
const box2 = MeshBuilder.CreateBox("box2",{width:4,height:1,depth:3},scene)
box2.position = new Vector3(2,0.2,0);
const box3 = MeshBuilder.CreateBox("box3",{width:6,height:1.5,depth:2},scene)
box3.position = new Vector3(-1,0.2,2.5);
const box4 = MeshBuilder.CreateBox("box4",{width:1,height:2,depth:2.5},scene)
box4.position = new Vector3(-3,0.1,1);
const box5 = MeshBuilder.CreateBox("box5",{width:1,height:1,depth:2.5},scene)
box5.position = new Vector3(-2,1,-2.5);
box5.rotation = new Vector3(0,-120,0);
const box7 = MeshBuilder.CreateBox("box6",{width:1,height:1,depth:1.5},scene)
box7.position = new Vector3(-1.5,1.2,-3.5);
box7.rotation = new Vector3(0,0,90)
const box6 = MeshBuilder.CreateBox("box6",{width:5,height:2,depth:1.5},scene)
box6.position = new Vector3(1,1,-3.5);
const sphere1 = MeshBuilder.CreateSphere("sphere1",{},scene)
sphere1.position = new Vector3(3.5,2,-3)
const ground1 = MeshBuilder.CreateGround("ground1",{width:5,height:5},scene)
ground1.position = new Vector3(3,2.5,0)
const sphere = MeshBuilder.CreateSphere("sphere",{},scene)
sphere.position = new Vector3(2,0,-1.4)
sphere.scaling = new Vector3(2,2,2)
const capsule = MeshBuilder.CreateCapsule("capsule",{},scene);
capsule.position = new Vector3(1,0.1,2)
capsule.scaling = new Vector3(2,5,2);
const cylinder = MeshBuilder.CreateCylinder("cylinder",{height:1,diameterTop:2},scene)
cylinder.position = new Vector3(-3,1,-1);
const ground = MeshBuilder.CreateGround("ground",{width:10,height:10},scene)
const mat = new StandardMaterial("mat",scene)
mat.diffuseColor = new Color3(0,1,1)
cylinder.material = mat
capsule.material = mat
box.material = mat
box2.material = mat
box3.material = mat
// sphere.material = mat
const mat2 = new StandardMaterial("mat2",scene)
mat2.diffuseColor = new Color3(0.5,0.5,0.5)
ground.material = mat2
return scene.meshes as Mesh[];
}
async CreatePlugin () {
try{
const plugin = new RecastJSPlugin(await (Recast).call({}))
return plugin;
}
catch(error){
console.log("出错:",error)
return null;
}
}
CreateNavMesh = (plugin:RecastJSPlugin, staticMesh: Mesh[], scene: Scene) => {
const navmeshParameters = {
// 地图网格精度,太小模型不会动 低于 0.03 不动,但是地图区域会变大,太大有些地方则无法到达
cs: 0.1,
// 地图网格高度,与cs数据有相关关系,太大则无法前进 0.3 就不行了
ch: 0.25,
walkableSlopeAngle: 30,
walkableHeight: 1.0,
walkableClimb: 1,
walkableRadius: 1,
maxEdgeLen: 12.,
maxSimplificationError: 1.3,
minRegionArea: 8,
mergeRegionArea: 20,
maxVertsPerPoly: 6,
detailSampleDist: 6,
detailSampleMaxError: 1,
};
if (!plugin) return null;
plugin.createNavMesh(staticMesh,navmeshParameters,(navmeshData)=>{
console.log("createNavMesh result:",navmeshData)
})
const binaryData = plugin.getNavmeshData()
// console.log(binaryData)
let navdebug = plugin.createDebugNavMesh(scene)
const mat3 = new StandardMaterial("matdebug",scene)
mat3.diffuseColor = new Color3(0.1, 0.5, 1);
mat3.alpha = 0.5;
navdebug.material = mat3
navdebug.position = new Vector3(0,-0.1,0)
navdebug?.position?.set(0,-0.05,0)
return navdebug;
}
CreateCrowd = (plugin:RecastJSPlugin,scene:Scene) =>{
const agentParams = {
// 半径,代理占的控件大小
radius: 0.1,
// 高度
height: 0.5,
// 最大加速度
maxAcceleration: 4.0,
// 行走速度
maxSpeed: 2.0,
// 碰撞间距大小
collisionQueryRange: 0.5,
// 路径优化范围
pathOptimizationRange: 0.1,
// 群体内个体间距
separationWeight: 0.5
};
this._crowd = plugin.createCrowd(10,0.1,scene);
for (let i = 0;i< this.state.boxNumber;i++){
const agentCube = MeshBuilder.CreateBox("startcube", { size: this.state.size, height: this.state.size }, scene);
// const targetCube = MeshBuilder.CreateBox("endcube",{ size: 0.1, height: 0.1 }, scene);
// 材质
const matAgent = new StandardMaterial('mat2', scene);
const variation = Math.random();
matAgent.diffuseColor = new Color3(0.4 + variation * 0.6, 0.3, 1.0 - variation * 0.3);
agentCube.material = matAgent;
const randomPos = plugin.getRandomPointAround(this.state.startPoint, 0.2);
const transform = new TransformNode('trf');
// agentCube.parent = transform;
const agentIndex = this._crowd.addAgent(randomPos, agentParams, transform);
agentCube.position = randomPos;
const _ag = this.state.agents;
_ag.push({idx:agentIndex, trf:transform, mesh:agentCube, target:this.state.endPoint});
this.setState({
agents:_ag
})
}
const callback = ()=>{
if (this.state.keydown){
this.state.agents.forEach(ag=>{
let pos = this._crowd.getAgentPosition(ag.idx)
const vel = this._crowd.getAgentVelocity(ag.idx)
const target = ag.target;
ag.mesh.position = pos;
const next = this._crowd.getAgentNextTargetPath(ag.idx)
if (vel.length() > 0.2){
vel.normalize()
const desiredRotation = Math.atan2(vel.x,vel.z)
ag.mesh.rotation.y = ag.mesh.rotation.y + (desiredRotation - ag.mesh.rotation.y) * 0.05;
}
// 一直往前的模式
this.Loopforward(ag,pos,target);
// // 往返模式
// this.RoundTripPath(ag,pos,target)
})
}
}
scene.onBeforeRenderObservable.add(callback);
}
/**
* 往返模式
* @param ag
* @param pos
* @param target
* @constructor
*/
private RoundTripPath = (ag:any,pos:Vector3,target:Vector3) =>{
if(Vector3.Distance(pos,target) < 0.5) {
// 往返
// 切换目标点
if (target.equals(this.state.endPoint)) {
ag.target = this.state.startPoint;
} else {
ag.target = this.state.endPoint;
}
// 重新计算路径
const newPathPoint = this._plugin.computePath(pos, ag.target);
console.log(ag.idx +" : "+ newPathPoint);
if (newPathPoint && newPathPoint.length > 0) {
this._crowd.agentGoto(ag.idx, this._plugin.getClosestPoint(ag.target));
}
}
}
/**
* 一直向前模式(到达终点后瞬间切换到起点)
* @param ag
* @param pos
* @param target
* @constructor
*/
private Loopforward = (ag:any,pos:Vector3,target:Vector3) =>{
// if(Vector3.Distance(pos,target) < 0.5){
if(Vector3.Distance(pos,target) < 0.5){
const randomPos = this._plugin.getRandomPointAround(this.state.startPoint, 0.2);
ag.mesh.position = this.state.startPoint;
ag.target = this.state.endPoint
// 重新计算路径
const newPathPoint = this._plugin.computePath(ag.mesh.position, ag.target);
// console.log(ag.idx +" : "+ newPathPoint);
if (newPathPoint && newPathPoint.length > 0) {
this._crowd.agentGoto(ag.idx, this._plugin.getClosestPoint(ag.target));
}
}
}
render() {
return (<canvas id="renderCanvas" ref={this.canvasRef}></canvas>);
}
}```
This is the specific source code