| 遊戲物件 | 渲染技術 | 每幀開銷 | 效能影響 |
|---|---|---|---|
| 殭屍 (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 | 固定開銷 | ✅ 輕量 |
| 技術 | 版本 | 用途 |
|---|---|---|
| Three.js | r185 | WebGL 渲染核心 (殭屍 + 背景) |
| @three.ez/instanced-mesh | v0.3.15 | GPU Instancing + per-instance 骨骼動畫 |
| Canvas 2D | 原生 | 陀螺、特效、UI(目前主要效能瓶頸所在) |
| Vite | — | Build tool + Dev server |
| 情境 | FPS | 觀察 |
|---|---|---|
| 單人 + 少量殭屍 | 35~45 | 尚可接受 |
| 單人 + 旋轉衝刺 | 28~35 | 旋風 + 護盾 + 殘影同時出現 |
| 多人 (2-4人) | 20~30 | 顯著下降,即使渲染物件沒有明顯增多 |
| 多人 + 碰撞爆發 | 15~25 | 碰撞時粒子爆量 + 碰撞計算本身 |
createRadialGradient() + 6× path stroke (132 頂點) + lineDashlineWidth 大於 10 的 arc stroke 會產生大量像素填充,加上半透明 compositingglobalCompositeOperation = 'screen' → translate → rotate → scale → drawImage → restoreglobalCompositeOperation = 'lighter' + 2 次 stroke = blend mode flushnew Map() + new Set<string>() + 4× .filter() 新 Array + 字串拼接 pairKeythis.particles = this.particles.filter(...) 每幀重建(出現 20+ 次)mapZombieStates(), mapTopStates() 每幀建立新 state 物件陣列collidableEntities.forEach → concreteBlocks.forEachconst pairKey = a.id + '|' + b.id — 每對碰撞候選都產生新字串checkedPairs.has(pairKey) — Set 的 string hashing 比 number 慢a.numId * 10000 + b.numId) 可消除字串 GC| 項目 | 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 | 靜音測試 |
| 項目 | 預期改善 | 工時 |
|---|---|---|
| 簡化陀螺旋風:降為 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 |
| 項目 | 預期改善 | 工時 |
|---|---|---|
| 殘影遷移 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 |
| 項目 | 預期改善 | 工時 |
|---|---|---|
| 所有特效/物件遷移完成,移除 compat mode | 單一 render call,Android 目標 60FPS | 2-3w |
| UI 改為 HTML DOM overlay | UI 不佔用 Canvas/WebGL 資源 | 3-5d |
| 指標 | 目前 | 短期目標 | 長期目標 |
|---|---|---|---|
| FPS(多人) | 20~30 | ≥ 30 | ≥ 50 |
| Frame time | 25-50ms | < 33ms | < 16ms |
| GC stall / 分 | 頻繁 | 大幅減少 | 接近零 |
| Draw calls 總計 | 6 (WebGL) + 100+ (Canvas) | < 50 | < 15 |
| compositeOp flush / 幀 | 20-40 次 | < 5 | 0 |