⚡ SpinningTop 效能分析報告

專案名稱:SpinningTop(殭屍陀螺對戰遊戲)
報告日期:2026-07-08
目標平台:Android WebView / Chrome (中低階設備)
技術棧:TypeScript + Three.js r185 + Canvas 2D + Vite

30~40
FPS 單人
20~30
FPS 多人
6
殭屍 Draw Calls
34
渲染物件種類

一、渲染框架概覽

1.1 渲染架構分層

┌─────────────────────────────────────────────────────────────┐ │ Game Layer(遊戲層) │ │ 職責:決定 what to render(什麼物件、在哪裡、什麼狀態) │ ├─────────────────────────────────────────────────────────────┤ │ Render Layer(渲染層) │ │ 職責:how to render(怎麼高效畫出來) │ ├─────────────────────────────────────────────────────────────┤ │ Pipeline Core(管線核心) │ │ WebGLRenderer + OrthographicCamera + LayerSystem │ └─────────────────────────────────────────────────────────────┘ 目前渲染路徑(Compat 模式): ┌──────────────────────────────┐ ┌──────────────────────────────┐ │ WebGL Pipeline │ │ Canvas 2D Overlay │ │ ✅ 殭屍 (InstancedMesh) │ │ ⚠️ 陀螺 + 旋風特效 │ │ ✅ 場景背景 │ │ ⚠️ 粒子 (8種) │ │ │ │ ⚠️ 殘影 (afterimage) │ │ → 1 次 renderer.render() │ │ ⚠️ 衝擊波 / 斬擊線 │ │ → 6 draw calls │ │ ⚠️ 投射物 + 光束 │ │ │ │ ⚠️ UI (血條/計時器) │ └──────────────────────────────┘ └──────────────────────────────┘ 合成輸出至畫面

1.2 各物件渲染方式

遊戲物件 渲染技術 每幀開銷 效能影響
殭屍 (6種, 最多200隻) GPU Instanced SkinnedMesh 6 draw calls (每 type 1 call) ✅ 已優化
場景背景 WebGL BackgroundTechnique 1-2 draw calls ✅ 已優化
陀螺本體 (最多4隻) Canvas 2D drawImage 4× translate + rotate + drawImage ⚠️ 本體開銷低,但附帶特效極重
陀螺旋風特效 Canvas 2D Path + Gradient 每陀螺:6× gradient + 6× stroke(22點/條) 🔴 極重
陀螺護盾/衝刺光罩 Canvas 2D Gradient + Arc 每陀螺:4× gradient + 6× fill/stroke (lineWidth=27) 🔴 極重
殘影 (Afterimage) Canvas 2D drawImage + screen blend 15-30× compositeOp switch + drawImage 🔴 blend mode flush
粒子特效 (8種子型別) Canvas 2D Path/Line 每粒子 1-6× stroke/fill 🟠 量多時很重
衝擊波 Canvas 2D Arc + Gradient 每個:1× gradient + arc + stroke ⚠️ 中等
投射物 + 光束 Canvas 2D Gradient + Path 彩虹光束:12-stop gradient × 3 層 🟠 光束極重
Boss 預警區域 (8種) Canvas 2D Arc + setLineDash 每個:arc + fill + dashed stroke + text ⚠️ 中等
障礙物/方塊/票券 Canvas 2D drawImage/fillRect 每個 1× drawImage 或 fillRect ✅ 輕量
UI (血條/計時器) Canvas 2D fillRect + fillText 固定開銷 ✅ 輕量

1.3 核心技術依賴

技術版本用途
Three.jsr185WebGL 渲染核心 (殭屍 + 背景)
@three.ez/instanced-meshv0.3.15GPU Instancing + per-instance 骨骼動畫
Canvas 2D原生陀螺、特效、UI(目前主要效能瓶頸所在)
ViteBuild tool + Dev server

二、效能瓶頸詳細分析

2.1 現象描述

情境FPS觀察
單人 + 少量殭屍35~45尚可接受
單人 + 旋轉衝刺28~35旋風 + 護盾 + 殘影同時出現
多人 (2-4人)20~30顯著下降,即使渲染物件沒有明顯增多
多人 + 碰撞爆發15~25碰撞時粒子爆量 + 碰撞計算本身

2.2 瓶頸排名:Canvas 2D 渲染開銷

🔴 #1:陀螺旋風螺線(每個陀螺每幀繪製)

