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

Matter.js调用World.add()时出现events未定义错误排查求助

问题:Matter.js调用World.add()触发"Cannot read properties of undefined (reading 'events')"错误

我用Matter.js结合p5.js开发斯诺克游戏,调用World.add()时触发错误:

Matter.js: Cannot read properties of undefined (reading 'events') in Events.trigger()

以下是原代码:

// matter.js modules 
let Engine = Matter.Engine,
    Render = Matter.Render,
    World = Matter.World,
    Bodies = Matter.Bodies,
    Mouse = Matter.Mouse, 
    MouseConstraint = Matter.MouseConstraint;
    
// Game variables
let engine;
let world;
let renderer;

// Table variables
let table;
let pocketSize = 50;
let tableWidth = 500;
let tableHeight = 250; 

// Ball arrays
let reds = [];
let colours = [];
let pockets = [];

// Cue variables
let cueBall;
let cue;
let cueLength = 100;
let aiming = false; 

// Game state
let consecutiveColours = 0;

function setup() {

  // Canvas
  createCanvas(800, 600);
  
  // Engine
  engine = Engine.create(); 
  
  // Renderer
  renderer = Render.create({
    element: document.body,
    engine: engine,    
    options: {
        width: 800,
        height: 600,
        wireframes: false
    }
  });

  // Table body
  table = Bodies.rectangle(width / 2, height / 2, tableWidth, tableHeight, { 
    isStatic: true 
  });
  World.add(world, table);

  // Table walls
  World.add(world, [
    Bodies.rectangle(width / 2, 50, width, 100, {isStatic: true}),
    Bodies.rectangle(width / 2, height - 50, width, 100, {isStatic: true}),   
    Bodies.rectangle(50, height / 2, 100, height, {isStatic: true}),
    Bodies.rectangle(width - 50, height / 2, 100, height, {isStatic: true})
  ]);
   
  // Pockets
  let pocketOpts = { isStatic: true, isSensor: true };
  pockets.push(Bodies.rectangle(width / 2, 50, pocketSize, pocketSize, pocketOpts));
  // Add other 5 pockets
  
  // Red balls
  for(let i = 0; i < 15; i++) {
    let ball = Bodies.circle(random(tableWidth / 4, tableWidth * 3 / 4), 
                            random(100, tableHeight - 50), 15, 
                            { friction: 0.05, 
                              restitution: 0.5 
                            });
    World.add(world, ball);
    reds.push(ball);
  }
  
  // Coloured balls  
  let colourOptions = { friction: 0.05, restitution: 0.5 }; 
  colours.push(Bodies.circle(tableWidth / 4, tableHeight / 2, 15, colourOptions));
  colours.push(Bodies.circle(tableWidth * 3/4, tableHeight / 2, 15, colourOptions));

  // Cue ball
  cueBall = Bodies.circle(tableWidth / 2, tableHeight - 30, 15, 
                          { friction: 0.05, 
                            restitution: 0.8});
  World.add(world, cueBall);                           

  // Mouse control
  let mouse = Mouse.create(renderer.canvas); 
  let mConstraint = MouseConstraint.create(engine, { mouse: mouse });
  World.add(world, mConstraint);

  // Run renderer
  Render.run(renderer);
 
}

function draw() {

  // Background
  background(0, 128, 0);

  // Table  
  stroke(128);
  strokeWeight(2);  
  fill(100, 50, 0);
  rect(table.position.x - tableWidth / 2, 
        table.position.y - tableHeight / 2, 
        tableWidth, 
        tableHeight);
  
  // Balls
  for(let i = 0; i < reds.length; i++) {
    drawBall(reds[i]);
  }
  
  for(let i = 0; i < colours.length; i++) {
    drawBall(colours[i], 10);  
  }

  // Cue ball  
  drawBall(cueBall);
  
  // Cue 
  if (aiming) {
    drawCue();
  }

  // Collision messages  
  checkCueCollisions();
  
  // Update physics
  Engine.update(engine);

  // Remove pocketed reds
  reds = reds.filter(red => !red.isSensor);
  
}

