安装cannon-es
yarn add cannon-es
import * as CANNON from 'cannon-es'
创建物理世界
const world = CANNON.world({
gravity: new CANNON.Vec3(0, -9.8, 0)
})
创建地面和小球的碰撞箱,并添加到物理世界运行,最后在渲染
const shpereWorld = new CANNON.Sphere(1)
const shpereBody = new CANNON.Body({
shape: shpereWorld, // 形状
mass: 2, // 质量
// 材质,多个物体只有都设置默认弹力和摩擦力,才能生效
material: new CANNON.Material({
// 默认弹性
restitution: 0.8,
// 默认摩擦力
friction: 1.0
}),
// 初始位置
position: new CANNON.Vec3(0, 10, 0)
})
world.addBody(shpereBody)
const planeWorld = new CANNON.Plane()
const planeBody = new CANNON.Body({
mass: 0,
material: new CANNON.Material({
restitution: 0.6, // 默认弹性
friction: 1.0 // 默认摩擦力
}),
position: new CANNON.Vec3(0, 0, 0),
shape: planeWorld,
})
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
world.addBody(planeBody)
const clock = new THREE.Clock()
function animate() {
requestAnimationFrame(animate);
const lastTime = clock.getDelta()
controls.update()
world.step(1/60, lastTime)
shpereMesh.position.copy(shpereBody.position)
renderer.render(scene, camera);
}
animate()
当然,我们也可以封装一下,每次点击创建一个,并使他们从随机位置掉落,这样就能看到碰撞效果
function createMesh() {
const sphere = new THREE.SphereGeometry(1, 30, 30)
const sphereMaterial = new THREE.MeshStandardMaterial({
color: new THREE.Color('#aacc00'),
})
sphereMaterial.wireframe = true
const shpereMesh = new THREE.Mesh(sphere, sphereMaterial)
shpereMesh.position.set(0,10,0)
scene.add(shpereMesh)
return shpereMesh
}
function createBody () {
const x = Math.random() * 5
const y = Math.random() * 5
const shpereWorld = new CANNON.Sphere(1)
const shpereBody = new CANNON.Body({
shape: shpereWorld,
mass: 2,
material: new CANNON.Material({
restitution: 0.8,
friction: 3.0
}),
position: new CANNON.Vec3(x, 10, y)
})
shpereBody.addEventListener('collide', (e) => {
const level = e.contact.getImpactVelocityAlongNormal()
if(level > 5) {
sound.currentTime = 0
sound.volume = Math.floor(level) / 20
setTimeout(() => {
sound.play()
},0)
}
})
world.addBody(shpereBody)
return shpereBody
}
const entities = []
function pushShpere() {
entities.push({
mesh: createMesh(),
body: createBody()
})
}
window.addEventListener('click', pushShpere)
function animate() {
requestAnimationFrame(animate);
const lastTime = clock.getDelta()
controls.update()
world.step(1/60, lastTime)
entities.forEach(entity => {
entity.mesh.position.copy(entity.body.position)
entity.mesh.quaternion.copy(entity.body.quaternion)
})
renderer.render(scene, camera);
}
animate()
在创建body函数中给物体施加力,不要随机垂直掉落
function createBody () {
const x = Math.random() * 5
const z = Math.random() * 5
const shpereWorld = new CANNON.Sphere(1)
const shpereBody = new CANNON.Body({
shape: shpereWorld,
mass: 2,
material: new CANNON.Material({
restitution: 0.8,
friction: 3.0
}),
position: new CANNON.Vec3(0, 10, 0)
})
shpereBody.addEventListener('collide', (e) => {
const level = e.contact.getImpactVelocityAlongNormal()
if(level > 5) {
sound.currentTime = 0
sound.volume = Math.floor(level) / 20
setTimeout(() => {
sound.play()
},0)
}
})
world.addBody(shpereBody)
shpereBody.applyLocalForce(
new CANNON.Vec3(x * 10, 0, z * 10),
new CANNON.Vec3(0.5, 0.5, 0.5),
)
return shpereBody
}