🔴 #2:陀螺護盾/衝刺光罩(衝刺中 or spin≥5)

🔴 #3:殘影 Afterimage(衝刺時大量生成)

🟠 #4:碰撞時粒子爆發(爪痕 / 火花 / 星爆)

🟠 #5:彩虹光束投射物 (BombBeam)

2.3 瓶頸排名:CPU / GC 開銷

🔴 #1:每幀 GC Allocation(記憶體分配壓力)

🟠 #2:Concrete Blocks / Spikes 碰撞是 O(N×M) 暴力迴圈

🟡 #3:碰撞 pairKey 字串拼接

三、Canvas 2D vs WebGL 效能比較

項目 Canvas 2D(目前) WebGL (Instanced) 差距
100 個粒子 100× save/restore + path + stroke 1 次 instanced draw call ~50-100×
20 個殘影 20× compositeOp switch + drawImage 1 次 instanced draw (同 texture) ~20×
4 個陀螺旋風 24× gradient + 24× stroke (132點/條) 4× textured ring mesh 或 shader ~10-20×
衝擊波 每個 createRadialGradient + arc + stroke 1 個 expanding ring shader ~5-10×
Blend mode 切換 GPU pipeline flush(極貴) Material 分組,batch 內無切換 不可比

核心差異:Canvas 2D 每次 globalCompositeOperation 改變,瀏覽器必須 flush 累積的 draw buffer → 切換 GPU blend state → 重新開始 batch。在 Android 上這是每次 2-5ms 的開銷。WebGL 中可以把所有 additive blend 物件排在一起,只切一次。

四、碰撞時「卡頓感」分析

FPS 顯示正常但撞到敵人時感覺卡卡的,可能原因:

可能原因機制驗證方式
碰撞解析瞬間位移跳動 resolveCollision 在穿透時一次性推開整個 overlap 距離,造成視覺上的瞬間位置跳躍 觀察碰撞瞬間陀螺是否有「彈跳」而非平滑滑開
碰撞觸發大量粒子生成 一次 addChainsawSparkParticles(24) + addParticles(60) 導致下一幀 Canvas 繪製量暴增 暫時關閉碰撞粒子看是否不卡
hitStop + screenShake 複合效果 hitStopTimer 暫停邏輯 + screenShakeTimer 同時觸發,可能讓畫面在 1-2 幀內凍結 移除 hitStop 看體感是否改善
音效觸發 (SoundSystem.play) Android 上 Web Audio API 首次播放可能造成 jank 靜音測試

五、優化建議路線圖

5.1 短期(不改架構,立即可做)

項目預期改善工時
簡化陀螺旋風:降為 3 條線 + 共用 1 個 gradient Canvas 2D 開銷降 50% 2h
殘影先分組再一次切 compositeOp:ctx.globalCompositeOperation = 'screen' 設一次,用迴圈畫完所有殘影 消除 15-30 次 pipeline flush 30min
粒子上限:場景最多 150 個粒子,超出時不生成新粒子 防止碰撞爆量 30min
碰撞系統零分配改造:預分配 grid Map / checked Set / filter 改為 swap-remove 消除每幀 GC 壓力 4h
護盾光罩 lineWidth 降至 12 (從 27) 像素填充量降一半 10min

5.2 中期(部分物件遷移 WebGL)

項目預期改善工時
殘影遷移 WebGL (instanced sprite quad, additive blend) 消除所有 compositeOp flush 1-2d
粒子遷移 WebGL (GL_POINTS 或 instanced quads) 100+ 粒子 → 1 draw call 2-3d
陀螺旋風改用預烘焙 texture + rotating sprite 6條螺線 → 1 張旋轉貼圖 1d
衝擊波/斬擊線遷移 WebGL shader ring 每個只需 uniform 更新 1-2d

5.3 長期(完全移除 Canvas 2D overlay)

項目預期改善工時
所有特效/物件遷移完成,移除 compat mode 單一 render call,Android 目標 60FPS 2-3w
UI 改為 HTML DOM overlay UI 不佔用 Canvas/WebGL 資源 3-5d

5.4 效能目標

指標目前短期目標長期目標
FPS(多人)20~30≥ 30≥ 50
Frame time25-50ms< 33ms< 16ms
GC stall / 分頻繁大幅減少接近零
Draw calls 總計6 (WebGL) + 100+ (Canvas)< 50< 15
compositeOp flush / 幀20-40 次< 50