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

如何利用流逝时间编写函数实现节奏游戏音符下落(DOM操作)

实现吉他英雄风格节奏游戏的音符触发逻辑

核心思路

你需要维护一个预设音符时间点的数组,结合实时更新的elapsedTime,通过索引跟踪已触发的音符,当流逝时间达到数组中对应音符的时间点时,触发生成音符的动作,同时避免重复触发同一个音符。

代码实现方案

以下是基于你现有代码的改造版本,同时提供更精准的requestAnimationFrame方案(推荐用于动画类游戏):


方案1:基于你现有setInterval的改造

// 预设的音符时间点(单位:秒),根据游戏节奏调整
const noteTimings = [1.0, 2.5, 4.0, 6.3, 8.1];
let currentNoteIndex = 0; // 记录待触发的音符索引
let elapsedTime = 0;
let intervalId;

// 更新游戏状态的核心函数
function updateGame() {
  // 累加本次间隔的时间(和setInterval参数对应,转换为秒)
  elapsedTime += 0.428571;

  // 检查并触发到达时间点的音符
  while (currentNoteIndex < noteTimings.length && elapsedTime >= noteTimings[currentNoteIndex]) {
    spawnNote(); // 执行音符生成逻辑
    currentNoteIndex++; // 移动到下一个待触发音符
  }

  // 所有音符触发完毕后停止计时器
  if (currentNoteIndex >= noteTimings.length) {
    clearInterval(intervalId);
    console.log("所有音符已触发");
  }
}

// 生成音符的实际逻辑(替换为你的DOM/动画代码)
function spawnNote() {
  console.log(`在${elapsedTime.toFixed(2)}秒触发音符下落`);
  // 示例:创建音符DOM元素并添加下落动画
  const note = document.createElement('div');
  note.className = 'rhythm-note';
  note.style.cssText = `
    position: absolute;
    top: 0;
    left: 120px; /* 对应吉他轨道的位置 */
    width: 60px;
    height: 120px;
    background: #ff4444;
    border-radius: 8px;
    transition: top 2s linear; /* 下落时长根据游戏速度调整 */
  `;
  document.body.appendChild(note);
  // 触发下落动画
  setTimeout(() => note.style.top = '550px', 10);
}

// 启动游戏(点击开始按钮时调用)
function startGame() {
  elapsedTime = 0;
  currentNoteIndex = 0;
  intervalId = setInterval(updateGame, 428.571);
}

方案2:推荐用requestAnimationFrame(更精准流畅)

setInterval会受浏览器事件循环延迟影响,时间累积误差较大,requestAnimationFrame会和浏览器刷新同步,更适合动画类游戏:

const noteTimings = [1.0, 2.5, 4.0, 6.3, 8.1];
let currentNoteIndex = 0;
let startTime = null;
let elapsedTime = 0;

function updateGame(timestamp) {
  // 初始化启动时间
  if (!startTime) startTime = timestamp;
  // 计算真实流逝时间(转换为秒)
  elapsedTime = (timestamp - startTime) / 1000;

  // 批量触发到达时间点的音符
  while (currentNoteIndex < noteTimings.length && elapsedTime >= noteTimings[currentNoteIndex]) {
    spawnNote();
    currentNoteIndex++;
  }

  // 还有未触发的音符就继续循环
  if (currentNoteIndex < noteTimings.length) {
    requestAnimationFrame(updateGame);
  } else {
    console.log("所有音符已触发");
  }
}

// 生成音符的函数(和方案1一致,可复用)
function spawnNote() {
  console.log(`在${elapsedTime.toFixed(2)}秒触发音符下落`);
  const note = document.createElement('div');
  note.className = 'rhythm-note';
  note.style.cssText = `
    position: absolute;
    top: 0;
    left: 120px;
    width: 60px;
    height: 120px;
    background: #ff4444;
    border-radius: 8px;
    transition: top 2s linear;
  `;
  document.body.appendChild(note);
  setTimeout(() => note.style.top = '550px', 10);
}

// 启动游戏
function startGame() {
  startTime = null;
  currentNoteIndex = 0;
  requestAnimationFrame(updateGame);
}

关键注意点

  • 避免重复触发:用currentNoteIndex跟踪已处理的音符,每次触发后索引+1,确保每个音符只生成一次。
  • 时间精度:如果游戏对节奏要求高,优先用requestAnimationFrame计算真实流逝时间,避免setInterval的误差累积。
  • 动画调整:transition的时长要和游戏BPM匹配,比如BPM为140时,音符从顶部落到判定线的时间要和节拍对应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 04:43:39