JavaScript赛博朋克破解小游戏类名操作致序列误判问题
问题解决:赛博朋克2077入侵协议克隆游戏的序列误判问题
项目背景与核心逻辑
我正在学习JavaScript,开发《赛博朋克2077》“入侵协议”迷你游戏的克隆项目。游戏核心玩法是玩家点击矩阵中的十六进制按钮,匹配屏幕上的3组目标序列,选中的十六进制会存入buffer。
核心代码逻辑:
coreMatrixMechanic:创建矩阵按钮,点击后将选中项存入buffer,添加chosen-hexadecimals类,调用compareClickedHexadecimalsToSequence和sequenceCheck函数。compareClickedHexadecimalsToSequence:检查选中项是否匹配当前序列的目标项,匹配则添加correctly-chosen-hex类并递增sequenceIndexTracker。sequenceCheck:检查矩阵中是否存在可用项匹配序列未完成的部分,若不存在则标记该序列为failed。
问题场景
当目标序列为「63 34」且矩阵中仅存在一个63时,点击63后,compare函数会禁用该按钮,随后sequenceCheck因找不到可用的63(已被禁用),误将仍有可用34的序列标记为failed。之前尝试添加if (arrayOfHexadecimalsInASequence[sequenceIndexTracker].classList.contains('correctly-chosen-hex')) return;解决,但因部分序列尚未匹配到对应元素出现undefined错误。
解决方案
方案一:修正sequenceCheck的检查逻辑(推荐)
不要检查整个序列的所有项,只针对当前序列下一个需要匹配的项做判断,同时跳过已完成的序列:
function sequenceCheck() { // 假设用数组维护每个序列的追踪器,对应3组序列 sequences.forEach((sequenceDom, seqIndex) => { const currentTracker = sequenceIndexTracker[seqIndex]; // 序列已完成,直接跳过检查 if (currentTracker >= targetSequences[seqIndex].length) return; const nextRequiredHex = targetSequences[seqIndex][currentTracker]; // 查找矩阵中未被选中的对应按钮 const hasAvailableBtn = Array.from(document.querySelectorAll('.matrix-button:not(.chosen-hexadecimals)')) .some(btn => btn.textContent.trim() === nextRequiredHex); if (!hasAvailableBtn) { sequenceDom.classList.add('failed'); } else { // 恢复之前误标记的failed状态 sequenceDom.classList.remove('failed'); } }); }
方案二:用MutationObserver监听类名变化
如果坚持用监听类名的思路,需要先判断目标元素是否存在,避免undefined错误:
// 初始化类名变化监听 const classObserver = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.type === 'attributes' && mutation.attributeName === 'class') { const targetBtn = mutation.target; sequences.forEach((sequenceDom, seqIndex) => { const tracker = sequenceIndexTracker[seqIndex]; const targetSequence = targetSequences[seqIndex]; if (tracker >= targetSequence.length) return; // 确保当前追踪的元素存在,再判断类名 const matchedElement = arrayOfHexadecimalsInASequence[seqIndex]?.[tracker]; if (matchedElement && matchedElement === targetBtn && targetBtn.classList.contains('correctly-chosen-hex')) { return; // 已匹配的项跳过检查 } // 执行序列可用性检查 checkSequenceValidity(sequenceDom, seqIndex); }); } }); }); // 给所有矩阵按钮添加监听 document.querySelectorAll('.matrix-button').forEach(btn => { classObserver.observe(btn, { attributes: true }); }); // 封装序列检查逻辑 function checkSequenceValidity(sequenceDom, seqIndex) { const tracker = sequenceIndexTracker[seqIndex]; const targetSequence = targetSequences[seqIndex]; if (tracker >= targetSequence.length) return; const nextHex = targetSequence[tracker]; const hasAvailable = Array.from(document.querySelectorAll('.matrix-button:not(.chosen-hexadecimals)')) .some(btn => btn.textContent.trim() === nextHex); sequenceDom.classList.toggle('failed', !hasAvailable); }
代码优化建议
- 独立维护序列状态:用数组
sequenceIndexTracker = [0, 0, 0]对应3组序列的追踪进度,避免全局变量冲突。 - 分离数据与DOM:提前将矩阵按钮的文本、目标序列存入JS变量,减少频繁DOM查询的开销。
- 禁用按钮更可靠:点击后给按钮添加
disabled属性,检查可用项时用.matrix-button:not([disabled])选择器,比类名判断更准确。 - 函数职责单一化:拆分
coreMatrixMechanic的逻辑,比如把buffer更新、类名标记拆成独立函数,提升代码可读性。
内容的提问来源于stack exchange,提问作者MInH THiỆn
相关产品推荐
相关产品推荐

