You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Pixi.js项目中使用Matter.js?DOM正常但Pixi无显示

如何在Pixi.js项目中渲染Matter.js内容?

问题描述

我在DOM环境中使用Matter.js实现了BallCabin组件,渲染到div元素上运行正常,但迁移到Pixi.js项目后没有任何内容显示。相关说明:

  • window.game.view是Pixi程序的根canvas元素(对应Application.view),原本想将BallCabin渲染到该画布上。
  • 通过Ticker.shared.add(this.update, this)调用update函数。

原代码如下:

export class BallCabin extends Sprite{
  constructor() {
    super();

    this._world = null;
    this._engine = null;
    this._cabin = null;
    this._balls = [];

    this.BALL_RADIUS = 12;

    this.initView();
  }

  initView() {
    this.initScene();
    this.makeCabin();
    this.makeSensors();
    this.makeBalls();
    this.initAction();
  }

  initScene() {
    const element = window.game.view;

    this._engine = Engine.create();
    let runner = Runner.create();
    let render = Render.create({
      background: '#6eb543',
      element: element,
      engine: this._engine,
      options: {
        width: 300,
        height: 300,
        background: 'transparent',
        wireframes: false,
        showIds: false
      }
    });
    this._world = this._engine.world;

    Render.run(render);
    Runner.run(runner, this._engine);
  }

  initAction() {
    Events.on(this._engine, 'collisionStart', (evt) => this.onCollision(evt));
    Ticker.shared.add(this.update, this);
  }

  makeCabin() {
    let parts = [];
    let pt = {x: 150, y: 150};

    for (let i = 0; i < 90; i++) {
      let x = pt.x + Math.cos(i * 4 * Math.PI / 180) * 120;
      let y = pt.y + Math.sin(i * 4 * Math.PI / 180) * 120;
      let options = {friction: 0.5, angle: Math.PI / 180 * i * 4, render: {fillStyle: i !== 0 ? '#2E2D34' : '#FFFFFF'}};
      let rec = Bodies.rectangle(x, y, 10, 12, options);
      parts.push(rec);
      Composite.add(this._world, rec);
    }

    this._cabin = Body.create({parts, isStatic: true});
  }

  makeSensors() {
    const pt = {x: 150, y: 150};
    const ary = [];

    for (let i = 20; i < 45; i++) {
      const x = pt.x + Math.cos(i * 4 * Math.PI / 180) * 120;
      const y = pt.y + Math.sin(i * 4 * Math.PI / 180) * 120;
      const render = {strokeStyle: '#C44D58', fillStyle: 'transparent', lineWidth: 1};
      const options = {isSensor: true, isStatic: true, friction: 0, label: 'addSpeedSensor', angle: Math.PI / 180 * i * 4, render: render};

      let rec = Bodies.rectangle(x, y, 30, 30, options);
      ary.push(rec);
      Composite.add(this._world, rec);
    }
  }

  makeBalls() {
    let pt = {x: 150, y: 150};

    for (let i = 0; i < 10; i++) {
      let x = pt.x - 65 + i * 5;
      let y = pt.y - 65 + i * 5;
      let options = {isStatic: false, mass: 1 + i * 2, friction: 0, frictionStatic: 0, label: 'ball', render: {opacity: 0.6,}, collisionFilter: {category: 0x0002, mask: 0x0002 | 0x0001}};

      const ball = Bodies.circle(x, y, this.BALL_RADIUS, options);
      this._balls.push(ball);
    }

    for (let i = 0; i < 15; i++) {
      let x = pt.x - 65 + i * 5;
      let y = pt.y - 65 + i * 5;
      let options = {isStatic: false, mass: 1 + i * 2, friction: 0, frictionStatic: 0, label: 'ball', render: {opacity: 1,}, collisionFilter: {category: 0x0004, mask: 0x0004 | 0x0001}};

      const ball = Bodies.circle(x, y, this.BALL_RADIUS, options);
      this._balls.push(ball);
    }
    Composite.add(this._world, this._balls);
  }

  onCollision(evt) {
    let pairs = evt.pairs;
    for (let i = 0, j = pairs.length; i != j; ++i) {
      let pair = pairs[i];
      let {bodyA, bodyB} = pair;

      if (bodyA.label === 'addSpeedSensor') {
        Body.setVelocity(bodyB, {x: -6, y: -5});
        //Body.applyForce(bodyB, bodyB.position, {x: 0.3, y: 0.5})
      } else if (bodyB.label === 'addSpeedSensor') {
        // Body.applyForce(bodyA, {x: bodyA.position.x, y: bodyA.position.y}, {x: -0.1, y: -0.05})
      }
    }
  }

  update(deltaTime) {
    //super.update(delta);
    if (this._cabin) {
      Body.rotate(this._cabin, Math.PI / 120);
    }
  }
}

解决方案

核心问题是你同时启用了Matter.js自带的Render模块和Pixi.js的渲染系统,两者会直接冲突。正确的做法是让Matter.js只负责物理逻辑计算,所有图形渲染交给Pixi.js处理,具体修改步骤如下:

1. 移除Matter.js的渲染相关代码

删除initScene中Render.create和Render.run的调用,只保留物理引擎和Runner的初始化:

initScene() {
  this._engine = Engine.create();
  let runner = Runner.create();
  this._world = this._engine.world;
  // 仅运行物理模拟,不启用Matter自带渲染
  Runner.run(runner, this._engine);
}

2. 为每个Matter物理体创建对应Pixi显示对象

在创建物理体的方法中,同步创建Pixi的Graphics对象(或Sprite),并绑定到物理体上,方便后续同步状态:

修改makeBalls方法

makeBalls() {
  let pt = {x: 150, y: 150};

  // 第一组半透明球
  for (let i = 0; i < 10; i++) {
    let x = pt.x - 65 + i * 5;
    let y = pt.y - 65 + i * 5;
    let options = {isStatic: false, mass: 1 + i * 2, friction: 0, frictionStatic: 0, label: 'ball', collisionFilter: {category: 0x0002, mask: 0x0002 | 0x0001}};

    const ball = Bodies.circle(x, y, this.BALL_RADIUS, options);
    // 创建Pixi圆形图形
    const pixiBall = new PIXI.Graphics();
    pixiBall.beginFill(0xFFFFFF, 0.6);
    pixiBall.drawCircle(0, 0, this.BALL_RADIUS);
    pixiBall.endFill();
    // 同步初始位置
    pixiBall.position.set(x, y);
    // 将Pixi对象绑定到物理体
    ball.pixiObj = pixiBall;
    // 添加到当前容器
    this.addChild(pixiBall);
    this._balls.push(ball);
  }

  // 第二组不透明球
  for (let i = 0; i < 15; i++) {
    let x = pt.x - 65 + i * 5;
    let y = pt.y - 65 + i * 5;
    let options = {isStatic: false, mass: 1 + i * 2, friction: 0, frictionStatic: 0, label: 'ball', collisionFilter: {category: 0x0004, mask: 0x0004 | 0x0001}};

    const ball = Bodies.circle(x, y, this.BALL_RADIUS, options);
    const pixiBall = new PIXI.Graphics();
    pixiBall.beginFill(0xFFFFFF, 1);
    pixiBall.drawCircle(0, 0, this.BALL_RADIUS);
    pixiBall.endFill();
    pixiBall.position.set(x, y);
    ball.pixiObj = pixiBall;
    this.addChild(pixiBall);
    this._balls.push(ball);
  }
  Composite.add(this._world, this._balls);
}

修改makeCabin方法

makeCabin() {
  let parts = [];
  let pt = {x: 150, y: 150};

  for (let i = 0; i < 90; i++) {
    let x = pt.x + Math.cos(i * 4 * Math.PI / 180) * 120;
    let y = pt.y + Math.sin(i * 4 * Math.PI / 180) * 120;
    let fillColor = i !== 0 ? 0x2E2D34 : 0xFFFFFF;
    let options = {friction: 0.5, angle: Math.PI / 180 * i * 4, collisionFilter: {category: 0x0001}};
    let rec = Bodies.rectangle(x, y, 10, 12, options);
    // 创建Pixi矩形图形
    const pixiRec = new PIXI.Graphics();
    pixiRec.beginFill(fillColor);
    // 设置锚点为中心,方便旋转同步
    pixiRec.pivot.set(5, 6);
    pixiRec.drawRect(-5, -6, 10, 12);
    pixiRec.endFill();
    // 同步初始位置和旋转
    pixiRec.position.set(x, y);
    pixiRec.rotation = options.angle;
    rec.pixiObj = pixiRec;
    this.addChild(pixiRec);
    parts.push(rec);
    Composite.add(this._world, rec);
  }

  this._cabin = Body.create({parts, isStatic: true});
}

3. 在update方法中同步物理体与Pixi对象的状态

修改update方法,将Matter计算出的位置、旋转同步到对应的Pixi显示对象上:

update(deltaTime) {
  // 同步 cabin 所有部件的状态
  if (this._cabin) {
    Body.rotate(this._cabin, Math.PI / 120);
    this._cabin.parts.forEach(part => {
      if (part.pixiObj) {
        part.pixiObj.position.set(part.position.x, part.position.y);
        part.pixiObj.rotation = part.angle;
      }
    });
  }
  // 同步所有小球的状态
  this._balls.forEach(ball => {
    if (ball.pixiObj) {
      ball.pixiObj.position.set(ball.position.x, ball.position.y);
      ball.pixiObj.rotation = ball.angle;
    }
  });
}

4. 传感器处理

传感器仅用于碰撞检测,不需要渲染,因此makeSensors方法可以保持原样,无需创建对应的Pixi对象。

额外提示

  • 你的BallCabin继承自Sprite,但它本质是一个容器,建议改为继承PIXI.Container,更符合逻辑。
  • 确保Pixi的Ticker和Matter的Runner同步,避免物理模拟与渲染帧率不一致。

内容的提问来源于stack exchange,提问作者user21462090

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 13:07:11