Three.js - 物体碰撞检测(二十六)
简介碰撞检测在three.js开发中是很常见的。最常见的两种方式实现:使用.Raycaste()在物体的各个顶点发出射线,计算是否和其他物体相交。使用.Box3在物体上创建包围盒,计算两个物体包围盒是否相交。本节介绍第一种碰撞检测法。Raycasterorigin — 光线投射的原点向量。direction — 射线的方向向量,应该归一化。near — 所有返回的结果应该比 near 远。near
·
简介
- 碰撞检测在
three.js
开发中是很常见的。最常见的两种方式实现:
- 本节介绍第一种碰撞检测法。
Raycaster
origin — 光线投射的原点向量。
direction — 射线的方向向量,应该归一化。
near — 所有返回的结果应该比 near 远。near不能为负,默认值为0。
far — 所有返回的结果应该比 far 近。far 不能小于 near,默认值为无穷大。
Raycaster(origin, direction, near, far)
- 射线检测法缺点也比较明显,当物体的中心在另一个物体内部时,是不能够检测到碰撞的。而且当两个物体能够互相穿过,且有较大部分重合时,检测效果也不理想。
开始实现
基础模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>学习</title>
</head>
<body>
<canvas id="c2d" class="c2d" width="1000" height="500"></canvas>
<script type="module">
import * as THREE from './file/three.js-dev/build/three.module.js'
const canvas = document.querySelector('#c2d')
// 渲染器
const renderer = new THREE.WebGLRenderer({ canvas })
const fov = 40 // 视野范围
const aspect = 2 // 相机默认值 画布的宽高比
const near = 0.1 // 近平面
const far = 10000 // 远平面
// 透视投影相机
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
camera.position.set(0, 6, 5)
camera.lookAt(0, 0, 0)
// 场景
const scene = new THREE.Scene()
{
// 灯光
const color = 0xffffff
const intensity = 1
const light = new THREE.DirectionalLight(color, intensity)
light.position.set(-1, 10, 4)
scene.add(light)
}
// 立方体
const boxWidth = 1
const boxHeight = 1
const boxDepth = 1
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth)
const material = new THREE.MeshPhongMaterial({
color: 0x6688aa
})
const cube = new THREE.Mesh(geometry, material)
cube.position.x = -1
scene.add(cube)
const material2 = new THREE.MeshPhongMaterial({
color: 0x6688aa
})
const cube2 = new THREE.Mesh(geometry, material2)
cube2.position.x = 1
scene.add(cube2)
// 渲染
function render() {
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
</script>
</body>
</html>
添加键盘控制
document.addEventListener(
'keydown',
(e) => {
var ev = e || window.event
switch (ev.keyCode) {
case 87:
cube.position.z -= 0.05
break
case 83:
cube.position.z += 0.05
break
case 65:
cube.position.x -= 0.05
break
case 68:
cube.position.x += 0.05
break
default:
break
}
},
false
)
创建检测方法
- 获取移动物体中心点的世界坐标。
- 获取移动物体的所有顶点坐标。
- 循环所有顶点坐标,根据移动物体的变换矩阵转换顶点坐标。
- 通过
Raycaster()
从中心点的世界坐标向转换后的顶点坐标,发出射线检测要碰撞的物体。 - 比较射线的长度是否小于物体的长度。
注意本节使用R135版本,几何对象都是继承的BufferGeometry是没有顶点数组对象(.vertices)
function onIntersect() {
// 声明一个变量用来表示是否碰撞
let bool = false
// .position 对象局部位置
// .clone() 复制一个新的三维向量
// 网格中心 世界坐标
const centerCoord = cube.position.clone()
// 获取网格中 几何对象的顶点对象
const position = cube.geometry.attributes.position
// 顶点三维向量
const vertices = []
// .count 矢量个数
for (let i = 0; i < position.count; i++) {
// .getX() 获取给定索引的矢量的第一维元素
vertices.push(new THREE.Vector3(position.getX(i), position.getY(i), position.getZ(i)))
}
for (let i = 0; i < vertices.length; i++) {
// .matrixWorld 物体的世界坐标变换 -- 物体旋转、位移 的四维矩阵
// .applyMatrix4() 将该向量乘以四阶矩阵
// 获取世界坐标下 网格变换后的坐标
let vertexWorldCoord = vertices[i].clone().applyMatrix4(cube.matrixWorld)
// .sub(x) 从该向量减去x向量
// 获得由中心指向顶点的向量
var dir = vertexWorldCoord.clone().sub(centerCoord)
// .normalize() 将该向量转换为单位向量
// 发射光线 centerCoord 为投射的原点向量 dir 向射线提供方向的方向向量
let raycaster = new THREE.Raycaster(centerCoord, dir.clone().normalize())
// 放入要检测的 物体cube2,返回相交物体
let intersects = raycaster.intersectObjects([cube2], true)
if (intersects.length > 0) {
// intersects[0].distance:射线起点与交叉点之间的距离(交叉点:射线和模型表面交叉点坐标)
// dir.length():几何体顶点和几何体中心构成向量的长度
// intersects[0].distance小于dir.length() 表示物体相交
if (intersects[0].distance < dir.length()) {
bool = true
}
}
}
return bool
}
- 在键盘监听事件中添加网格颜色修改。
if (onIntersect()) {
cube.material.color.set('yellow')
} else {
cube.material.color.set(0x6688aa)
}
- 这种方式存在很多小问题,在需要物理引擎这一类操作的时候,建议大家还是直接使用第三方插件。如官方使用的ammo.js等。
更多推荐
已为社区贡献2条内容
所有评论(0)