Re: 「我為歌狂」遊戲製作
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);
}
}
});
```
加入這兩段修復後,吃話筒就會有平滑的放大兼淡出效果,而翻滾點數的骰子在落地時也會完美、精準地正向貼合棋盤了。https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 「我為歌狂」遊戲製作
改進:
一,播放動畫的問題
當在行動階段播放動畫時,當立方體走到第三步拿到分數時,翻轉還沒結束就停止了,立方體停留在一個奇怪的角度,這是不對的。應該要翻轉完成,再執行餘下步驟。
在播放動畫時,當一個立方體經過對方話筒,這個話筒會消失,但沒有消失動畫,這是不對的。應該要像遊戲時那樣,變大1.2倍,同時變透明,最終消失。
在播放動畫時的步數需要更正。當開始走第三步時,步數為3,走完第三步後,如果要放話筒,步數就要顯示「3+1」。放完話筒,才切換到下一回合。
新增功能:div#message中的文字也要實時發生變化,跟遊戲時一樣。
二,箭頭顯示的問題
當顯示箭頭時,當前一方回合開始,卻顯示的是對方上回合的三步行走,這是錯誤的。應該顯示當前方的往後三步以及放置的話筒。
在顯示本回合所放置的話筒時,它的高度應為48,且上下移動(幅度為4),同時旋轉(藍方為順時針,綠方為逆時針)。
改變箭頭的三角形,應跟矩形一樣,也是一個平面,只不過是三角形。
需要明確給出所要修改的地方和所要替換的代碼。
一,播放動畫的問題
當在行動階段播放動畫時,當立方體走到第三步拿到分數時,翻轉還沒結束就停止了,立方體停留在一個奇怪的角度,這是不對的。應該要翻轉完成,再執行餘下步驟。
在播放動畫時,當一個立方體經過對方話筒,這個話筒會消失,但沒有消失動畫,這是不對的。應該要像遊戲時那樣,變大1.2倍,同時變透明,最終消失。
在播放動畫時的步數需要更正。當開始走第三步時,步數為3,走完第三步後,如果要放話筒,步數就要顯示「3+1」。放完話筒,才切換到下一回合。
新增功能:div#message中的文字也要實時發生變化,跟遊戲時一樣。
二,箭頭顯示的問題
當顯示箭頭時,當前一方回合開始,卻顯示的是對方上回合的三步行走,這是錯誤的。應該顯示當前方的往後三步以及放置的話筒。
在顯示本回合所放置的話筒時,它的高度應為48,且上下移動(幅度為4),同時旋轉(藍方為順時針,綠方為逆時針)。
改變箭頭的三角形,應跟矩形一樣,也是一個平面,只不過是三角形。
需要明確給出所要修改的地方和所要替換的代碼。
代码: 全选
改進:
一,播放動畫的問題
當在行動階段播放動畫時,當立方體走到第三步拿到分數時,翻轉還沒結束就停止了,立方體停留在一個奇怪的角度,這是不對的。應該要翻轉完成,再執行餘下步驟。
在播放動畫時,當一個立方體經過對方話筒,這個話筒會消失,但沒有消失動畫,這是不對的。應該要像遊戲時那樣,變大1.2倍,同時變透明,最終消失。
在播放動畫時的步數需要更正。當開始走第三步時,步數為3,走完第三步後,如果要放話筒,步數就要顯示「3+1」。放完話筒,才切換到下一回合。
新增功能:div#message中的文字也要實時發生變化,跟遊戲時一樣。
二,箭頭顯示的問題
當顯示箭頭時,當前一方回合開始,卻顯示的是對方上回合的三步行走,這是錯誤的。應該顯示當前方的往後三步以及放置的話筒。
在顯示本回合所放置的話筒時,它的高度應為48,且上下移動(幅度為4),同時旋轉(藍方為順時針,綠方為逆時針)。
改變箭頭的三角形,應跟矩形一樣,也是一個平面,只不過是三角形。
需要明確給出所要修改的地方和所要替換的代碼。https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 「我為歌狂」遊戲製作
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);
}
```https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 「我為歌狂」遊戲製作
改進:
一,行走重做的問題
當點擊「行走重做」到第三步時,應該出現的是放置話筒的兩個或一個標示點,並在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),同時旋轉(藍方為順時針,綠方為逆時針)。
箭頭的三角形的方向反了,應轉過來,三角形的尖頂對準棋位中心。
需要明確給出所要修改的地方和所要替換的代碼。
代码: 全选
改進:
一,行走重做的問題
當點擊「行走重做」到第三步時,應該出現的是放置話筒的兩個或一個標示點,並在div#message中提示「…方行動:選擇在停留點放置話筒」,這時「行走重做」的按鈕將disable。如果本回合不用放置話筒,則會在第二步走完的狀態,div#message中顯示「輪到…方行動:第3次行走」,這時「行走重做」的按鈕將disable,點擊「回合前進」才會走到第三步走完的狀態(同時也是對方回合開始的狀態)。
二,播放動畫的問題
當進入「播放動畫」時,在行動階段開始時,回合數是0,步數是0,這是錯誤的,正確的應該是回合1,步數1。當走完第一步時,步數應該是2而不是1。當走完第二步時,步數應該是3而不是2。當走完第三步時,(如果要放話筒)步數應該是「3+1」而不是3。當放完話筒時,回合數應該是2,步數應該是1。
三,箭頭顯示的問題
當處於「箭頭顯示」狀態時,如果本方本回合放置了話筒,則這個話筒將顯示在放置話筒的棋位上方,高度為48,且一直上下移動(幅度為4),同時旋轉(藍方為順時針,綠方為逆時針)。
箭頭的三角形的方向反了,應轉過來,三角形的尖頂對準棋位中心。
需要明確給出所要修改的地方和所要替換的代碼。- 附件
-
csinger24541.html.7z- (31.76 KiB) 已下载 4 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 「我為歌狂」遊戲製作
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` 迴圈內)原代碼已實裝了正確的上下浮動與藍方順時針/綠方逆時針旋轉邏輯,不需要再額外改動。)*https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
Re: 「我為歌狂」遊戲製作
改進:
在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的判斷應該是錯誤的,它沒能正確分辨當前步是行走還是放置話筒,請修復。
代码: 全选
改進:
在function saveSnapshot() {...}中,subPhase: markers.some(m => m.position.y === 6) ? 'mic' : 'walk'這句對subPhase的判斷應該是錯誤的,它沒能正確分辨當前步是行走還是放置話筒,請修復。- 附件
-
csinger24542.html.7z- (31.73 KiB) 已下载 1 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
弈趣極光:享受思維樂趣
-
- 相似主题
- 回复总数
- 阅读次数
- 最新帖子
在线用户
正浏览此版面之用户: 没有注册用户 和 0 访客
