简介
碰撞检测在three.js
开发中是很常见的。最常见的两种方式实现:
使用.Raycaste()
在物体的各个顶点发出射线,计算是否和其他物体相交。 使用.Box3
在物体上创建包围盒,计算两个物体包围盒是否相交。
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
const centerCoord = cube. position. clone ( )
const position = cube. geometry. attributes. position
const vertices = [ ]
for ( let i = 0 ; i < position. count; i++ ) {
vertices. push ( new THREE. Vector3 ( position. getX ( i) , position. getY ( i) , position. getZ ( i) ) )
}
for ( let i = 0 ; i < vertices. length; i++ ) {
let vertexWorldCoord = vertices[ i] . clone ( ) . applyMatrix4 ( cube. matrixWorld)
var dir = vertexWorldCoord. clone ( ) . sub ( centerCoord)
let raycaster = new THREE. Raycaster ( centerCoord, dir. clone ( ) . normalize ( ) )
let intersects = raycaster. intersectObjects ( [ cube2] , true )
if ( intersects. length > 0 ) {
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 等。