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

如何在碰撞时仅执行一次函数?圆形碰撞检测问题求助

解决碰撞时仅触发一次操作的正确姿势

看起来你之前踩了个很常见的坑——只盯着当前是否碰撞,却没跟踪碰撞状态的变化瞬间。别慌,咱们用「状态对比法」就能完美解决这个问题,核心思路就是记录上一帧的碰撞状态,和当前帧对比,只在状态切换的节点触发操作。

核心逻辑

我们需要两个状态变量来跟踪碰撞的变化:

  • isCollidingCurrent:记录当前帧是否处于碰撞状态
  • isCollidingPrevious:记录上一帧的碰撞状态

每一帧的执行流程是:

  1. 检测当前是否发生碰撞
  2. 对比当前与上一帧的状态:
    • 从「未碰撞」切换到「碰撞」:触发一次播放正弦波的操作
    • 从「碰撞」切换到「未碰撞」:触发停止正弦波的操作
  3. 更新上一帧的状态,为下一帧的判断做准备

代码示例(通用JS风格/伪代码)

// 全局/类级别的状态变量,确保跨帧保留状态
let isCollidingCurrent = false;
let isCollidingPrevious = false;
let activeSineWave = null; // 存储正在播放的正弦波实例

function update() {
    // 第一步:执行你的圆形碰撞检测逻辑
    isCollidingCurrent = checkCircleCollision(circleA, circleB);

    // 第二步:处理状态切换事件
    if (isCollidingCurrent && !isCollidingPrevious) {
        // 碰撞开始:仅执行一次播放操作
        startSineWave();
    } else if (!isCollidingCurrent && isCollidingPrevious) {
        // 碰撞结束:执行停止操作
        stopSineWave();
    }

    // 第三步:更新上一帧状态,必须放在最后!
    isCollidingPrevious = isCollidingCurrent;
}

// 你的圆形碰撞检测实现(用平方对比避免开根号,性能更优)
function checkCircleCollision(circle1, circle2) {
    const dx = circle1.x - circle2.x;
    const dy = circle1.y - circle2.y;
    const distanceSquared = dx ** 2 + dy ** 2;
    const combinedRadius = circle1.radius + circle2.radius;
    return distanceSquared <= combinedRadius ** 2;
}

// 播放正弦波的逻辑,加判断避免重复播放
function startSineWave() {
    if (!activeSineWave || !activeSineWave.isPlaying) {
        activeSineWave = createAndPlaySineWave(); // 替换成你实际的创建/播放逻辑
        console.log("正弦波开始播放");
    }
}

// 停止正弦波的逻辑
function stopSineWave() {
    if (activeSineWave && activeSineWave.isPlaying) {
        activeSineWave.stop();
        activeSineWave = null; // 可选:释放资源
        console.log("正弦波停止播放");
    }
}

为什么你之前的方法失效?

  • 如果只使用单个布尔值(比如hasCollided),第一次碰撞后设为true,后续就无法检测到「新的碰撞开始」信号,导致碰撞检测直接“罢工”;
  • 如果每次碰撞都重置这个布尔值,又会回到每帧触发的循环状态,相当于没加控制;
  • 而「上一帧+当前帧」的状态对比,只在状态切换的瞬间触发操作,完美避开了这两个极端问题。

额外注意事项

  • 如果是多组碰撞对(比如多个圆形和玩家碰撞),需要为每一组单独维护状态变量,比如用Map存储每对的previous和current状态;
  • 确保状态变量是跨帧持久化的,别在update函数内部重新声明,否则会丢失上一帧的状态;
  • 音频播放逻辑里一定要加「是否正在播放」的判断,避免重复创建/播放音频实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:18:11