As shown in the title, the project I created using ts and webback was used according to the documents on the official website. I found that the recastjsplugin object could not be created. Then I searched the Forum on the official website for the answer. According to the practice of many students before, I called recast through NPM, but the following error occurred in the call. What is the reason?
babylon.js:1 Uncaught TypeError: this.bjsRECAST.Vec3 is not a constructor
at new e (babylon.js:1:5004387)
at NavMeshParametersInspector.CreateNavMeshGround (navMeshParametersInspector.tsx:438:24)
at NavMeshParametersInspector.NavMeshCreate (navMeshParametersInspector.tsx:412:14)
at onClick (navMeshParametersInspector.tsx:181:35)
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
at invokeGuardedCallback (react-dom.development.js:4277:1)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:4291:1)
at executeDispatch (react-dom.development.js:9041:1)
at processDispatchQueueItemsInOrder (react-dom.development.js:9073:1)
cc @Cedric
@zertaul
Did you try this ? Using Recast.js via NPM package import - #15 by Rata_Blanca
Can you provide a test repo ?
I used this method, and found the problem in the past two days. The recast class needs an asynchronous method at the beginning of loading. After I changed the code of the created object to asynchronous, I succeeded. But now there is a new problem. In the process of porting the code to another project, the results are different,
This is the case of creating a scene separately
Related parameters:
Here’s how i migrated to another project
Related parameters:
I now know that the two methods of creating models are different. Which one of the above uses meshbuilder to create models, and the one below should use mesh Createxxx is created in this way
I don’t know why it’s so different
import React from “react”;
import * as BABYLON from ‘babylonjs’;
import Recast from “recast-detour”;
import {RecastJSCrowd,RecastJSPlugin} from “babylonjs/Navigation/Plugins/recastJSPlugin”;
import {Simulate} from “react-dom/test-utils”;
import {Nullable} from “babylonjs/types”;
export default class App extends React.Component<any, any>{
private canvasRef = React.createRef<HTMLCanvasElement>();
private navmeshParameters = {};
private navigationPlugin:any;
// 定义获取地面位置函数
private startingPoint: BABYLON.Vector3 | null;
private currentMesh: BABYLON.AbstractMesh | null;
private pathLine: Nullable<BABYLON.LinesMesh>;
constructor(props: any) {
super(props);
this.startingPoint = null;
this.currentMesh = null;
this.pathLine = null;
}
componentDidMount() {
const canvas = this.canvasRef.current;
if (canvas){
const engine = new BABYLON.Engine(canvas,true);
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 4, Math.PI / 3, 10, BABYLON.Vector3.Zero(), scene);
this.customCameraInputHandler(camera,canvas)
const light = new BABYLON.HemisphericLight("ligth",new BABYLON.Vector3(0,1,0),scene)
const staticMesh = this.CreateScene(scene);
const plugin = this.CreatePlugin();
plugin.then((item)=>{
if (item !== null){
const navmesh = this.CreateNavMesh(item,staticMesh,scene)
const agents = this.CreateCrowd(item,scene,camera)
}
})
engine.runRenderLoop(()=>{
scene.render();
})
window.addEventListener("resize",()=>{
engine.resize()
})
}
}
customCameraInputHandler = (camera:BABYLON.ArcRotateCamera,canvas:HTMLCanvasElement) =>{
camera.inputs.clear()
// const mouseInput = new BABYLON.ArcRotateCameraMouseInput();
// mouseInput.buttons = [2];
// camera.inputs.add(mouseInput);
const wheelInput = new BABYLON.ArcRotateCameraMouseWheelInput();
camera.inputs.add(wheelInput);
camera.panningSensibility = 0;
camera.attachControl(canvas,true)
}
CreateScene = (scene:BABYLON.Scene):BABYLON.Mesh[] =>{
const box = BABYLON.MeshBuilder.CreateBox("box",{},scene)
box.position = new BABYLON.Vector3(0,0.5,0);
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere",{},scene)
sphere.position = new BABYLON.Vector3(1,0,0)
sphere.scaling = new BABYLON.Vector3(2,2,2)
const capsule = BABYLON.MeshBuilder.CreateCapsule("capsule",{},scene);
capsule.position = new BABYLON.Vector3(1,0.1,2)
capsule.scaling = new BABYLON.Vector3(2,5,2);
const cylinder = new BABYLON.MeshBuilder.CreateCylinder("cylinder",{height:2,diameterTop:2},scene);
cylinder.position = new BABYLON.Vector3(-3,0,-1);
const ground = BABYLON.MeshBuilder.CreateGround("ground",{width:10,height:10},scene)
const mat = new BABYLON.StandardMaterial("mat",scene)
mat.diffuseColor = new BABYLON.Color3(0,1,1)
cylinder.material = mat
capsule.material = mat
box.material = mat
sphere.material = mat
const mat2 = new BABYLON.StandardMaterial("mat2",scene)
mat2.diffuseColor = new BABYLON.Color3(0.5,0.5,0.5)
ground.material = mat2
return scene.meshes as BABYLON.Mesh[];
}
async CreatePlugin() {
try {
const recast = (Recast).call({})
console.log(recast);
const plugin = new BABYLON.RecastJSPlugin(await (Recast).call({}))
return plugin;
}
catch (error){
console.log("出错:",error)
return null;
}
}
CreateNavMesh = (plugin:BABYLON.RecastJSPlugin, staticMesh: BABYLON.Mesh[], scene: BABYLON.Scene) => {
console.log(this.navmeshParameters)
const navmeshParameters = {
cs: 0.15,
ch: 0.2,
walkableSlopeAngle: 90,
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 navdebug = plugin.createDebugNavMesh(scene)
const mat3 = new BABYLON.StandardMaterial("matdebug",scene)
mat3.diffuseColor = new BABYLON.Color3(0.1, 0.2, 1);
mat3.alpha = 0.2;
navdebug.material = mat3
navdebug.position = new BABYLON.Vector3(0,-0.1,0)
return navdebug;
}
CreateCrowd = (item:BABYLON.RecastJSPlugin,scene:BABYLON.Scene,camera:BABYLON.ArcRotateCamera) =>{
const agents = [];
const agentParams = {
radius: 0.1,
height: 0.2,
maxAcceleration: 4.0,
maxSpeed: 1.0,
collisionQueryRange: 0.5,
pathOptimizationRange: 0.0,
separationWeight: 1.0
};
const crowd: BABYLON.ICrowd =item.createCrowd(10,0.1,scene);
let i
// 循环创建代理对象
for (i = 0; i <4; i++) {
const width = 0.20;
const agentCube = BABYLON.MeshBuilder.CreateBox("cube", { size: width, height: width }, scene);
const targetCube = BABYLON.MeshBuilder.CreateBox("cube", { size: 0.1, height: 0.1 }, scene);
const matAgent = new BABYLON.StandardMaterial('mat2', scene);
const variation = Math.random();
matAgent.diffuseColor = new BABYLON.Color3(0.4 + variation * 0.6, 0.3, 1.0 - variation * 0.3);
agentCube.material = matAgent;
const randomPos = item.getRandomPointAround(new BABYLON.Vector3(-5.0, 0.1, -3.0), 0.5);
const transform = new BABYLON.TransformNode('trf');
// agentCube.parent = transform;
const agentIndex = crowd.addAgent(randomPos, agentParams, transform);
agents.push({idx:agentIndex, trf:transform, mesh:agentCube, target:targetCube});
}
scene.onPointerObservable.add((pointerInfo)=>{
// 0左键,2右键
if (pointerInfo.event.button === 0)
{
switch (pointerInfo.type){
case BABYLON.PointerEventTypes.POINTERDOWN:
if (pointerInfo.pickInfo.hit){
this.PointerDown(item,pointerInfo.pickInfo.pickedMesh,crowd,camera,scene)
}
break;
}
}
})
scene.onBeforeRenderObservable.add(()=>{
const agentCount = agents.length;
for (let i = 0;i < agentCount;i++){
const ag = agents[i]
ag.mesh.position = crowd.getAgentPosition(ag.idx);
const vel = crowd.getAgentVelocity(ag.idx)
crowd.getAgentNextTargetPathToRef(ag.idx,ag.target.position)
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;
}
}
})
return agents;
}
getGroundPosition = (scene:BABYLON.Scene) =>{
const pickinfo = scene.pick(scene.pointerX,scene.pointerY)
if (pickinfo.hit){
return pickinfo.pickedPoint;
}
return null;
}
PointerDown = (item: BABYLON.RecastJSPlugin, mesh:BABYLON.AbstractMesh,crowd:BABYLON.ICrowd,camera:BABYLON.ArcRotateCamera,scene:BABYLON.Scene) =>{
this.currentMesh = mesh;
const name = "ribbon";
this.pathLine = scene.getMeshByName(name);
if (this.pathLine){
scene.removeMesh(this.pathLine);
this.pathLine = null;
}
this.startingPoint = this.getGroundPosition(scene)
if (this.startingPoint !== null){
// setTimeout(()=>{
// // camera.detachControl(this.canvasRef.current)
// },0.1)
const agents = crowd.getAgents()
let i
for (i = 0; i< agents.length;i++){
const randomPos = item.getRandomPointAround(this.startingPoint, 1.0);
crowd.agentGoto(agents[i],item.getClosestPoint(this.startingPoint));
}
const pathPoint = item.computePath(crowd.getAgentPosition(agents[0]),item.getClosestPoint(this.startingPoint))
if (!this.pathLine){
this.pathLine = BABYLON.MeshBuilder.CreateDashedLines("ribbon",{points:pathPoint,updatable:true,instance:this.pathLine},scene)
console.log("pahtline ", this.pathLine);
}
}
}
render() {
return (<canvas id="renderCanvas" ref={this.canvasRef}></canvas>);
}
}
This is my sample code of TSX type, but the part of creating the pathfinding map is the same as that of porting the previous code. The modeling aspect is the difference I said before. One is built using meshbuilder, and the other is built using mesh.createxxx
Whether using meshbuilder or mesh.create, at the end, what is important is to have a valid mesh with triangulated geometry. Loading geometry from .gltf is valid as well.
This said, my guess here is the offset exists in both scenes but it’s more visible because of the world size.
Do the mesh creation calls have the same paramters (radius, size,…) ?
Did you try to export scenes as .glb and open the .glb in the other scene to compare sizes?
I haven’t done this comparison, but I can post the code created by those models
In addition, I will export the model of that scene and compare it myself. I will give you feedback on the results in time
This is almost impossible to troubleshoot from forum shared code. Accurate playground repro leads to faster resolution.