原文参考我的公众号文章 # threejs碰撞检测-后退后退,高低楼梯一口气搞定!
物体挪动 - 前后碰撞检测
次要还是依附Raycaster(origin, direction)
射线检测。在物体前后挪动时,实时获取物体的地位origin=target.position.clone()
作为「射线发射点」,并通过player.getWorldDirection(dir)
实时获取物体的方向作为射线「发射方向」,当物体挪动的 forward>0,示意后退,否则示意后退。如果是后退,则dir.negate();
方向反转。
效果图
GIF 图只有 4fps,20 品质,所以很卡。否则图片太大了
次要代码,正文具体阐明了过程
/** * JoyStick控制器-角色碰撞检测【前|后】 * @param {Object3D} target 须要碰撞检测的挪动物体 * @param {THREE.Raycaster} raycaster 挪动物体的碰撞检测射线 * @param {Number} intersectDistance 检测间隔,小于此间隔示意碰撞了 * @param {Boolean} recursive 是否递归检测场景中的物体 * @returns Boolean */function checkCollide( target, raycaster, intersectDistance = 2, recursive = true) { // 是否在【后退】 let isForward = this.js.forward >= 0; // 获取target以后地位,Y轴加一个固定量,代表纵轴射线发射(检测碰撞的)地位 let origin = target.position.clone().add(new THREE.Vector3(0, 1, 0)); // 获取target以后朝向 let direction = new THREE.Vector3(); //定义一个方向向量 this.player.getWorldDirection(direction); direction.normalize(); this.playerLight.position.x = this.player.position.x; this.playerLight.position.y = this.player.position.y + 2; this.playerLight.position.z = this.player.position.z; // 如果在【后退】,检测方向direction取反 if (!isForward) { direction.negate(); // console.log("move fallback:", direction); } else { // console.log("move forward:", direction); } // 设置射线发射地位 raycaster.ray.origin.copy(origin); // 设置射线发射方向 raycaster.ray.direction.copy(direction); // 开始【前、后】检测:对于blender制作的模型,须要递归遍历所有child,否则无奈实现射线碰撞检测{[childs], true} let ins = raycaster.intersectObjects(this.objects, recursive); if (ins.length) { let { distance, object } = ins[0]; if (object.name == "stair") { return false; } if (distance < intersectDistance) { console.warn("碰撞了:", object); // 主动回退1米,给接下来小角度后退留余地,否则会继续触发碰撞条件而无奈挪动 if (isForward) { console.log("后退过程碰撞了,主动后退1米"); this.player.translateZ(-1); } else { console.log("后退过程碰撞了,主动后退1米"); this.player.translateZ(+1); } return true; } } return false;}
一些优化
- 产生碰撞时,依据此前挪动方向,向相同的方向回退 1m(
this.player.translateZ(±1);
),这样能够避免转弯时继续碰撞物体 - TODO 还未解决在楼梯下后退后退对楼梯的碰撞检测
物体挪动 - 高低楼梯
次要还是依附 THREE.Raycaster(origin, dir)
射线检测。从挪动物体的顶部的中央收回射线,方向朝下,检测楼梯,进行间隔差值判断。须要留神的点在于要躲避人「走在楼梯下」的场景,不要误判为「上楼」。
效果图
GIF 图只有 4fps,20 品质,所以很卡。否则图片太大了
高低楼梯射线碰撞检测代码
/** * target 检测的挪动指标 */function checkStairCollide(target) { // 获取target以后地位,Y轴加一个固定量(50米),代表纵轴射线发射(检测碰撞的)地位 let origin = target.position.clone().add(new THREE.Vector3(0, 50, 0)); // 获取target以后朝向 let direction = new THREE.Vector3(0, -1, 0); //定义一个向下的方向向量 let raycaster = new THREE.Raycaster(origin, direction); let ins = raycaster.intersectObjects(this.objects, true); if (ins.length) { let { distance, object } = ins[0]; const name = object.name; const oneStairHeight = 0.4; //一个楼梯高度 const fallenSpeed = 8; //脱离楼梯或高台后的坠落速度 let diffY = origin.y - distance; let targetY = target.position.y; // 脱离楼梯或高台,开始坠落 if (name.includes("floor") || name.includes("ground")) { // 坠落动画 let fallDistance = target.position.y; gsap.to(target.position, { y: 0, duration: fallDistance / fallenSpeed, }); return; } // 遇到楼梯,上楼检测 if (name.includes("stair")) { let diffHeigh = diffY - targetY; // 超过2个楼梯高度不进行上楼检测,阐明在楼梯下走路 if (diffHeigh >= oneStairHeight * 2) { return; } // 射线命中的地位比target的以后Y轴地位高,能够上楼 if (diffY > targetY) { // target.position.y = diffY; // 动画过渡一下 gsap.to(target.position, { y: diffY, ease: "linear", duration: 0.1, //这个提早如果太久,会导致target.position.y更新不及时而无奈判断为上楼梯 }); console.log("上楼梯⬆️"); } else if (diffY < targetY) { target.position.y -= 0.1; console.log("下楼梯⬇️"); } } }}
一些优化
- 检测在楼梯下静止:
const oneStairHeight = 0.4; //一个楼梯高度if (name.includes("stair")) { let diffHeigh = diffY - targetY; // 超过2个楼梯高度不进行上楼检测,阐明在楼梯下走路 if (diffHeigh >= oneStairHeight * 2) { return; } // ...高低楼梯}
- 退出gsap缓动动画,让镜头平滑些。