分页: 7 / 7

Re: 「我為歌狂」遊戲製作

发表于 : 2026年 6月 19日 16:47
ejsoon
動畫仍然沒有做好

Re: 「我為歌狂」遊戲製作

发表于 : 2026年 6月 19日 16:53
ejsoon
gemjni 3.1 pro
希望不要用的是下一個四小時

代码: 全选

看到精心製作的 3D 動畫在最後關頭表現不如預期,確實會讓人有些挫折,不過這兩個動畫問題在 Three.js 開發中非常經典,只需要對程式碼進行微調就能完美解決。

以下是針對你提到的兩個 Bug 產生的原因以及修復方式:

### 1. 解決「話筒被吃掉時瞬間消失,沒有過渡動畫」

**原因:** 在 Three.js 中,材質預設是**不透明**的(`transparent: false`)。如果你在渲染過後才動態將 `transparent` 設為 `true`,WebGL 管線不會自動偵測到這個改變,導致你設定的 `opacity` 漸變完全失效。話筒看起來會保持實體狀態,直到動畫時間結束被 `scene.remove()` 瞬間移除。
**解法:** 必須在修改透明度屬性時加上 `m.needsUpdate = true`,通知渲染器更新材質。

請找到程式碼中的 `removeMicrophoneAnim` 函數並替換為以下內容:

```javascript
function removeMicrophoneAnim(mic, delay = 0) {
  if (window.IS_FAST_FORWARD) {
    scene.remove(mic);
    return;
  }
  const mats = Array.isArray(mic.material) ? mic.material : [mic.material];
  mats.forEach(m => {
    m.transparent = true;
    m.needsUpdate = true; // [修正] 必須加入這行,才能讓後續的透明度(opacity)漸變生效
  });
  mic.userData.outroAnim = {
    startTime: performance.now() + delay,
    duration: 600,
    mats: mats
  };
  outroAnimData.push(mic);
}

```

---

### 2. 解決「立方體翻轉時會在沒有完全翻好時停止」

**原因:** 目前的程式碼使用 `slerpQuaternions` 來計算插值,當時間常數 `t` 達到 1 的瞬間,有時因為浮點數精度運算的誤差(特別是涉及 `Math.PI` 180度翻轉時),立方體最後落下的角度可能會產生微微的歪斜。
**解法:** 在動畫的最後一幀(即 `t >= 1` 時),不要再仰賴插值,而是強行透過 `.copy()` 將目標角度完美賦值給立方體,確保它 100% 貼齊目標點數的朝向。

請找到 `animate()` 函數中負責處理立方體得分動畫的段落,並將原先的 `slerpQuaternions` 判斷改寫如下:

```javascript
// 處理立方體得分時的 3D 旋轉翻轉點數動畫
cubes.forEach(cube => {
  if (cube.userData.animating) {
    const elapsed = now - cube.userData.startTime;
    const duration = 1000;
    let t = elapsed / duration;

    if (t >= 1) {
      t = 1;
      cube.userData.animating = false;
    }
    
    // 翻轉時的離地跳躍微幅晃動,需基於其所在的目標高度
    if (!cube.userData.moving) {
      const baseRestY = cube.userData.targetY !== undefined ? cube.userData.targetY : itemY;
      cube.position.y = baseRestY + Math.sin(t * Math.PI) * 48;
    }
    
    // [修正] 確保動畫結束時強制對齊目標角度,避免浮點數誤差導致停在半空中
    if (t >= 1) {
      cube.quaternion.copy(cube.userData.targetQuat);
    } else {
      cube.quaternion.slerpQuaternions(cube.userData.startQuat, cube.userData.targetQuat, t);
    }
  }
});

```

加入這兩段修復後,吃話筒就會有平滑的放大兼淡出效果,而翻滾點數的骰子在落地時也會完美、精準地正向貼合棋盤了。

Re: 「我為歌狂」遊戲製作

发表于 : 2026年 6月 19日 19:07
ejsoon
改進:
一,播放動畫的問題
當在行動階段播放動畫時,當立方體走到第三步拿到分數時,翻轉還沒結束就停止了,立方體停留在一個奇怪的角度,這是不對的。應該要翻轉完成,再執行餘下步驟。

在播放動畫時,當一個立方體經過對方話筒,這個話筒會消失,但沒有消失動畫,這是不對的。應該要像遊戲時那樣,變大1.2倍,同時變透明,最終消失。

