Research 01 / Rendering

不縮放 gameplay,讓裁切範圍成為鏡頭。

原版座標與關卡演算法保留在 PC gameplay space;GBA 以 240×160 柔性視窗使用被裁掉的邊界,不參與敵人位置或碰撞重算。

兩個座標空間

Tyrian 的完整 PC 畫面是 320×200,但戰鬥視窗不是整個畫面。右側儀表板與 下方狀態區移除後,專案使用 264×184 的原始 gameplay space。GBA 顯示為 240×160,因此水平與垂直各捨去 24 pixels:

264×184PC gameplay space
36, 12source crop origin
240×160GBA final viewport

玩家、敵人、子彈、碰撞與 level event 仍使用 source coordinates。 只有 presentation 把 world coordinate 減去裁切 origin。這避免逐物件縮放、 rounding drift 與碰撞規格分叉,也讓畫面密度保持原版感。

`(36, 12)` 是鏡頭的中立原點,不再是永遠固定的 crop。柔性鏡頭只改 presentation;關卡事件、敵人 AI、子彈與碰撞從不讀取 camera state。

把隱藏的 24 pixels 變成柔性鏡頭

264×184 與 240×160 在兩軸都相差 24 pixels。固定置中裁切會把每一側 12 pixels 永久藏起來;現行實作則把這段 slack 當作有限的鏡頭行程。 玩家待在中央區域時畫面穩定不動,接近來源視野邊緣後才讓 crop origin 平順跟上。

24..48水平 source origin X
0..24垂直 source origin Y
120×80中央 stationary dead zone
  1. 從 source player position 求目標。 中心點為 `(156, 92)`;玩家超出 dead zone 的部分才轉成 camera target。
  2. 把目標限制在原本的裁切餘量。 每軸最多移動 ±12 pixels,所以不會顯示到 264×184 gameplay space 之外。
  3. 以 Q8 fixed point 做一階平滑。 每個 30 Hz logic tick 前進剩餘距離的 1/4,再四捨五入成硬體 scroll pixel; 小幅操作不會讓畫面顫動,快速靠邊也不會突然跳 12 pixels。
  4. 所有 presentation 共用同一原點。 三層 BG、玩家、敵人、子彈、爆炸與拾取物一起平移,原有 parallax 仍在各背景層自己的 source offset 上運作。

增加的是 GBA 可用活動範圍,不是改大關卡

固定中央 crop 的早期版本為避免船體被切掉,曾把玩家 source Y 從 OpenTyrian 的 `10..160` 收窄為 `17..152`。柔性鏡頭抵達上下邊界後, release 可以恢復原始 `JE_playerMovement()` 的 `10..160`: 最上方時船體第一個有效 pixel 位於 screen Y=5,最下方時最後一個有效 pixel 位於 Y=155,兩端仍完整可見。

因此「活動範圍增加」是相對於固定 GBA crop:玩家重新取得原版 source viewport 的邊界空間。PC 關卡尺寸、座標、碰撞與事件觸發範圍都沒有擴張, 也不會因鏡頭位置產生另一套遊戲規則。

鏡頭移動也必須納入背景串流

Tyrian 背景是持續捲動的 tile map,不是 264×184 靜態圖片。只改 GBA `VOFS` 會讓鏡頭在 Y=0 或 Y=24 時讀到尚未安裝的 ring rows,形成上下 邊界直條破圖。現行 scheduler 使用與硬體相同的 camera-adjusted presentation scroll;map scroll 或 camera scroll 任一方跨越 8-pixel 邊界,都會排程新露出的 top/bottom row。

所有 active layers 會先 preflight。若該 VBlank 暫時無法完成必要 row DMA,Q8 camera target 只延後一個 logic tick,不會先顯示 stale tile。 背景 ownership 另保留 21 個可見 rows,加上上下各 2 個 hysteresis rows; 這個 25-row window 避免玩家在邊界折返時反覆釋放、重建相同資料。

25 rows21 visible + 上下各 2
0stream drops/approximations
41 = 41Episode 2 soft/fixed missed VBlank

原始 Episode 2 High/Normal A/B 中,25-row 柔性鏡頭與固定 crop 都是 41 missed VBlanks;也就是增加視野活動範圍後,背景換列成本仍回到 固定鏡頭基線。v89 LOW 正式版沿用相同 camera 與 ownership band; dynamic frame drop 保存的 held window 也使用 camera-adjusted scroll,延後 presentation 時不會提前釋放仍在畫面上的 row。

三層不是三張裝飾圖

原版每層具有獨立捲動速度、水平 phase、是否位於敵人前後方等狀態。 GBA 使用三個 tile background 保存 layer identity,不能把缺少的 tile 用「看起來最接近」的圖補上,否則岩壁、雲與可破壞物件會在後續事件中錯位。

  • 底層地形:通常最慢或作為固定地面。
  • 中層:可依 source flag 移到部分 enemy group 前方。
  • 上層/雲層:需要與 sky enemy、top enemy 分開排序。

把 PC draw order 翻成有限 priority

PC renderer 可以依序畫很多群組;GBA 只有有限 BG priority 與 OBJ priority。 專案沒有為某張錯圖硬寫例外,而是把 OpenTyrian 的 background2overbackground3overtopEnemyOverskyEnemyOverAll 等規則整理成 可枚舉的 priority policy。

回歸測試會跑完整組合,現行契約包含 252 個 layer-rule checks。這讓修正 Episode 1 的雲層時,不會無意破壞 Episode 4 的前景物件。

為何 1:1 更便宜

ARM7TDMI 沒有硬體除法器。若每個 sprite、子彈與碰撞 box 都做 240/264、 160/184 的比例換算,除了 CPU cost,也會引入不同的四捨五入結果。 1:1 crop 把成本收斂成整數加減、OAM clipping 與 background offset, 將有限 cycle 留給敵人 AI、碰撞與資源快取。

柔性裁切後的目前版本第一關多圖層場景
Source coordinates,GBA 1:1 soft crop240×160