WEEK 06 · JAVASCRIPT

06|讀懂遊戲流程

看懂一小段,動手改一點,再回到遊戲找用途。

畫布座標:往右 x 增加,往下 y 增加

原遊戲畫布寬 360、高 640,左上角是 (0, 0)。我方子彈 vy = -520,表示往上移動;敵方子彈 vy = 230,表示往下移動。

canvasX() 用畫布在頁面上的位置及縮放比例,把滑鼠座標換成畫布座標。觸控的 e.touches[0] 是第一個接觸點;passive: false 允許處理函式取消預設捲動。

const r = canvas.getBoundingClientRect();
return (clientX - r.left) * (W / r.width);

時間差 dt:速度乘以時間才是位移

performance.now() 提供毫秒時間。除以 1000 轉成秒,乘上每秒速度得到移動距離。若速度 320、dt 是 0.016 秒,就移動約 5.12 個畫布單位。

原遊戲將 dt 上限設為 0.05 秒,避免停頓後突然跳很遠;這會捨棄超過上限的時間,不是補跑所有落後時間。

const dt = Math.min((now - last) / 1000, 0.05);
last = now;
cannon.x += cannon.speed * dt;

基本繪圖指令

指令用途
clearRect()清除上一張畫面
fillStyle / strokeStyle設定填色/線條顏色
fillRect() / strokeRect()畫實心/外框矩形
beginPath()、moveTo()、lineTo()、closePath()開始路徑、移到起點、畫線、封閉路徑
arc() / ellipse()畫圓弧/橢圓路徑
fill() / stroke()把路徑填色/描邊
save() / restore() / translate()保存/還原繪圖狀態,以及移動座標原點
globalAlpha / fillText()透明度/畫文字

Math.PI × 2 是一整圈的弧度。Math.sin、Math.cos 在爆炸特效中把方向角度轉成水平、垂直速度,先理解用途即可。

主迴圈:資料與畫面分開

init 先建立資料。requestAnimationFrame 登記下一次畫面更新前要呼叫的函式;瀏覽器會傳入時間 now。每次 loop 都先 update 再 draw,最後再登記下一次,不保證固定每秒 60 次。

function loop(now) {
  const dt = Math.min((now - last) / 1000, 0.05);
  last = now;
  update(dt); // 移動、碰撞、勝負
  draw();     // 依照目前資料繪圖
  requestAnimationFrame(loop);
}
init();
requestAnimationFrame(loop);

操作看看:速度 × 時間

你的瀏覽器不支援 Canvas。

按開始後可調整速度,抵達右側後從左側繼續。

動手修改

讓敵機下降速度從每秒 14 改成每秒 25。

展開參考答案

修改 update 裡的數字,仍乘上 dt。

en.y += 25 * dt;