在播放動畫時的步數需要更正。當開始走第三步時,步數為3,走完第三步後,如果要放話筒,步數就要顯示「3+1」。放完話筒,才切換到下一回合。

新增功能:div#message中的文字也要實時發生變化,跟遊戲時一樣。

二,箭頭顯示的問題
當顯示箭頭時,當前一方回合開始,卻顯示的是對方上回合的三步行走,這是錯誤的。應該顯示當前方的往後三步以及放置的話筒。

在顯示本回合所放置的話筒時,它的高度應為48,且上下移動(幅度為4),同時旋轉(藍方為順時針,綠方為逆時針)。

改變箭頭的三角形,應跟矩形一樣,也是一個平面,只不過是三角形。

需要明確給出所要修改的地方和所要替換的代碼。

代码: 全选

改進:
一,播放動畫的問題
當在行動階段播放動畫時,當立方體走到第三步拿到分數時,翻轉還沒結束就停止了,立方體停留在一個奇怪的角度,這是不對的。應該要翻轉完成,再執行餘下步驟。

在播放動畫時,當一個立方體經過對方話筒,這個話筒會消失,但沒有消失動畫,這是不對的。應該要像遊戲時那樣,變大1.2倍,同時變透明,最終消失。

在播放動畫時的步數需要更正。當開始走第三步時,步數為3,走完第三步後,如果要放話筒,步數就要顯示「3+1」。放完話筒,才切換到下一回合。

新增功能:div#message中的文字也要實時發生變化,跟遊戲時一樣。

二,箭頭顯示的問題
當顯示箭頭時,當前一方回合開始,卻顯示的是對方上回合的三步行走,這是錯誤的。應該顯示當前方的往後三步以及放置的話筒。

在顯示本回合所放置的話筒時,它的高度應為48,且上下移動(幅度為4),同時旋轉(藍方為順時針,綠方為逆時針)。

改變箭頭的三角形,應跟矩形一樣,也是一個平面,只不過是三角形。

需要明確給出所要修改的地方和所要替換的代碼。

Re: 「我為歌狂」遊戲製作

发表于 : 2026年 6月 19日 19:18
ejsoon
gemini 3.1 pro要加油啊……有沒有更好用的AI……

代码: 全选

沒問題!這些改進能大幅提升播放動畫時的流暢度與邏輯正確性,同時修正箭頭模式的判斷條件。

以下是針對你提出的需求所需要替換的代碼段落。請在你的原始碼中找到對應的函數並進行替換:

### 一、 播放動畫的問題修復

**1. 修改 `togglePlayback` (解決動畫被截斷的問題)**
將原先的 `setInterval` 改為遞迴的 `setTimeout`,透過判斷該步驟是否有得分,動態給予額外的時間(讓 1000 毫秒的翻轉動畫能順利播完)。

請找到 `function togglePlayback(forcePlay)` 並替換為以下代碼:

```javascript
      function togglePlayback(forcePlay) {
        const isPlaying = forcePlay !== undefined ? forcePlay : !playbackInterval;
        const btn = document.getElementById('btn-playback-toggle');
        const slider = document.getElementById('playback-slider');
        const btnPrev = document.getElementById('btn-playback-prev');
        const btnNext = document.getElementById('btn-playback-next');

        if (isPlaying && historyRedoStack.length > 0) {
          btn.innerHTML =
            '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>';
          btn.style.backgroundColor = '#f39c12';
          slider.disabled = true;
          btnPrev.disabled = true;
          btnNext.disabled = true;

          // 使用遞迴 setTimeout 取代 setInterval,以便在得分翻轉時給予足夠的動畫時間
          function playNextStep() {
            if (historyRedoStack.length === 0 || !playbackInterval) {
              togglePlayback(false);
              return;
            }

            let nextSnap = historyRedoStack[historyRedoStack.length - 1];
            let currentSnap = historyUndoStack[historyUndoStack.length - 1];

            // 判斷是否在此步發生了得分 (分數增加)
            let scoreChanged = false;
            if (currentSnap && nextSnap) {
              nextSnap.cubesData.forEach(nc => {
                let cc = currentSnap.cubesData.find(c => c.color === nc.color);
                if (cc && nc.score > cc.score) scoreChanged = true;
              });
            }

            let prevFF = window.IS_FAST_FORWARD;
            window.IS_FAST_FORWARD = false;
            // 取代 walkRedo,強行前進一格
            historyUndoStack.push(saveSnapshot());
            restoreSnapshot(historyRedoStack.pop());
            window.IS_FAST_FORWARD = prevFF;
            updatePlaybackUI();
            clearMarkers();

            // 如果有得分,給予更多時間讓翻轉動畫完成 (800ms移動 + 1000ms翻轉 = 1800ms,這裡抓 2000ms)
            let delay = scoreChanged ? 2000 : 1200;
            playbackInterval = setTimeout(playNextStep, delay);
          }

          playbackInterval = setTimeout(playNextStep, 1200);
        } else {
          btn.innerHTML =
            '<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>';
          btn.style.backgroundColor = '#2ecc71';
          if (playbackInterval) clearTimeout(playbackInterval);
          playbackInterval = null;
          slider.disabled = false;
          btnPrev.disabled = false;
          btnNext.disabled = false;
        }
      }

```

