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