// Ball function
function drawBall(ball, r = 15) {
 
  push();
  noStroke();
  fill(255); 
  circle(ball.position.x, ball.position.y, r * 2);  
  pop();
   
}

// Cue rendering  
function drawCue() {

  let x = mouseX;
  let y = mouseY; 
  if (!onTable(x, y)) {
    x = cue.position.x; 
    y = cue.position.y;
  }
  
  push();
  stroke(128);
  strokeWeight(10); 
  line(x, y, x - cueLength * cos(cue.angle),  
        y - cueLength * sin(cue.angle)); 
  pop();
  
}

// Cue ball shot
function keyPressed() {
  if (keyCode === ENTER) {
    aiming = true;
    cue = Bodies.rectangle(cueBall.position.x, cueBall.position.y, 100, 10);
    World.add(world, cue);
  }
}

// Collision messages
function checkCueCollisions() {

  // Implement collisions with balls and cushions
  
}

// Helper functions
function onTable(x, y) {

  return x >= 0 && x <= width &&  
         y >= 100 && y <= height - 100; 
  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>

错误原因

代码中声明了let world;但未初始化,调用World.add(world, ...)时传入的是undefined,导致Matter.js内部事件系统尝试访问undefined的events属性,触发报错。

修复方案

  1. 初始化world变量:在创建引擎后,添加一行绑定世界实例:
engine = Engine.create();
world = engine.world; // 新增此行
  1. 修复其他潜在问题:
    • drawCue()中判断cue是否存在,避免未创建时访问属性报错
    • 将彩色球添加到世界中
    • 将口袋添加到世界中
    • 优化击球逻辑,不需要将cue添加到物理世界,直接绘制瞄准线即可

完整修正代码

// matter.js modules 
let Engine = Matter.Engine,
    Render = Matter.Render,
    World = Matter.World,
    Bodies = Matter.Bodies,
    Mouse = Matter.Mouse, 
    MouseConstraint = Matter.MouseConstraint;
    
// Game variables
let engine;
let world;
let renderer;

// Table variables
let table;
let pocketSize = 50;
let tableWidth = 500;
let tableHeight = 250; 

// Ball arrays
let reds = [];
let colours = [];
let pockets = [];

// Cue variables
let cueBall;
let cueLength = 100;
let aiming = false; 

// Game state
let consecutiveColours = 0;

function setup() {

  // Canvas
  createCanvas(800, 600);
  
  // Engine
  engine = Engine.create(); 
  world = engine.world; // 初始化world变量
  
  // Renderer
  renderer = Render.create({
    element: document.body,
    engine: engine,    
    options: {
        width: 800,
        height: 600,
        wireframes: false
    }
  });

  // Table body
  table = Bodies.rectangle(width / 2, height / 2, tableWidth, tableHeight, { 
    isStatic: true 
  });
  World.add(world, table);

  // Table walls
  World.add(world, [
    Bodies.rectangle(width / 2, 50, width, 100, {isStatic: true}),
    Bodies.rectangle(width / 2, height - 50, width, 100, {isStatic: true}),   
    Bodies.rectangle(50, height / 2, 100, height, {isStatic: true}),
    Bodies.rectangle(width - 50, height / 2, 100, height, {isStatic: true})
  ]);
   
  // Pockets
  let pocketOpts = { isStatic: true, isSensor: true };
  pockets.push(Bodies.rectangle(width / 2, 50, pocketSize, pocketSize, pocketOpts));
  pockets.push(Bodies.rectangle(50, 50, pocketSize, pocketSize, pocketOpts));
  pockets.push(Bodies.rectangle(width - 50, 50, pocketSize, pocketSize, pocketOpts));
  pockets.push(Bodies.rectangle(width / 2, height - 50, pocketSize, pocketSize, pocketOpts));
  pockets.push(Bodies.rectangle(50, height - 50, pocketSize, pocketSize, pocketOpts));
  pockets.push(Bodies.rectangle(width - 50, height - 50, pocketSize, pocketSize, pocketOpts));
  World.add(world, pockets); // 将口袋添加到世界
  
  // Red balls
  for(let i = 0; i < 15; i++) {
    let ball = Bodies.circle(random(tableWidth / 4, tableWidth * 3 / 4), 
                            random(100, tableHeight - 50), 15, 
                            { friction: 0.05, 
                              restitution: 0.5 
                            });
    World.add(world, ball);
    reds.push(ball);
  }
  
  // Coloured balls  
  let colourOptions = { friction: 0.05, restitution: 0.5 }; 
  colours.push(Bodies.circle(tableWidth / 4, tableHeight / 2, 15, colourOptions));
  colours.push(Bodies.circle(tableWidth * 3/4, tableHeight / 2, 15, colourOptions));
  // 将彩色球添加到世界
  colours.forEach(ball => World.add(world, ball));

  // Cue ball
  cueBall = Bodies.circle(tableWidth / 2, tableHeight - 30, 15, 
                          { friction: 0.05, 
                            restitution: 0.8});
  World.add(world, cueBall);                           

  // Mouse control
  let mouse = Mouse.create(renderer.canvas); 
  let mConstraint = MouseConstraint.create(engine, { mouse: mouse });
  World.add(world, mConstraint);

  // Run renderer
  Render.run(renderer);
 
}

function draw() {

  // Background
  background(0, 128, 0);

  // Table  
  stroke(128);
  strokeWeight(2);  
  fill(100, 50, 0);
  rect(table.position.x - tableWidth / 2, 
        table.position.y - tableHeight / 2, 
        tableWidth, 
        tableHeight);
  
  // Balls
  for(let i = 0; i < reds.length; i++) {
    drawBall(reds[i]);
  }
  
  for(let i = 0; i < colours.length; i++) {
    drawBall(colours[i], 10);  
  }

  // Cue ball  
  drawBall(cueBall);
  
  // Cue 
  if (aiming) {
    drawCue();
  }

  // Collision messages  
  checkCueCollisions();
  
  // Update physics
  Engine.update(engine);

  // Remove pocketed reds
  reds = reds.filter(red => !red.isSensor);
  
}

// Ball function
function drawBall(ball, r = 15) {
 
  push();
  noStroke();
  fill(255); 
  circle(ball.position.x, ball.position.y, r * 2);  
  pop();
   
}

// Cue rendering  
function drawCue() {

  let x = mouseX;
  let y = mouseY; 
  if (!onTable(x, y)) {
    x = cueBall.position.x; 
    y = cueBall.position.y;
  }
  
  // 计算瞄准角度
  let angle = atan2(y - cueBall.position.y, x - cueBall.position.x);
  
  push();
  stroke(128);
  strokeWeight(10); 
  line(cueBall.position.x, cueBall.position.y, 
       cueBall.position.x + cueLength * cos(angle),  
       cueBall.position.y + cueLength * sin(angle)); 
  pop();
  
}

// Cue ball shot
function keyPressed() {
  if (keyCode === ENTER) {
    aiming = !aiming; // 切换瞄准状态
    if (!aiming) {
      // 计算击球力度
      let angle = atan2(mouseY - cueBall.position.y, mouseX - cueBall.position.x);
      let force = 0.05;
      Matter.Body.applyForce(cueBall, cueBall.position, {
        x: force * cos(angle),
        y: force * sin(angle)
      });
    }
  }
}

// Collision messages
function checkCueCollisions() {

  // Implement collisions with balls and cushions
  
}

// Helper functions
function onTable(x, y) {

  return x >= 0 && x <= width &&  
         y >= 100 && y <= height - 100; 
  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 20:47:32