**2. 修改 `restoreSnapshot` 中的退場動畫判定**
話筒之所以沒有消失動畫,是因為程式跑到退場判斷時 `window.IS_FAST_FORWARD` 狀態被污染成了 `true`。我們強行解除快進狀態來呼叫它。

在 `restoreSnapshot` 函數中找到以下段落並替換(約在第 760 行附近):

```javascript
        // 播放動畫:檢查並執行話筒被吃掉的退場動畫
        if (window.IS_PLAYBACK_ANIM && !prevFastForward) {
          prevMics.forEach(pm => {
            let stillExists = snap.micsData.some(sm => sm.c === pm.c && sm.r === pm.r && sm.color === pm.color);
            if (!stillExists) {
              let geo =
                pm.color === 'blue'
                  ? new THREE.CylinderGeometry(20, 20, 4, 6)
                  : new THREE.CylinderGeometry(20, 20, 4, 3);
              let mat = new THREE.MeshLambertMaterial({ color: pm.color === 'blue' ? 0xff4444 : 0x800080 });
              let dummyMic = new THREE.Mesh(geo, mat);
              dummyMic.material.transparent = true; // 確保材質可透明
              let pos = getCellWorldPos(pm.c, pm.r);
              dummyMic.position.set(pos.x, 6, pos.z);
              scene.add(dummyMic);
              
              // 【修復】強制暫時解除快進狀態,以確保觸發退場動畫
              let tempFF = window.IS_FAST_FORWARD;
              window.IS_FAST_FORWARD = false;
              removeMicrophoneAnim(dummyMic, 800); // 延遲 800 毫秒等方塊走到
              window.IS_FAST_FORWARD = tempFF;
            }
          });
        }

```

**3. 修改 `updatePlaybackUI` (同步實時文字與 `3+1` 步數顯示)**
這段修改將確保頂部的 `#message` 會跟著當前步驟進行更新。

請找到 `function updatePlaybackUI()` 並完全替換為:

```javascript
      function updatePlaybackUI() {
        const slider = document.getElementById('playback-slider');
        const totalSteps = historyUndoStack.length + historyRedoStack.length;
        const currentStep = historyUndoStack.length;
        slider.max = totalSteps;
        slider.value = currentStep;

        let dispRound = 0;
        let dispStep = '0';

        let snap = historyUndoStack[historyUndoStack.length - 1];
        if (snap) {
          if (!snap.gameplayActive) {
            dispRound = 0;
            dispStep = '0';
          } else {
            // 計算當前對抗的「回合」數:從行動階段開始算,切換行動方就+1
            dispRound = 1;
            let lastPlayer = null;
            for (let i = 0; i < historyUndoStack.length; i++) {
              let s = historyUndoStack[i];
              if (s.gameplayActive) {
                if (lastPlayer === null) lastPlayer = s.currentPlayer;
                else if (s.currentPlayer !== lastPlayer) {
                  dispRound++;
                  lastPlayer = s.currentPlayer;
                }
              }
            }

            if (snap.subPhase === 'mic') {
              dispStep = '3+1';
            } else {
              dispStep = snap.walkCount.toString();
            }

            // 【新增】同步實時更新頂部 message 文字
            let teamStr = snap.currentPlayer === 'blue' ? (window.currentLang === 'zh' ? '藍方' : 'Blue') : (window.currentLang === 'zh' ? '綠方' : 'Green');
            let msg = '';
            if (snap.subPhase === 'mic') {
                msg = window.currentLang === 'zh' ? `${teamStr}行動:選擇在停留點放置話筒` : `${teamStr} Turn: Place mic on valid stop`;
            } else {
                msg = window.currentLang === 'zh' ? `輪到 ${teamStr} 行動:第 ${snap.walkCount} 次行走` : `${teamStr}'s Turn: Walk ${snap.walkCount}`;
            }
            uiMsg.style.display = 'block';
            uiMsg.innerText = msg;
          }
        }

        document.getElementById('playback-info').innerHTML =
          window.currentLang === 'zh'
            ? `回合: ${dispRound}<br>步數: ${dispStep}`
            : `Round: ${dispRound}<br>Step: ${dispStep}`;
      }

