在用matter.js游戏引擎写的plinko抽奖游戏中,如何让小球总是落在指定的位置?

新手上路,请多包涵

抽奖结果是后台提前返回的,前端怎样才能让小球每次都看似很随机地落入到预定的位置(比如图片中的第三个奖品位置)?我能想到的就是在小球下落的过程中不断判断它的碰撞位置,让小球朝着目标运动,但是如何让运动的过程显得自然随意,难倒了我,期待您的答案(最好是代码表明)想要的效果

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

  <body>
    <center>
      <h1>点击黑色区开始游戏</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>
    //--scrip.js
    const Engine = Matter.Engine,
      Render = Matter.Render,
      World = Matter.World,
      Bodies = Matter.Bodies,
      Body = Matter.Body

    // 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: 0.2,
      restitution: 1
    }

    function addD() {
      disk.push(Bodies.circle(event.offsetX, event.offsetY, 20, dOptions))
      World.add(engine.world, [disk[count]])
      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)
  </script>
  <style>
    * {
      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;
    }
  </style>
</html>
阅读 2.9k
2 个回答

那就模拟真实世界,物理引擎、重力、碰撞,然后记录下小球的轨迹,这样可以针对每个结果坐标位,记录下三到五个路径。然后你只需根据路径去复现动画即可 。

我理解通过点然后去做补间动画即可。


可以看一下,我之前的刮卡活动 https://segmentfault.com/a/1190000020623775

就是通过前期记录用户操作,然后根据操作去模拟的。

image.png

这个问题,从我的角度来看,其实不太适合物理引擎来做,因为物理引擎是带有很强的随机性的,哪怕是一个场景内只有一个小球,每次下落的轨迹都是不确定的,或者说确定性物理本身就是个业界难题(比如王者荣耀里面,英雄碰到墙之后的移动轨迹,一定是要确定的,确定性物理引擎也有一些业界解决方案,比如https://vibrantlink.com/

回到问题本身,我觉得可以分成两个阶段,第一个阶段是在跟三角形的碰撞阶段,这其实是个纯逻辑上的行为,跟前面的答主的思路是类似的,你可以自己确定几个关键的反弹点,然后自己去做补间动画。

当掉出三角形区域后,再赋予它物理引擎的能力,因为不同小球之间也还有一定的反弹效果,这部分要做物理效果自己去做就比较麻烦了。

推荐问题
宣传栏