碰撞检测
事情都要从在搭建的场景中来回穿墙开始说起😒(粗略的总结)
碰撞检测 顾名思义就是通过计算物体之间的距离来判断物体是否相交
问题
- 被检测物体结构复杂
- 被检测物体数量庞大
简化物体结构
即通过简单的矩形,圆形,立方体,球来包裹模型
为什么使用简单图形
很方便,很简单
- 矩形的相交,即检查边是否相交
- 圆的相交,即圆心之间的距离和半径对比
包围盒
- AABB 轴对齐包围盒
- OBB 转向包围盒
包围体
- 球
- 圆柱
- 胶囊
简化物体数量
即通过一些数据结构来减少需要参与检测的物体
四叉树(2D)
简单理解就是
- 判断:给定一个区域最大物体数 MAX,将当前区域物体数与 MAX 进行比较
- 分割:若区域内物体数量大于 Max,则通过 x y 轴将区域分为四个象限(区域)
- 递归:对分割出来的区域不断的进行 判断、分割,最终将区域分割完成
这样只需要先判定区域再进行碰撞判断即可,有效的减少了物体数量
八叉树(3D)
三维 ‘四叉树’
等等(待探究)
…
ThreeJS 中的碰撞检测例子
Raycaster (射线)
定义一个原点,朝着一个方向发出射线,然后计算路程中是否有相交面。
ThreeJS Raycaster 例子
// 伪代码 -> 拾取被射到的物体
// 被检测的物体
const targets = [];
const raycaster = new THREE.Raycaster();
// 设置 原点 方向
raycaster.setFromCamera(原点, camera);
// 获取被拾取的物体
const intersects = rayCaster.intersectObjects(targets, false);
if (intersects.length > 0) {
// 一些操作
}
Octree (八叉树)
// 伪代码 -> player 在 sceneGroup 里不撞墙
// 场景
const sceneGroup = {}
const worldOctree = new Octree();
worldOctree.fromGraphNode(sceneGroup);
// 模拟小人
const player = new Capsule(
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(0, 8, 0),
1
);
// 获取相交结果
const result = worldOctree.capsuleIntersect(player);
if (result) {
// 一些操作
}
CannonJs(物理引擎)
建造一个脱离渲染世界的物理世界,设置重力、摩擦力、惯性等物理性质
将物理世界的物体运动坐标映射到 ThreeJS 中
import * as CANNON from 'cannon-es'
// 物理世界
const world = new CANNON.World({ gravity: new CANNON.Vec3(0, -9.8, 0) })
// 预检
world.broadphase = new CANNON.SAPBroadphase(world)
// 物理世界的材质(摩擦力,回弹),区别于渲染材质(map, emssiveMap)
const defaultMaterial = new CANNON.Material('default')
world.addContactMaterial(
new CANNON.ContactMaterial(defaultMaterial, defaultMaterial, {
friction: 0,
restitution: 0.7,
}),
)
const boxBody = new CANNON.Body({
mass: 0,
shape: new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5)),
position: new CANNON.Vec3(2, 0.5, 0),
})
const sphereBody = new CANNON.Body({
shape: new CANNON.Sphere(0.5),
mass: 1,
position: new CANNON.Vec3(0, 3, 0),
material: defaultMaterial,
})
// 给一个 力
sphereBody.applyLocalForce(new CANNON.Vec3(0, 100, 0), new CANNON.Vec3(0, 0, 0))
world.addBody(boxBody)
world.addBody(sphereBody)
function animate() {
window.requestAnimationFrame(animate)
world.fixedStep()
// 伪 ,拿坐标属性
ThreeJSMeshObject.position.copy(boxBody.position)
}
animate()