```

---

### 二、 箭頭顯示的問題修復

**1. 替換 `drawArrows` (確保顯示當前方未來動作)**
我們修改提取邏輯:如果存在 `aiPlannedActions`(你剛搜尋的最佳行動),就優先繪製它;否則從 `historyRedoStack` 抓取當前玩家「即將前進」的三步。

請找到 `function drawArrows()` 並完全替換為:

```javascript
      function drawArrows() {
        clearArrows();
        if (!window.isArrowMode || !gameplayActive) return;
        clearMarkers();

        let cp = currentPlayer;
        let steps = [];
        let micPos = null;

        // 優先顯示 AI 規劃的路線或搜尋的最佳路線
        if (aiPlannedActions && aiPlannedActions.length > 0) {
          let activeCube = cubes.find(q => q.userData.color === cp);
          if (activeCube) {
              steps.push({c: activeCube.userData.col, r: activeCube.userData.row});
              aiPlannedActions.forEach(act => {
                  if (act.type === 'walk') steps.push({c: act.c, r: act.r});
                  else if (act.type === 'mic') micPos = {c: act.c, r: act.r};
              });
          }
        }
        // 若沒有預組路線,則顯示重做堆疊中當前方的未來路線 (也就是往後的三步)
        else if (historyRedoStack.length > 0) {
          let activeCube = cubes.find(q => q.userData.color === cp);
          if (activeCube) {
              steps.push({c: activeCube.userData.col, r: activeCube.userData.row});
              for (let i = historyRedoStack.length - 1; i >= 0; i--) {
                  let snap = historyRedoStack[i];
                  if (snap.currentPlayer !== cp) break; // 如果遇到換人則終止
                  
                  let cData = snap.cubesData.find(c => c.color === cp);
                  if (cData) {
                      let lastStep = steps[steps.length - 1];
                      if (lastStep.c !== cData.c || lastStep.r !== cData.r) {
                          steps.push({c: cData.c, r: cData.r});
                      }
                  }
                  
                  // 檢查是否有放置話筒
                  let prevMics = i === historyRedoStack.length - 1 ? historyUndoStack[historyUndoStack.length-1].micsData : historyRedoStack[i+1].micsData;
                  let addedMic = snap.micsData.find(m => m.color === cp && !prevMics.some(pm => pm.c === m.c && pm.r === m.r));
                  if (addedMic) micPos = addedMic;
              }
          }
        }

        if (steps.length <= 1) return;

        // 依據步驟對應不同高度與深淺顏色
        let blueColors = [0x5dade2, 0x2e86c1, 0x1b4f72];
        let greenColors = [0x82e0aa, 0x28b463, 0x186a3b];
        let colors = cp === 'blue' ? blueColors : greenColors;
        let heights = [12, 24, 36];

        for (let i = 0; i < steps.length - 1 && i < 3; i++) {
          createArrow(steps[i], steps[i + 1], colors[i], heights[i]);
        }

        if (micPos) {
          createFloatingMic(cp, micPos.c, micPos.r); 
        }
      }

