2D 視差滾動
前言
視差(Parallax)是一種常用來模擬景深的效果,讓不同圖層(紋理)相對於攝影機以不同速度移動。Godot 提供 Parallax2D 節點來實現這個效果。不過在使用上還是很容易出錯,所以本頁會詳細說明相關屬性,以及常見問題與修正方法。
備註
本頁介紹如何使用 Parallax2D,建議優先使用它來取代過去的 ParallaxLayer 和 ParallaxBackground 節點。
入門
Parallax 節點可以加上算繪用的子節點,因此每一層可以用一個或多個節點組成。首先,將你希望能獨立滾動的節點都放到各自的 Parallax 節點下當子節點。請確保每個用到的紋理左上角都對齊 (0, 0),如下圖所示。為什麼這很重要,參考 定位 一節。
上圖的場景是用一張事先準備好的高雲紋理加在 Sprite2D 上,但你也可以用多個節點分別排開來組成這一層。
捲動縮放
視差效果的核心是 scroll_scale 屬性。這個屬性會當作滾動速度的倍率,讓每個軸向的圖層可以跟攝影機有不同的移動速度。數值 1 表示該層與攝影機同速移動。如果你希望圖像看起來比較遠,就設小於 1(0 則完全不動);如果要讓內容看起來更靠近攝影機,則設大於 1,這樣滾動會比較快。
上圖場景有五個圖層,以下是一些常用的 scroll_scale 值參考:
(0.7, 1)- 森林(0.5, 1)- 山丘(0.3, 1)- 低雲(0.2, 1)- 高雲(0.1, 1)- 天空
下方影片展示這些數值在遊戲中如何影響視差滾動效果:
無限重複
Parallax2D 還能額外讓紋理看起來像是無限重複。repeat_size 屬性會指定當攝影機捲動超過這個數值時自動將位置往前或往後跳。這個效果是透過為所有子畫布項目多加一次重複、並做位移來實現的。當攝影機在原始圖像與重複圖像之間捲動時,畫面會在看不見的情況下跳回頭,營造出圖像無限循環的錯覺。
這個效果比較細緻,新手常常容易設錯。我們來看看常見問題的「成因」與「解法」。
尺寸不正確
要讓無限重複效果好用,最好是圖像本身就是無縫可重複的,並且「在設定 repeat_size 前」圖像尺寸要跟視窗一樣大或更大。如果你的素材不是專門做來無縫重複,還有一些其他方式可以調整圖像尺寸。
下面是一個紋理太小、不足以覆蓋視窗的例子:
可以看到視埠大小是 500x300,但圖像只有 288x208。如果我們把 repeat_size 設成圖像大小,無限重複效果會失敗,因為原始圖像覆蓋不了整個視窗。如果 repeat_size 設成視窗大小,中間又會有大空隙。該怎麼辦?
把視窗縮小
最簡單的解法,就是把視窗調成跟你的圖像一樣大或更小。在 專案設定 > 顯示 > 視窗 裡,把 Viewport Width 和 Viewport Height 設成跟背景圖一樣。
縮放 Parallax2D 本身
如果你不追求像素完美,或不介意有點模糊,也可以直接把紋理放大到填滿螢幕。只要設定 Parallax2D 的 scale,所有子節點紋理也會跟著縮放。
縮放子節點
和縮放 Parallax2D 類似,你也可以直接把 Sprite2D 子節點放大到足以覆蓋整個畫面。但要注意,像 Parallax2D.repeat_size 和 Sprite2D.region_rect 這些設定不會自動根據縮放調整,所以必須手動依縮放倍率對應調整這些數值。
重複紋理
你也可以一開始就將子節點準備好。如果你有一個 Sprite2D 紋理想要重複但尺寸太小,可以用下列做法讓它重複鋪滿:
將 region_enabled 設為
true把 region_rect 設成足夠覆蓋視窗的多倍尺寸。
如下圖,將圖像重複兩次就足以覆蓋整個畫面。
位置錯誤
常見錯誤是把所有紋理都設為以 (0,0) 為中心:
這樣會導致無限重複效果出錯,應該避免。所謂的「無限重複畫布」從 (0,0) 開始,向右下延伸到 repeat_size 設定的大小。
如果紋理是以 (0,0) 為中心,無限重複畫布就只有部分被覆蓋,導致只有部分能正確重複。
多設幾個 repeat_times 可以解決嗎?
把 repeat_times 加大在某些情況下確實有用,但這只是硬解,並不是這個參數本來該解決的問題(稍後會說明)。更好的做法是先了解無限重複的原理,再正確設置你的視差紋理。
首先檢查你的紋理有沒有超出畫布的負座標。確保所有用於視差的紋理都放在從 (0,0) 開始的「無限重複畫布」範圍內。這樣只要 Parallax2D.repeat_size 設正確,就會像這樣,圖像單次循環就能覆蓋整個視窗甚至更大:
想像畫面捲動時,會先顯示紅色方框(這個區域由 repeat_size 決定)的內容,當畫面滾到黃色方框時,畫面會瞬間跳到下一輪,營造出無限捲動的錯覺。
如果你的圖像沒有對齊「無限重複畫布」,當攝影機滾到黃色方框時,畫面會被截掉一半才跳轉,如下圖所示:
捲動偏移
如果你的視差紋理已經正常顯示,但你希望畫面從不同位置開始,Parallax2D 有一個 scroll_offset 屬性可以調整無限重複畫布的起始點。例如圖像大小 288x208 時,把 scroll_offset 設成 (-144,0) 或 (144,0),就能讓它從圖像中間起始。
重複次數
理想情況下,根據本指南設置後,你的視差紋理即使在畫面縮小時也足以覆蓋整個螢幕。前面我們範例是 288x208 紋理對應 288x208 視窗,但如果我們把 Camera2D.zoom 設為 (0.5, 0.5),畫面縮小一半就會出現問題:
即使預設縮放下都沒問題,當畫面縮小後,原本的紋理就不夠大,無限重複效果就會壞掉。這時就需要用 repeat_times 。設成 3 (前後各加一個重複),就能讓圖像足夠大來支援無限重複。
如果你的紋理設計本來就要垂直重複,可以把 repeat_size 的 y 設成相應數值,repeat_times 就會自動在上下也加一個重複。這裡範例只有做水平視差,所以上下會有空白。該怎麼辦呢?可以有創意地把天空拉高、草地拉低,這樣紋理就能同時支援正常與縮小一半的縮放。
分割畫面
大多數 Godot 分割畫面教學會先寫個小腳本,把第一個 SubViewport 的 Viewport.world_2d 指定給第二個 SubViewport,讓它們共享顯示。這時就常常會有人問:「要怎麼讓兩個畫面都能有同樣的視差效果?」。
視差效果是透過每個圖層根據攝影機位置不同而移動,營造出景深。如果你有多個攝影機,就會產生問題,因為同一張紋理不可能同時出現在兩個不同位置!
其實還是可以辦到,只要把視差節點複製到第二個(甚至第三、第四個):ref:SubViewport<class_subviewport>。下圖是雙人遊戲的範例:
這時會發現兩個 SubViewport 都會顯示兩個背景。我們想要的是每個視差圖層只顯示在對應的畫面。可以這樣做:
讓所有視差節點都維持預設的 visibility_layer (層 1)。
把第一個 SubViewport 的 canvas_cull_mask 設成只顯示層 1 和 2。
第二個 SubViewport 也一樣設,但改用層 1 和 3。
把第一個 SubViewport 的所有視差節點放在同一個父節點下,並把該父節點的 visibility_layer 設為 2。
第二個 SubViewport 的視差節點也同理,但設成 3 層。
這個機制是這樣:如果某個畫布項目的 visibility_layer 跟 SubViewport 的 canvas_cull_mask 不符,就算子節點有設定,整個分支也都會被隱藏。我們就利用這點,讓每個 SubViewport 只顯示有對應父層 visibility_layer 的視差節點。
在編輯器中預覽
Godot 4.3 以前的做法,建議每一層都用一個自己的 ParallaxBackground ,啟用 follow_viewport_enabled ,再分別縮放每一層。這個做法其實一直都比較複雜,但現今你可以改用 CanvasLayer 來實現分層,而不是 ParallaxBackground 。
備註
另一個推薦是 KoBeWi 的「Parallax2D Preview」外掛,它提供多種預覽模式,非常好用!