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

基于Matter.js实现Plinko游戏碰撞Ping波纹效果的技术问询

实现Plinko游戏中销的Ping波纹效果(基于Matter.js)

我正在重构Plinko游戏,想要实现Stake平台同款的Ping波纹效果——就是球碰撞销时,销周围会出现向外扩散的半透明波纹动画。目前已经通过Matter.js实现了球与销的碰撞事件触发,但Matter.js自带的渲染器功能有限,不知道怎么给销添加这个波纹效果。

效果示意图:
Ping波纹效果示意图

现有的碰撞检测代码片段:

// if (bodyB.label.includes("ball") && bodyA.label.includes("pin")) {
//   console.log("Ball hit pin");
//   // Style pin to create a wave effect when hit
//   bodyA.render.fillStyle = "red";

// }

可行解决方案

方案一:叠加独立Canvas绘制波纹

Matter.js的渲染基于Canvas,你可以在游戏画布上层叠加一个独立的Canvas(绝对定位,和游戏画布重合),专门用来绘制波纹动画:

  1. 碰撞触发时,记录被碰撞销的坐标,生成包含半径、透明度、扩散速度的波纹实例
  2. 在游戏的动画循环中,遍历波纹数组更新参数并绘制,过期的波纹直接移除

示例代码:

// 初始化波纹数组与叠加Canvas
const ripples = [];
const rippleCanvas = document.createElement('canvas');
rippleCanvas.width = window.innerWidth;
rippleCanvas.height = window.innerHeight;
rippleCanvas.style.position = 'absolute';
rippleCanvas.style.top = '0';
rippleCanvas.style.left = '0';
rippleCanvas.style.pointerEvents = 'none'; // 不干扰游戏交互
document.body.appendChild(rippleCanvas);
const rippleCtx = rippleCanvas.getContext('2d');

// 碰撞事件中添加波纹
Matter.Events.on(engine, 'collisionStart', function(event) {
  const pairs = event.pairs;
  pairs.forEach(pair => {
    const { bodyA, bodyB } = pair;
    const pinBody = bodyA.label.includes('pin') ? bodyA : bodyB;
    if ((bodyA.label.includes('pin') && bodyB.label.includes('ball')) || 
        (bodyA.label.includes('ball') && bodyB.label.includes('pin'))) {
      ripples.push({
        x: pinBody.position.x,
        y: pinBody.position.y,
        radius: 0,
        opacity: 1,
        maxRadius: 30,
        speed: 2,
        fadeSpeed: 0.02
      });
    }
  });
});

// 动画循环中绘制波纹
Matter.Events.on(engine, 'afterUpdate', function() {
  rippleCtx.clearRect(0, 0, rippleCanvas.width, rippleCanvas.height);
  ripples.forEach((ripple, index) => {
    ripple.radius += ripple.speed;
    ripple.opacity -= ripple.fadeSpeed;
    if (ripple.opacity <= 0 || ripple.radius >= ripple.maxRadius) {
      ripples.splice(index, 1);
      return;
    }
    rippleCtx.beginPath();
    rippleCtx.arc(ripple.x, ripple.y, ripple.radius, 0, Math.PI * 2);
    rippleCtx.strokeStyle = `rgba(255, 255, 255, ${ripple.opacity})`; // 可自定义波纹颜色
    rippleCtx.lineWidth = 2;
    rippleCtx.stroke();
  });
});

方案二:用CSS动画配合DOM元素

如果允许动态生成DOM元素,可以用CSS的缩放和透明度动画实现波纹:

  1. 预定义波纹的CSS动画样式
  2. 碰撞时在销的坐标位置生成DOM元素,触发动画后自动移除

示例代码:

.pin-ripple {
  position: absolute;
  border-radius: 50%;
  border: 2px solid white;
  pointer-events: none;
  animation: ripple 0.6s ease-out forwards;
}

@keyframes ripple {
  from {
    transform: scale(0);
    opacity: 1;
  }
  to {
    transform: scale(2);
    opacity: 0;
  }
}
Matter.Events.on(engine, 'collisionStart', function(event) {
  const pairs = event.pairs;
  pairs.forEach(pair => {
    const { bodyA, bodyB } = pair;
    let pinBody;
    if (bodyA.label.includes('pin') && bodyB.label.includes('ball')) {
      pinBody = bodyA;
    } else if (bodyA.label.includes('ball') && bodyB.label.includes('pin')) {
      pinBody = bodyB;
    }
    if (pinBody) {
      // 转换Matter坐标到页面坐标(根据你的画布定位调整)
      const canvas = document.getElementById('your-game-canvas');
      const rect = canvas.getBoundingClientRect();
      const x = pinBody.position.x + rect.left;
      const y = pinBody.position.y + rect.top;
      
      const ripple = document.createElement('div');
      ripple.className = 'pin-ripple';
      ripple.style.width = '20px';
      ripple.style.height = '20px';
      ripple.style.left = `${x - 10}px`; // 让波纹居中于销
      ripple.style.top = `${y - 10}px`;
      document.body.appendChild(ripple);
      
      // 动画结束后自动移除元素
      ripple.addEventListener('animationend', () => {
        ripple.remove();
      });
    }
  });
});

注意事项

  • Canvas方案要确保两个画布的坐标系统一致,避免波纹偏移
  • CSS方案要注意游戏画布是否有缩放/变换,需正确转换坐标
  • 两种方案都要控制波纹数量,避免过多波纹导致性能问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 02:35:13