```

**2. 替換 `createArrow` (將圓錐改為平面三角形)**
使用 `BufferGeometry` 定義平面的頂點,使其呈現薄如紙張的平面三角形。

請找到 `function createArrow(p1, p2, colorHex, height)` 並完全替換為:

```javascript
      function createArrow(p1, p2, colorHex, height) {
        let pos1 = getCellWorldPos(p1.c, p1.r);
        let pos2 = getCellWorldPos(p2.c, p2.r);

        let dx = pos2.x - pos1.x;
        let dz = pos2.z - pos1.z;
        let len = Math.sqrt(dx * dx + dz * dz);
        if (len < 1) return;

        let arrowLen = 24;
        let boxLen = len - arrowLen;
        if (boxLen < 0) {
          boxLen = 0;
          arrowLen = len;
        }

        let mat = new THREE.MeshBasicMaterial({ color: colorHex, side: THREE.DoubleSide });

        if (boxLen > 0) {
          let boxGeo = new THREE.BoxGeometry(24, 2, boxLen);
          let box = new THREE.Mesh(boxGeo, mat);
          let dirAngle = Math.atan2(dx, dz);
          box.position.set(pos1.x + (dx / len) * (boxLen / 2), height, pos1.z + (dz / len) * (boxLen / 2));
          box.rotation.y = dirAngle;
          arrowsGroup.add(box);
        }

        // 修改箭頭為平面三角形
        const vertices = new Float32Array([
            0, 0, -arrowLen / 2,     // 尖端點向 local -Z 軸
            -12, 0, arrowLen / 2,    // 左後
            12, 0, arrowLen / 2      // 右後
        ]);
        let coneGeo = new THREE.BufferGeometry();
        coneGeo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
        let cone = new THREE.Mesh(coneGeo, mat);
        
        let coneCenter = boxLen + arrowLen / 2;
        cone.position.set(pos1.x + (dx / len) * coneCenter, height, pos1.z + (dz / len) * coneCenter);
        cone.lookAt(new THREE.Vector3(pos2.x, height, pos2.z));
        arrowsGroup.add(cone);
      }

```

Re: 「我為歌狂」遊戲製作

发表于 : 2026年 6月 19日 20:37
ejsoon
改進:
一,行走重做的問題
當點擊「行走重做」到第三步時,應該出現的是放置話筒的兩個或一個標示點,並在div#message中提示「…方行動:選擇在停留點放置話筒」,這時「行走重做」的按鈕將disable。如果本回合不用放置話筒,則會在第二步走完的狀態,div#message中顯示「輪到…方行動:第3次行走」,這時「行走重做」的按鈕將disable,點擊「回合前進」才會走到第三步走完的狀態(同時也是對方回合開始的狀態)。

二,播放動畫的問題
當進入「播放動畫」時,在行動階段開始時,回合數是0,步數是0,這是錯誤的,正確的應該是回合1,步數1。當走完第一步時,步數應該是2而不是1。當走完第二步時,步數應該是3而不是2。當走完第三步時,(如果要放話筒)步數應該是「3+1」而不是3。當放完話筒時,回合數應該是2,步數應該是1。

三,箭頭顯示的問題
當處於「箭頭顯示」狀態時,如果本方本回合放置了話筒,則這個話筒將顯示在放置話筒的棋位上方,高度為48,且一直上下移動(幅度為4),同時旋轉(藍方為順時針,綠方為逆時針)。

箭頭的三角形的方向反了,應轉過來,三角形的尖頂對準棋位中心。

需要明確給出所要修改的地方和所要替換的代碼。

代码: 全选

改進:
一,行走重做的問題
當點擊「行走重做」到第三步時,應該出現的是放置話筒的兩個或一個標示點,並在div#message中提示「…方行動:選擇在停留點放置話筒」,這時「行走重做」的按鈕將disable。如果本回合不用放置話筒,則會在第二步走完的狀態,div#message中顯示「輪到…方行動:第3次行走」,這時「行走重做」的按鈕將disable,點擊「回合前進」才會走到第三步走完的狀態(同時也是對方回合開始的狀態)。

二,播放動畫的問題
當進入「播放動畫」時,在行動階段開始時,回合數是0,步數是0,這是錯誤的,正確的應該是回合1,步數1。當走完第一步時,步數應該是2而不是1。當走完第二步時,步數應該是3而不是2。當走完第三步時,(如果要放話筒)步數應該是「3+1」而不是3。當放完話筒時,回合數應該是2,步數應該是1。

三,箭頭顯示的問題
當處於「箭頭顯示」狀態時,如果本方本回合放置了話筒,則這個話筒將顯示在放置話筒的棋位上方,高度為48,且一直上下移動(幅度為4),同時旋轉(藍方為順時針,綠方為逆時針)。

箭頭的三角形的方向反了,應轉過來,三角形的尖頂對準棋位中心。

需要明確給出所要修改的地方和所要替換的代碼。

Re: 「我為歌狂」遊戲製作

发表于 : 2026年 6月 19日 20:49
ejsoon
gemini 3.1 pro說:話筒已經在浮多在轉了

代码: 全选

Here are the specific modifications needed to resolve the issues with "Walk Redo" step logic, Playback animation step counting, and Arrow Mode visuals.

### 1. 解決「行走重做」與「播放動畫」的步數及回合對齊問題

問題的核心在於遊戲切換至對戰階段(`startGameplayPhase`)以及回合切換(`switchTurn`)時,並沒有儲存當下的起點快照,導致「行走重做」在特定階段無法準確鎖定狀態,且播放動畫時找不到回合起點而顯示為 `0/0`。

請修改以下三個函數:`pushAction`、`startGameplayPhase` 和 `switchTurn`。

**替換代碼:**

```javascript
      function pushAction() {
        const newSnap = saveSnapshot();
        // 防呆機制:避免在同一個狀態重複儲存快照
        if (historyUndoStack.length > 0) {
          const lastSnap = historyUndoStack[historyUndoStack.length - 1];
          if (lastSnap.gameplayActive === newSnap.gameplayActive &&
              lastSnap.currentPlayer === newSnap.currentPlayer &&
              lastSnap.walkCount === newSnap.walkCount &&
              lastSnap.subPhase === newSnap.subPhase &&
              JSON.stringify(lastSnap.cubesData) === JSON.stringify(newSnap.cubesData) &&
              JSON.stringify(lastSnap.micsData) === JSON.stringify(newSnap.micsData)) {
            return; 
          }
        }
        historyUndoStack.push(newSnap);
        historyRedoStack = []; 
        updateUndoRedoButtons();
      }

      function startGameplayPhase() {
        gameplayActive = true;
        currentPlayer = gameRound === 1 ? 'blue' : 'green';
        pushAction(); // 儲存遊戲階段的第一個起點快照
        updateUndoRedoButtons();
        startPlayerTurn();
      }

      function switchTurn() {
        // 回合切換時合併紀錄
        if (currentTurnCoords.length > 0) {
          globalMoveHistory.push(currentTurnCoords.join(''));
          currentTurnCoords = [];
        }
        currentPlayer = currentPlayer === 'blue' ? 'green' : 'blue';
        pushAction(); // 儲存換人後的回合起點快照
        startPlayerTurn();
      }

