初面网初面网

物理系统

安装cannon依赖

npm install --save cannon-es

使用逻辑:

1、导入

import * as CANNON from 'cannon-es'

可以取符合命名规则的,自己喜欢的名字

2、创建物理世界,创建物体,添加到世界

// 创建物理世界
const world = new CANNON.World();
world.gravity.set(0, -9.8, 0);

// 设置物体材质
const sphereWorldMaterial = new CANNON.Material();

// 创建物理小球
const sphereShape = new CANNON.Sphere(1);
const sphereBody = new CANNON.Body({
  shape: sphereShape,
  position: new CANNON.Vec3(0,5,0),
  mass: 1,
  material: sphereWorldMaterial,
});

// 将物体添加到物理世界
world.addBody(sphereBody);

// 渲染里渲染物理世界
const clock = new THREE.Clock();
function animate() {
  controls.update();

  // 下面这两行
  world.step(1/120, clock.getDelta());
  sphere.position.copy(sphereBody.position);

  requestAnimationFrame( animate );

  renderer.render( scene, camera );
}

3、代码示例:

import * as CANNON from 'cannon-es';
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

// 1、创建场景
scene = new THREE.Scene();

// 2、创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);

// 设置相机位置
camera.position.set(0, 0, 10);
scene.add(camera);

// 创建球和平面
const sphereGeometry = new THREE.SphereGeometry(1, 20, 20);
const sphereMaterial = new THREE.MeshStandardMaterial();
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.castShadow = true;

scene.add(sphere);

const floor = new THREE.Mesh(
  new THREE.PlaneGeometry(10, 10),
  new THREE.MeshStandardMaterial()
);

floor.position.set(0, -1, 0);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;

scene.add(floor);

// 创建物理世界
const world = new CANNON.World();
world.gravity.set(0, -9.8, 0);

// 设置物体材质
const sphereWorldMaterial = new CANNON.Material();

// 创建物理小球
const sphereShape = new CANNON.Sphere(1);
const sphereBody = new CANNON.Body({
  shape: sphereShape,
  position: new CANNON.Vec3(0,5,0),
  mass: 1,
  material: sphereWorldMaterial,
});

// 创建物理地面
const floorShape = new CANNON.Plane();
const floorBody = new CANNON.Body({
  shape: floorShape,
  position: new CANNON.Vec3(0, -1, 0),
  mass: 0,
});

floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI/2);

// 将物体添加到物理世界
world.addBody(sphereBody);
world.addBody(floorBody);

// 添加环境光、平行光
const ambientLight = new THREE.AmbientLight(0xffffff, 1.0);
const dirLight = new THREE.DirectionalLight(0xffffff, 1.0);
dirLight.castShadow = true;

scene.add(ambientLight);
scene.add(dirLight);

// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// console.log(renderer);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);

// 使用渲染器,通过相机将场景渲染进来
renderer.render(scene, camera);
const clock = new THREE.Clock();

const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

function animate() {
  controls.update();

  world.step(1/120, clock.getDelta());

  sphere.position.copy(sphereBody.position);

  requestAnimationFrame( animate );

  renderer.render( scene, camera );
}

animate();

window.addEventListener("resize", () => {
  // 更新宽高比
  camera.aspect = window.innerWidth / window.innerHeight;

  // 更新摄像机的投影矩阵
  camera.updateProjectionMatrix();

  // 更新渲染器
  renderer.setSize(window.innerWidth, window.innerHeight);

  // 设置渲染器的像素比
  renderer.setPixelRatio(window.devicePixelRatio);
});

window.addEventListener("dblclick", () => {
  const isFull = document.fullscreenElement;
  if(!isFull){
    renderer.domElement.requestFullscreen();
    isFull = true;
  }
  document.exitFullscreen();
});

监听碰撞

function HitEvent(e){
  console.log(e);
}
sphereBody.addEventListener("collide", HitEvent);
  • 获取碰撞强度

getImpactVelocityAlongNormal()

function HitEvent(e){
  const impactStrength = e.contact.getImpactVelocityAlongNormal();
  console.log(impactStrength);
}

设置物理材质

const sphereWorldMaterial = new CANNON.Material("default");
const defaultContactMaterial = new CANNON.ContactMaterial(
  sphereMaterial,
  {
    friction: 0.1,  // 摩擦力
    restitution: 0.7  // 弹性
  }
)

// 将材料的关联设置添加到物理世界
world.addContactMaterial(defaultContactMaterial);

添加互相间的碰撞

施加力

applyLocalForce()

cubeBody.applyLocalForce(
  new CANNON.Vec3(18, 0, 0),    // 力的大小和方向
  new CANNON.Vec3(0, 0, 0)      // 施加的力的位置
);

更新于 2025/2/26