先看看整体的效果展示
玩家网格导航功能演示
一、前期准备
同样的你需要带骨骼动画的人物模型,这个如何制作在前面的第三人称控制有说过,同时我们需要详细的阅读下官方文档的网格导航部分内容
Crowd Navigation System | Babylon.js Documentation
二、具体实现
1.引入导航功能需要的第三方插件
你如果按照官方的流程你可能会安装 Recast 插件,然后你会发现用不了,其实是要安装 recast-detour 插件,只需要 npm install ecast-detour 就可以了。
构建导航网格可能耗费时间和资源吗,然后你就需要找到 navMeshWorker.js 的计算插件然后引入到本地就可以了,下载地址: https://github.com/BabylonJS/Babylon.js/blob/master/packages/tools/playground/public/workers/navMeshWorker.js
下你只要按照如下进行实例化就可以了
const recast = await new Recast()
this.navigationPlugin = new BABYLON.RecastJSPlugin(recast);
this.navigationPlugin.setWorkerURL(new URL(`xxx/navMeshWorker.js`, import.meta.url).href);
如果到这一步你都顺利运行了,那就成功导入插件了
2.将导航地图导入生成导航网格
根据你实际的需求来编辑好生成参数然后进行生成,你会发现这个步骤是比较耗时长的,所以你也可以用计算好的导航网格数据来直接生成大大节省了时间
let navmeshParameters = {
cs: 0.4,
ch: 0.001,
walkableSlopeAngle: 0,
walkableHeight: 0,
walkableClimb: 0,
walkableRadius: 3,
maxEdgeLen: 12,
maxSimplificationError: 1,
minRegionArea: 50,
mergeRegionArea: 20,
maxVertsPerPoly: 6,
detailSampleDist: 6,
detailSampleMaxError: 35,
// borderSize: 1,
// tileSize: 25
};
this.navigationPlugin.createNavMesh(this.navMesh, navmeshParameters,() =>{})
3.将人物加入导航
这边我特别做了可取消的导航设计,每次点击设置传送点的时候就会重新将人物加入导航,这样来实现导航过程中的打断,你仅仅只需要加个状态判断
let agentParams = {
radius: 0.3,
height: 1.8,
maxAcceleration: 50.0,
maxSpeed: 8,
collisionQueryRange: 0.5,
pathOptimizationRange: 0.2,
separationWeight: 1.0
};
let agents = crowd.getAgents();
if (agents.length == 0) {
this.navStatus = true
let position = this.navigationPlugin.getClosestPoint(new BABYLON.Vector3(player.position.x, 0, player.position.z));
let agentIndex = crowd.addAgent(position, agentParams, this.player);
player.idx = agentIndex;
}
//取消导航
crowd.removeAgent(player.idx)
4.创建带动画的传送点
为了突出传送点的效果,所以你要做个 mesh 来展示下
createPointNav() {
this.pointNav = BABYLON.MeshBuilder.CreateGround("pointNav", { width: 1, height: 1 }, this.scene);
//pointNav = BABYLON.MeshBuilder.CreateBox("pointNav", {size: 2, height:0.01}, scene);
this.pointNav.disableLighting = true;
let pointNavMaterial = new BABYLON.StandardMaterial("pointNavMaterial", this.scene);
pointNavMaterial.diffuseTexture = new BABYLON.Texture("./model/dialog/circles.png");
pointNavMaterial.diffuseTexture.hasAlpha = true;
pointNavMaterial.useAlphaFromDiffuseTexture = true;
pointNavMaterial.specularPower = 0;
pointNavMaterial.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1);
pointNavMaterial.roughness = 1;
pointNavMaterial.alphaCutOff = 0.2;
pointNavMaterial.backFaceCulling = false;
pointNavMaterial.reflectionLevel = null;
pointNavMaterial.emissiveColor = BABYLON.Color3.White();
pointNavMaterial.emissiveTexture = pointNavMaterial.albedoTexture;
pointNavMaterial.emissiveIntensity = 0.3;
this.pointNav.material = pointNavMaterial;
this.pointNav.visibility = 0;
this.scene.registerAfterRender(() => {
this.pointNav.rotation.y = this.pointNav.rotation.y + 0.03;
});
}
5.为人物导航增加切换的过程动作
人物过程行走的动作和站着的动作之间的切换需要做个缓动,使用了动作权重实现,为了方便调用使用了生成器行数来实现
* animationBlending(fromAnim, fromAnimSpeedRatio, toAnim, toAnimSpeedRatio, repeat, animationBlendingSpeed) {
let currentWeight = 1;
let newWeight = 0;
fromAnim.stop();
toAnim.play(repeat);
fromAnim.speedRatio = fromAnimSpeedRatio;
toAnim.speedRatio = toAnimSpeedRatio;
while (newWeight < 1) {
newWeight += animationBlendingSpeed;
currentWeight -= animationBlendingSpeed;
toAnim.setWeightForAllAnimatables(newWeight);
fromAnim.setWeightForAllAnimatables(currentWeight);
yield;
}
}