```

---

### 2. 修復箭頭顯示的三角形尖頂方向

在 `createArrow` 函數中,三角形的頂點陣列 Z 軸方向設置相反了,導致箭頭尖端背對目標中心。將其 Z 軸數值反轉即可修正。

**尋找以下代碼 (`createArrow` 函數內):**

```javascript
        // 修改箭頭為平面三角形
        const vertices = new Float32Array([
          0, 0, -arrowLen / 2, // 尖端點向 local -Z 軸
          -12, 0, arrowLen / 2, // 左後
          12, 0, arrowLen / 2 // 右後
        ]);

```

**替換為:**

```javascript
        // 修改箭頭為平面三角形 (反轉 Z 軸使尖端對準棋位中心)
        const vertices = new Float32Array([
          0, 0, arrowLen / 2,
          -12, 0, -arrowLen / 2,
          12, 0, -arrowLen / 2
        ]);

```

*(註:你所描述的「箭頭顯示」下的浮動話筒動畫與旋轉方向(`createFloatingMic` 與 `animate` 迴圈內)原代碼已實裝了正確的上下浮動與藍方順時針/綠方逆時針旋轉邏輯,不需要再額外改動。)*

Re: 「我為歌狂」遊戲製作

发表于 : 2026年 6月 20日 01:29
ejsoon
改進:
在function saveSnapshot() {...}中,subPhase: markers.some(m => m.position.y === 6) ? 'mic' : 'walk'這句對subPhase的判斷應該是錯誤的,它沒能正確分辨當前步是行走還是放置話筒,請修復。

代码: 全选

改進:
在function saveSnapshot() {...}中,subPhase: markers.some(m => m.position.y === 6) ? 'mic' : 'walk'這句對subPhase的判斷應該是錯誤的,它沒能正確分辨當前步是行走還是放置話筒,請修復。