Babylon.js 物理引擎 #
物理引擎概述 #
物理引擎可以模拟真实世界的物理效果,如重力、碰撞、弹跳等。Babylon.js 支持多种物理引擎插件。
text
┌─────────────────────────────────────────────────────────────┐
│ Babylon.js 物理引擎 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Havok Physics ───── 官方推荐 │
│ - 高性能 │
│ - 功能完整 │
│ - 官方支持 │
│ │
│ Ammo.js ─────────── 开源选择 │
│ - Bullet Physics 移植 │
│ - 功能丰富 │
│ - 免费使用 │
│ │
│ Cannon.js ───────── 轻量级 │
│ - 简单易用 │
│ - 适合简单场景 │
│ - 性能适中 │
│ │
│ Oimo.js ─────────── 快速 │
│ - 性能优先 │
│ - 功能基础 │
│ │
└─────────────────────────────────────────────────────────────┘
启用物理引擎 #
使用 Havok #
javascript
// 加载 Havok
import HavokPhysics from '@babylonjs/havok';
// 初始化 Havok
const havokInstance = await HavokPhysics();
// 启用物理
const physicsPlugin = new BABYLON.HavokPlugin(true, havokInstance);
scene.enablePhysics(new BABYLON.Vector3(0, -9.81, 0), physicsPlugin);
使用 Ammo.js #
javascript
// 加载 Ammo.js
import Ammo from 'ammo.js';
// 初始化
await Ammo();
// 启用物理
scene.enablePhysics(
new BABYLON.Vector3(0, -9.81, 0),
new BABYLON.AmmoJSPlugin(true, Ammo)
);
使用 Cannon.js #
javascript
// 加载 Cannon.js
import * as CANNON from 'cannon';
// 启用物理
scene.enablePhysics(
new BABYLON.Vector3(0, -9.81, 0),
new BABYLON.CannonJSPlugin(true, 10, CANNON)
);
物理代理 #
创建物理代理 #
javascript
// 创建物理代理
mesh.physicsImpostor = new BABYLON.PhysicsImpostor(
mesh,
BABYLON.PhysicsImpostor.BoxImpostor, // 代理类型
{
mass: 1, // 质量
restitution: 0.9, // 弹性系数
friction: 0.5 // 摩擦系数
},
scene
);
代理类型 #
text
┌─────────────────────────────────────────────────────────────┐
│ 物理代理类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ BoxImpostor ─────── 盒子碰撞体 │
│ - 适合立方体物体 │
│ - 性能最好 │
│ │
│ SphereImpostor ──── 球体碰撞体 │
│ - 适合球体物体 │
│ - 滚动效果好 │
│ │
│ CylinderImpostor ── 圆柱碰撞体 │
│ - 适合圆柱物体 │
│ │
│ MeshImpostor ────── 网格碰撞体 │
│ - 使用实际网格形状 │
│ - 性能开销大 │
│ │
│ ConvexHullImpostor 凸包碰撞体 │
│ - 使用凸包形状 │
│ - 比网格性能好 │
│ │
│ HeightmapImpostor 高度图碰撞体 │
│ - 适合地形 │
│ │
│ NoImpostor ──────── 无碰撞体 │
│ - 只作为静态障碍 │
│ │
└─────────────────────────────────────────────────────────────┘
物理属性 #
javascript
mesh.physicsImpostor = new BABYLON.PhysicsImpostor(
mesh,
BABYLON.PhysicsImpostor.BoxImpostor,
{
mass: 1, // 质量(0 = 静态物体)
restitution: 0.5, // 弹性(0-1)
friction: 0.5, // 摩擦系数
damping: 0.1, // 阻尼
angularDamping: 0.1, // 角度阻尼
linearDamping: 0.1 // 线性阻尼
},
scene
);
静态物体 #
javascript
// 创建静态地面
ground.physicsImpostor = new BABYLON.PhysicsImpostor(
ground,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 0 }, // 质量 0 = 静态
scene
);
// 静态墙壁
wall.physicsImpostor = new BABYLON.PhysicsImpostor(
wall,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 0 },
scene
);
物理操作 #
施加力 #
javascript
// 施加力
mesh.physicsImpostor.applyForce(
new BABYLON.Vector3(0, 100, 0), // 力向量
mesh.getAbsolutePosition() // 施力点
);
// 施加冲量
mesh.physicsImpostor.applyImpulse(
new BABYLON.Vector3(10, 0, 0),
mesh.getAbsolutePosition()
);
施加扭矩 #
javascript
// 施加扭矩
mesh.physicsImpostor.applyTorque(
new BABYLON.Vector3(0, 10, 0)
);
// 施加角冲量
mesh.physicsImpostor.applyAngularImpulse(
new BABYLON.Vector3(0, 5, 0)
);
设置速度 #
javascript
// 设置线性速度
mesh.physicsImpostor.setLinearVelocity(
new BABYLON.Vector3(0, 10, 0)
);
// 设置角速度
mesh.physicsImpostor.setAngularVelocity(
new BABYLON.Vector3(0, Math.PI, 0)
);
// 获取速度
const linearVelocity = mesh.physicsImpostor.getLinearVelocity();
const angularVelocity = mesh.physicsImpostor.getAngularVelocity();
设置位置和旋转 #
javascript
// 设置位置
mesh.physicsImpostor.setAngularVelocity(BABYLON.Vector3.Zero());
mesh.physicsImpostor.setLinearVelocity(BABYLON.Vector3.Zero());
mesh.position = new BABYLON.Vector3(0, 5, 0);
mesh.physicsImpostor.wakeUp();
// 设置旋转
mesh.physicsImpostor.setAngularVelocity(BABYLON.Vector3.Zero());
mesh.rotation = new BABYLON.Vector3(0, Math.PI / 2, 0);
mesh.physicsImpostor.wakeUp();
碰撞事件 #
碰撞回调 #
javascript
// 注册碰撞回调
mesh.physicsImpostor.registerOnPhysicsCollide(
otherMesh.physicsImpostor,
function(main, collided) {
console.log('碰撞发生');
console.log('主物体:', main.object.name);
console.log('碰撞物体:', collided.object.name);
}
);
碰撞过滤 #
javascript
// 设置碰撞组
mesh.physicsImpostor.physicsBody.collisionFilterGroup = 1;
mesh.physicsImpostor.physicsBody.collisionFilterMask = 1;
// 只与特定组碰撞
player.physicsImpostor.physicsBody.collisionFilterGroup = 1;
player.physicsImpostor.physicsBody.collisionFilterMask = 1 | 2;
enemy.physicsImpostor.physicsBody.collisionFilterGroup = 2;
enemy.physicsImpostor.physicsBody.collisionFilterMask = 1;
物理约束 #
点对点约束 #
javascript
// 创建点对点约束
const constraint = new BABYLON.PointToPointConstraint(
mesh1.physicsImpostor,
new BABYLON.Vector3(0, 1, 0), // mesh1 上的锚点
mesh2.physicsImpostor,
new BABYLON.Vector3(0, -1, 0) // mesh2 上的锚点
);
// 添加到场景
scene.addConstraint(constraint);
铰链约束 #
javascript
// 创建铰链约束(门)
const hinge = new BABYLON.HingeConstraint(
door.physicsImpostor,
frame.physicsImpostor,
{
mainPivot: new BABYLON.Vector3(-1, 0, 0),
connectedPivot: new BABYLON.Vector3(0, 0, 0),
mainAxis: new BABYLON.Vector3(0, 1, 0),
connectedAxis: new BABYLON.Vector3(0, 1, 0)
}
);
scene.addConstraint(hinge);
滑动约束 #
javascript
// 创建滑动约束
const slider = new BABYLON.SliderConstraint(
mesh1.physicsImpostor,
mesh2.physicsImpostor,
{
mainPivot: new BABYLON.Vector3(0, 0, 0),
connectedPivot: new BABYLON.Vector3(0, 0, 0),
mainAxis: new BABYLON.Vector3(0, 1, 0),
connectedAxis: new BABYLON.Vector3(0, 1, 0)
}
);
scene.addConstraint(slider);
物理射线检测 #
射线投射 #
javascript
// 物理射线检测
const ray = new BABYLON.Ray(
new BABYLON.Vector3(0, 10, 0),
new BABYLON.Vector3(0, -1, 0),
20
);
const hit = scene.pickWithRay(ray, (mesh) => {
return mesh.physicsImpostor !== null;
});
if (hit.hit) {
console.log('命中:', hit.pickedMesh.name);
console.log('距离:', hit.distance);
}
物理调试 #
显示调试 #
javascript
// 显示物理调试
scene.getPhysicsEngine().setDebugMode(true);
// 隐藏调试
scene.getPhysicsEngine().setDebugMode(false);
物理引擎信息 #
javascript
// 获取物理引擎
const physicsEngine = scene.getPhysicsEngine();
// 获取物理时间步长
const timeStep = physicsEngine.getTimeStep();
// 设置时间步长
physicsEngine.setTimeStep(1 / 60);
性能优化 #
使用简单碰撞体 #
javascript
// 使用简单形状代替复杂网格
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 2 }, scene);
sphere.physicsImpostor = new BABYLON.PhysicsImpostor(
sphere,
BABYLON.PhysicsImpostor.SphereImpostor, // 使用球体代理
{ mass: 1 },
scene
);
睡眠设置 #
javascript
// 设置睡眠阈值
mesh.physicsImpostor.sleepThreshold = 0.1;
// 手动睡眠
mesh.physicsImpostor.sleep();
// 唤醒
mesh.physicsImpostor.wakeUp();
禁用物理 #
javascript
// 暂停物理模拟
scene.getPhysicsEngine().setEnabled(false);
// 恢复物理模拟
scene.getPhysicsEngine().setEnabled(true);
实战:物理场景 #
javascript
class PhysicsScene {
constructor(scene) {
this.scene = scene;
this.init();
}
async init() {
await this.setupPhysics();
this.createGround();
this.createWalls();
this.createObjects();
this.setupInteraction();
}
async setupPhysics() {
// 使用 Havok
const havokInstance = await HavokPhysics();
const physicsPlugin = new BABYLON.HavokPlugin(true, havokInstance);
this.scene.enablePhysics(
new BABYLON.Vector3(0, -9.81, 0),
physicsPlugin
);
}
createGround() {
this.ground = BABYLON.MeshBuilder.CreateGround(
'ground',
{ width: 20, height: 20 },
this.scene
);
const groundMat = new BABYLON.StandardMaterial('groundMat', this.scene);
groundMat.diffuseColor = new BABYLON.Color3(0.3, 0.3, 0.35);
this.ground.material = groundMat;
this.ground.physicsImpostor = new BABYLON.PhysicsImpostor(
this.ground,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 0, restitution: 0.9 },
this.scene
);
}
createWalls() {
const wallHeight = 5;
const wallThickness = 0.5;
const groundSize = 20;
const wallMat = new BABYLON.StandardMaterial('wallMat', this.scene);
wallMat.diffuseColor = new BABYLON.Color3(0.4, 0.4, 0.45);
// 四面墙
const walls = [
{ pos: [0, wallHeight / 2, -groundSize / 2], size: [groundSize, wallHeight, wallThickness] },
{ pos: [0, wallHeight / 2, groundSize / 2], size: [groundSize, wallHeight, wallThickness] },
{ pos: [-groundSize / 2, wallHeight / 2, 0], size: [wallThickness, wallHeight, groundSize] },
{ pos: [groundSize / 2, wallHeight / 2, 0], size: [wallThickness, wallHeight, groundSize] }
];
walls.forEach((w, i) => {
const wall = BABYLON.MeshBuilder.CreateBox(`wall${i}`, {
width: w.size[0],
height: w.size[1],
depth: w.size[2]
}, this.scene);
wall.position = new BABYLON.Vector3(...w.pos);
wall.material = wallMat;
wall.physicsImpostor = new BABYLON.PhysicsImpostor(
wall,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 0 },
this.scene
);
});
}
createObjects() {
// 创建多个球体
for (let i = 0; i < 20; i++) {
const sphere = BABYLON.MeshBuilder.CreateSphere(`sphere${i}`, {
diameter: 0.5 + Math.random() * 0.5
}, this.scene);
sphere.position = new BABYLON.Vector3(
(Math.random() - 0.5) * 10,
5 + Math.random() * 5,
(Math.random() - 0.5) * 10
);
const mat = new BABYLON.StandardMaterial(`mat${i}`, this.scene);
mat.diffuseColor = new BABYLON.Color3(
Math.random(),
Math.random(),
Math.random()
);
sphere.material = mat;
sphere.physicsImpostor = new BABYLON.PhysicsImpostor(
sphere,
BABYLON.PhysicsImpostor.SphereImpostor,
{
mass: 1,
restitution: 0.8,
friction: 0.5
},
this.scene
);
}
// 创建多个盒子
for (let i = 0; i < 10; i++) {
const box = BABYLON.MeshBuilder.CreateBox(`box${i}`, {
width: 0.5 + Math.random() * 0.5,
height: 0.5 + Math.random() * 0.5,
depth: 0.5 + Math.random() * 0.5
}, this.scene);
box.position = new BABYLON.Vector3(
(Math.random() - 0.5) * 10,
5 + Math.random() * 5,
(Math.random() - 0.5) * 10
);
const mat = new BABYLON.StandardMaterial(`boxMat${i}`, this.scene);
mat.diffuseColor = new BABYLON.Color3(
Math.random(),
Math.random(),
Math.random()
);
box.material = mat;
box.physicsImpostor = new BABYLON.PhysicsImpostor(
box,
BABYLON.PhysicsImpostor.BoxImpostor,
{
mass: 1,
restitution: 0.5,
friction: 0.5
},
this.scene
);
}
}
setupInteraction() {
// 点击施加力
this.scene.onPointerDown = (evt, pickResult) => {
if (pickResult.hit && pickResult.pickedMesh.physicsImpostor) {
const direction = pickResult.pickedMesh.position.subtract(
pickResult.pickedMesh.position.clone().add(new BABYLON.Vector3(0, 1, 0))
);
pickResult.pickedMesh.physicsImpostor.applyImpulse(
direction.scale(10),
pickResult.pickedPoint
);
}
};
}
reset() {
// 重置所有物体
this.scene.meshes.forEach(mesh => {
if (mesh.physicsImpostor && mesh.physicsImpostor.mass > 0) {
mesh.physicsImpostor.setLinearVelocity(BABYLON.Vector3.Zero());
mesh.physicsImpostor.setAngularVelocity(BABYLON.Vector3.Zero());
mesh.position = new BABYLON.Vector3(
(Math.random() - 0.5) * 10,
10,
(Math.random() - 0.5) * 10
);
mesh.rotation = BABYLON.Vector3.Zero();
mesh.physicsImpostor.wakeUp();
}
});
}
}
// 使用
const physicsScene = new PhysicsScene(scene);
// 重置按钮
document.getElementById('reset').onclick = () => physicsScene.reset();
下一步 #
现在你已经掌握了物理引擎,接下来学习 高级主题,了解性能优化、WebGPU 和扩展开发!
最后更新:2026-03-29