如何利用流逝时间编写函数实现节奏游戏音符下落(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
相关产品推荐
相关产品推荐

