如何在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
相关产品推荐
相关产品推荐

