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

基于matter.js的Plinko游戏:如何让球看似随机落入底部第三格

实现Plinko小球定向落位(看似随机但必入底部第三个空位)

问题描述

基于给定的Plinko游戏代码,需要实现小球下落过程看似随机,但最终必然落入底部第三个空位。核心是平衡自然的随机运动轨迹与确定的落位结果,不能让引导痕迹过于明显。

实现思路

  • 利用Matter.js的碰撞事件监听,在小球每次碰撞障碍物后触发调整逻辑
  • 计算小球当前位置与目标空位的水平偏移,施加与偏移成正比的定向力,引导小球向目标方向移动
  • 加入随机扰动(小幅度随机调整力的方向和大小),让运动轨迹看起来自然无规律
  • 限制最大作用力,避免小球突然变向,保证运动的连贯性和真实感

修改后的完整代码

JavaScript(script.js)

const Engine = Matter.Engine,
  Render = Matter.Render,
  World = Matter.World,
  Bodies = Matter.Bodies,
  Body = Matter.Body,
  Events = Matter.Events; // 新增事件模块

// create an engine
const engine = Engine.create();

// create a renderer
const render = Render.create({
  element: document.getElementById('can'),
  engine: engine
});
// create flanges
const fOptions = {
  isStatic: true,
}
const flanges = [
  //row one
  Bodies.polygon(45, 150, 3, 20, fOptions),
  Bodies.polygon(100, 250, 3, 20, fOptions),
  Bodies.polygon(60, 350, 3, 20, fOptions),
  Bodies.polygon(100, 450, 3, 20, fOptions),
  //row two
  Bodies.polygon(150, 150, 3, 20, fOptions),
  Bodies.polygon(200, 250, 3, 20, fOptions),
  Bodies.polygon(150, 350, 3, 20, fOptions),
  Bodies.polygon(200, 450, 3, 20, fOptions),
  //row three
  Bodies.polygon(250, 150, 3, 20, fOptions),
  Bodies.polygon(300, 250, 3, 20, fOptions),
  Bodies.polygon(250, 350, 3, 20, fOptions),
  Bodies.polygon(300, 450, 3, 20, fOptions),
  //row four
  Bodies.polygon(350, 150, 3, 20, fOptions),
  Bodies.polygon(400, 250, 3, 20, fOptions),
  Bodies.polygon(350, 350, 3, 20, fOptions),
  Bodies.polygon(400, 450, 3, 20, fOptions),
  //row five
  Bodies.polygon(450, 150, 3, 20, fOptions),
  Bodies.polygon(500, 250, 3, 20, fOptions),
  Bodies.polygon(450, 350, 3, 20, fOptions),
  Bodies.polygon(500, 450, 3, 20, fOptions),
  //row six
  Bodies.polygon(550, 150, 3, 20, fOptions),
  Bodies.polygon(600, 250, 3, 20, fOptions),
  Bodies.polygon(550, 350, 3, 20, fOptions),
  Bodies.polygon(600, 450, 3, 20, fOptions),
  //row seven
  Bodies.polygon(650, 150, 3, 20, fOptions),
  Bodies.polygon(700, 250, 3, 20, fOptions),
  Bodies.polygon(650, 350, 3, 20, fOptions),
  Bodies.polygon(700, 450, 3, 20, fOptions),
  //row 8
  Bodies.polygon(755, 150, 3, 20, fOptions),
  Bodies.polygon(740, 350, 3, 20, fOptions),
];
for (i = 0; i < flanges.length; i++) {
  World.add(engine.world, [flanges[i]])
  Body.setAngle(flanges[i], 16.23)
}
//create catches
const cOptions = {
  isStatic: true
}
const catches = [
  Bodies.rectangle(40, 560, 2, 40, cOptions)
];
xasx = 82;
for (v = 0; v < 20; v++) {
  catches.push(Bodies.rectangle(xasx, 560, 2, 40, cOptions))
  World.add(engine.world, [catches[v]])
  xasx = xasx + 45;
}
// create disk
let disk = [];
let count = 0;
const dOptions = {
  friction: .2,
  restitution: 1
}
const TARGET_X = 127; // 底部第三个空位的x坐标

function addD() {
  const newDisk = Bodies.circle(event.offsetX, event.offsetY, 20, dOptions)
  disk.push(newDisk);
  World.add(engine.world, [newDisk]);

  // 监听碰撞事件,引导小球向目标位置移动
  Events.on(newDisk, 'collisionStart', function(event) {
    const ball = event.bodyA === newDisk ? event.bodyA : event.bodyB;
    const diffX = TARGET_X - ball.position.x;

    // 施加定向力+随机扰动,保证运动自然
    const forceX = diffX * 0.1 + (Math.random() - 0.5) * 0.8;
    const forceY = (Math.random() - 0.5) * 0.2;

    // 限制最大力,避免突兀变向
    const maxForce = 2;
    Body.applyForce(ball, ball.position, {
      x: Math.max(-maxForce, Math.min(maxForce, forceX)),
      y: Math.max(-maxForce, Math.min(maxForce, forceY))
    });
  });

  count++;
}
// create ground sky and walls
const ground = Bodies.rectangle(400, 610, 810, 60, {
  isStatic: true
});
const leftWall = Bodies.rectangle(0, 305, 2, 810, {
  isStatic: true
});
const righttWall = Bodies.rectangle(800, 305, 2, 810, {
  isStatic: true
});
const sky = Bodies.rectangle(400, 0, 810, 2, {
  isStatic: true
});
// add all of the bodies to the world
World.add(engine.world, [sky, righttWall, leftWall, ground]);

// run the engine
Engine.run(engine);

// run the renderer
Render.run(render);

CSS(styles.css)

* {
  margin: 0;
  touch-action: manipulation;
}

body {
  background-color: #353535;
}

h1 {
  font-family: 'Luckiest Guy', serif;
  color: white;
  background: -webkit-linear-gradient(pink, blueviolet);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  font-size: 100px;
  margin-bottom: 1%;
}

hr {
  width: 70%;
  margin-bottom: 2%;
  border-radius: 90%;
  background-color: pink;
  color: pink;
}

h4 {
  margin-top: 3%;
  color: darkorange;
}

a {
  color: darkorange;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

HTML(index.html)

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <title>Plinko</title>
  <link href="styles.css" rel="stylesheet" />
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Luckiest+Guy" />
</head>

<body>
  <center>
    <h1>PlinkO</h1>
    <hr />
    <aside onclick="addD()" id="can"></aside>
  </center>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/matter-js/0.19.0/matter.js"></script>
<script src="script.js"></script>

</html>

关键说明

  • 定向引导逻辑:在每次碰撞后,根据小球与目标空位的水平距离施加拉力,距离越远拉力越大,确保小球逐步向目标靠近
  • 随机扰动:在定向力基础上加入小范围随机值,让每次下落的轨迹都有差异,模拟真实Plinko的随机感
  • 力的限制:通过maxForce控制最大作用力,避免小球出现不符合物理规律的突然变向,保证运动自然流畅

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 02:42:15