说明
这里主要实现物理世界中的自由落体和物体间的碰撞
学习本篇文章前,请事先了解或者学习一下three的内容,方便理解,这里不会教大家创建一个基础的three3d场景,直接就是进行物理世界的搭建
下载依赖包
npm i cannon-es
引入依赖包
import * as CANNON from "cannon-es";
搭建物理世界
1.实例化一个物理世界,并且设置重力加速度
// CANNON.World创建物理世界对象
const world = new CANNON.World();
// 设置物理世界的重力加速度
world.gravity.set(0, -9.8, 0)
2.创建碰撞体的形状和位置
说明:这里为什么说的是形状,因为cannon本身并不会真正去渲染出一个物体,而是利用three创建物体,并进行渲染,但是此时创建的物体是没有任何物理效果的,所以就必须将物理世界中的碰撞体与three中的物体关联起来,变成一个整体,从而实现物理效果,就是在three的物体表面附着一层碰撞形状,从而实现碰撞的检测
比如创建一个球的形状
const bodyShape = new CANNON.Sphere(0.3);
// 设置碰撞体规则
const body = new CANNON.Body({
mass: 1, // 质量,如果为0表示静止不动
position: new CANNON.Vec3(0, 2, 0), //位置
shape: bodyShape,//碰撞体的几何体形状
});
3.将物体添加到物理世界中
world.addBody(body);
4.在动画函数中更新物理世界
// 更新物理世界
world.step(1 / 60)
// 渲染循环中,同步物理世界与网格世界
mesh.position.copy(body.position);
完整代码
这里作者增加了一个地面,并且设置了地面与球体间的摩擦系数和弹性系数,这里以vue3框架
<template>
<div id="canvas">
</div>
</template>
<script setup lang="js">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import * as CANNON from "cannon-es";
import { onMounted } from "vue"
onMounted(() => {
const width = document.documentElement.clientWidth
const height = document.documentElement.clientHeight
// 创建3d场景
const scene = new THREE.Scene()
// 环境光
const light = new THREE.AmbientLight("#ffffff", 0.2)
scene.add(light)
// 添加一个点光源
const pointLight = new THREE.PointLight("#ffffff", 100, 100)
pointLight.position.set(5, 5, 5)
scene.add(pointLight)
// CANNON.World创建物理世界对象
const world = new CANNON.World();
// 设置物理世界的重力加速度
world.gravity.set(0, -9.8, 0)
// 创建碰撞体的形状
// 球
const bodyShape = new CANNON.Sphere(0.3);
// 地面
const bodyFloor = new CANNON.Plane()
// 设置碰撞体规则
const body = new CANNON.Body({
mass: 1,
position: new CANNON.Vec3(0, 2, 0),
shape: bodyShape,//碰撞体的几何体形状
});
const body2 = new CANNON.Body({
mass: 0,
position: new CANNON.Vec3(0, 0, 0),
shape: bodyFloor,//碰撞体的几何体形状
});
// 设置刚体的旋转
body2.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
// 创建球体和地面的材质,并设置弹性系数
const ballMaterial = new CANNON.Material("ballMaterial");
const floorMaterial = new CANNON.Material("floorMaterial");
// 为球体和地面的碰撞体设置材质
body.material = ballMaterial; // 球体的碰撞体
body2.material = floorMaterial; // 地面的碰撞体
const contactMaterial = new CANNON.ContactMaterial(
ballMaterial,
floorMaterial,
{
friction: 0.3, // 摩擦系数
restitution: 0.5, // 弹性系数,这里再次强调以保持一致性
}
);
// 添加接触材料对到物理世界
world.addContactMaterial(contactMaterial);
// 在物理世界添加物体
world.addBody(body);
world.addBody(body2);
// 创建相机
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000)
camera.position.z = 7
camera.position.y = 3
camera.position.x = 3
// 创建球
const boxGeometry = new THREE.SphereGeometry(0.3)
const material = new THREE.MeshPhongMaterial({ color: "#e67e22" })
const mesh = new THREE.Mesh(boxGeometry, material)
mesh.position.set(0, 2, 0)
scene.add(mesh)
// 创建地面
const planeGeometry = new THREE.PlaneGeometry(10, 10)
const materialfloor = new THREE.MeshPhongMaterial({ color: "#95a5a6", side: THREE.DoubleSide })
const meshfloor = new THREE.Mesh(planeGeometry, materialfloor)
meshfloor.position.set(0, 0, 0)
meshfloor.rotation.x = -Math.PI / 2
scene.add(meshfloor)
// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true })
// 调整渲染器大小
renderer.setSize(width, height)
// 添加动画
renderer.setAnimationLoop(animate)
document.querySelector("#canvas").appendChild(renderer.domElement)
// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 动画函数
function animate(time) {
// 更新物理世界
world.step(1 / 60)
// 渲染循环中,同步物理世界与网格世界
mesh.position.copy(body.position);
meshfloor.position.copy(body2.position);
controls.update()
renderer.render(scene, camera);
}
})
</script>
<style scoped></style>