2D 視差滾動

前言

視差(Parallax)是一種常用來模擬景深的效果,讓不同圖層(紋理)相對於攝影機以不同速度移動。Godot 提供 Parallax2D 節點來實現這個效果。不過在使用上還是很容易出錯,所以本頁會詳細說明相關屬性,以及常見問題與修正方法。

備註

本頁介紹如何使用 Parallax2D,建議優先使用它來取代過去的 ParallaxLayerParallaxBackground 節點。

入門

Parallax 節點可以加上算繪用的子節點,因此每一層可以用一個或多個節點組成。首先,將你希望能獨立滾動的節點都放到各自的 Parallax 節點下當子節點。請確保每個用到的紋理左上角都對齊 (0, 0),如下圖所示。為什麼這很重要,參考 定位 一節。

../../_images/2d_parallax_size_viewport.webp

上圖的場景是用一張事先準備好的高雲紋理加在 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 屬性會指定當攝影機捲動超過這個數值時自動將位置往前或往後跳。這個效果是透過為所有子畫布項目多加一次重複、並做位移來實現的。當攝影機在原始圖像與重複圖像之間捲動時,畫面會在看不見的情況下跳回頭,營造出圖像無限循環的錯覺。

../../_images/2d_parallax_scroll.gif

這個效果比較細緻,新手常常容易設錯。我們來看看常見問題的「成因」與「解法」。

尺寸不正確

要讓無限重複效果好用,最好是圖像本身就是無縫可重複的,並且「在設定 repeat_size 前」圖像尺寸要跟視窗一樣大或更大。如果你的素材不是專門做來無縫重複,還有一些其他方式可以調整圖像尺寸。

下面是一個紋理太小、不足以覆蓋視窗的例子:

../../_images/2d_parallax_size_bad.webp

可以看到視埠大小是 500x300,但圖像只有 288x208。如果我們把 repeat_size 設成圖像大小,無限重複效果會失敗,因為原始圖像覆蓋不了整個視窗。如果 repeat_size 設成視窗大小,中間又會有大空隙。該怎麼辦?

把視窗縮小

最簡單的解法,就是把視窗調成跟你的圖像一樣大或更小。在 專案設定 > 顯示 > 視窗 裡,把 Viewport WidthViewport Height 設成跟背景圖一樣。

../../_images/2d_parallax_size_viewport.webp

縮放 Parallax2D 本身

如果你不追求像素完美,或不介意有點模糊,也可以直接把紋理放大到填滿螢幕。只要設定 Parallax2Dscale,所有子節點紋理也會跟著縮放。

縮放子節點

和縮放 Parallax2D 類似,你也可以直接把 Sprite2D 子節點放大到足以覆蓋整個畫面。但要注意,像 Parallax2D.repeat_sizeSprite2D.region_rect 這些設定不會自動根據縮放調整,所以必須手動依縮放倍率對應調整這些數值。

../../_images/2d_parallax_size_scale.webp

重複紋理

你也可以一開始就將子節點準備好。如果你有一個 Sprite2D 紋理想要重複但尺寸太小,可以用下列做法讓它重複鋪滿:

如下圖,將圖像重複兩次就足以覆蓋整個畫面。

../../_images/2d_parallax_size_repeat.webp

位置錯誤

常見錯誤是把所有紋理都設為以 (0,0) 為中心:

../../_images/2d_parallax_single_centered.webp

這樣會導致無限重複效果出錯,應該避免。所謂的「無限重複畫布」從 (0,0) 開始,向右下延伸到 repeat_size 設定的大小。

../../_images/2d_parallax_single_expand.webp

如果紋理是以 (0,0) 為中心,無限重複畫布就只有部分被覆蓋,導致只有部分能正確重複。

多設幾個 repeat_times 可以解決嗎?

repeat_times 加大在某些情況下確實有用,但這只是硬解,並不是這個參數本來該解決的問題(稍後會說明)。更好的做法是先了解無限重複的原理,再正確設置你的視差紋理。

首先檢查你的紋理有沒有超出畫布的負座標。確保所有用於視差的紋理都放在從 (0,0) 開始的「無限重複畫布」範圍內。這樣只要 Parallax2D.repeat_size 設正確,就會像這樣,圖像單次循環就能覆蓋整個視窗甚至更大:

../../_images/2d_parallax_repeat_good_norect.webp

想像畫面捲動時,會先顯示紅色方框(這個區域由 repeat_size 決定)的內容,當畫面滾到黃色方框時,畫面會瞬間跳到下一輪,營造出無限捲動的錯覺。

../../_images/2d_parallax_repeat_good.webp

如果你的圖像沒有對齊「無限重複畫布」,當攝影機滾到黃色方框時,畫面會被截掉一半才跳轉,如下圖所示:

../../_images/2d_parallax_repeat_bad.webp

捲動偏移

如果你的視差紋理已經正常顯示,但你希望畫面從不同位置開始,Parallax2D 有一個 scroll_offset 屬性可以調整無限重複畫布的起始點。例如圖像大小 288x208 時,把 scroll_offset 設成 (-144,0)(144,0),就能讓它從圖像中間起始。

重複次數

理想情況下,根據本指南設置後,你的視差紋理即使在畫面縮小時也足以覆蓋整個螢幕。前面我們範例是 288x208 紋理對應 288x208 視窗,但如果我們把 Camera2D.zoom 設為 (0.5, 0.5),畫面縮小一半就會出現問題:

../../_images/2d_parallax_zoom_single.webp

即使預設縮放下都沒問題,當畫面縮小後,原本的紋理就不夠大,無限重複效果就會壞掉。這時就需要用 repeat_times 。設成 3 (前後各加一個重複),就能讓圖像足夠大來支援無限重複。

../../_images/2d_parallax_zoom_repeat_times.webp

如果你的紋理設計本來就要垂直重複,可以把 repeat_sizey 設成相應數值,repeat_times 就會自動在上下也加一個重複。這裡範例只有做水平視差,所以上下會有空白。該怎麼辦呢?可以有創意地把天空拉高、草地拉低,這樣紋理就能同時支援正常與縮小一半的縮放。

../../_images/2d_parallax_zoom_repeat_adjusted.webp

分割畫面

大多數 Godot 分割畫面教學會先寫個小腳本,把第一個 SubViewport 的 Viewport.world_2d 指定給第二個 SubViewport,讓它們共享顯示。這時就常常會有人問:「要怎麼讓兩個畫面都能有同樣的視差效果?」。

視差效果是透過每個圖層根據攝影機位置不同而移動,營造出景深。如果你有多個攝影機,就會產生問題,因為同一張紋理不可能同時出現在兩個不同位置!

其實還是可以辦到,只要把視差節點複製到第二個(甚至第三、第四個):ref:SubViewport<class_subviewport>。下圖是雙人遊戲的範例:

../../_images/2d_parallax_splitscreen.webp

這時會發現兩個 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」外掛,它提供多種預覽模式,非常好用!