我們現在有主角,有場景,有背景。但是我們的活動範圍依然被限制在“一個屏幕”中。
我們現在可以在TileMap上畫出更多的場景內容,但是我們的主角即使可以走到畫面外,畫面也不會自己跟著走過去。
這種時候我們需要一個攝像機來跟拍我們的主角。在虛擬世界中也需要一個攝像機來觀察虛擬世界。
Godot中的攝像機節點就叫Camera,主要有2D和3D兩種版本。在2D場景中我們自然選擇Camera2D。基本的用法也非常簡單,我們只需要在我們的Player場景中加入Camera2D即可:
2D攝像機可以調整的參數也比較簡單直白。Offset是偏移不用說。Ignore Rotation會忽略旋轉。Zoom是縮放,你可以根據場景大小來拉近拉遠攝像機。
Anchor Mode(錨定模式)可以調整攝像機的移動邏輯。默認的Drag Center會始終把畫面固定到拖移的中心……聽起來有點迷惑。但是我可以告訴你默認情況下這樣設置就可以保持玩家在拍攝的畫面中心。而它的另一個選項會讓攝像機固定在左上角。
由於它是玩家的子節點,因此攝像機會跟著玩家一起移動,現在跟拍的效果就有了,之前畫面外面的場景也能看到了:
如果你不想要這種跟拍效果,也可以讓它在移動到一定的程度再開始移動攝像機。攝像機節點的Drag一欄下還有進一步控制拖移的屬性可以設置。
排頭兩項是控制水平和垂直方向上的拖移啟用與否。主要看下面四個方向上的Margin(邊距)屬性。這個margin是一個0到1的值,它實際上意思大致是“要在離屏幕邊緣多遠才開始移動攝像機”。正如文檔所說“設置為1會使得攝像機在屏幕邊緣處才會移動”。例如我們啟用水平方向上的拖移,左右margin默認為0.2,由於攝像機綁在玩家身上,所以相當於說要左右走一點攝像機才會移動:
這樣一來就可以根據自己的設計來調整攝像機的相關屬性。當然為了方便觀察我這裡還是關閉拖移一直保持玩家在中心好了。
背景
畫面是可以拍到了,但是我們的背景如你所見穿幫了。之前的背景因為就是一個普通的Sprite2D所以沒有額外操作的話它就會一直杵在那裡。
這張背景屬於是遠景,我們可能希望它不會隨著攝像機移動而移動。當然有個笨辦法是你可以讓玩家揹著它跑。不過這裡實際上可以用CanvasLayer來簡單實現。
在場景中添加CanvasLayer節點,然後把之前的背景放到它下面,然後把它的Layer屬性調整成一個負值(保證它最小),這樣它就不會覆蓋其它節點:
其實現在啟動遊戲,你就會發現背景已經保持不動了。
那啥是CanvasLayer。CanvasLayer是容納CanvasItem的容器,一個視口(Viewport)中的場景樹可以存在若干CanvasLayer。
遊戲世界的攝像機作為一種觀察場景的界面時,它實際上只是一系列變換(transform,數學名詞)。攝像機的變換應用到場景上,給我們的感覺好像就是我們觀察的畫面移動了起來。
在畫面移動過程中,我們可能希望有些部分不受這種變換影響。比如此處的背景,比如還沒講到的UI。這個時候就可以把它們放到不同的CanvasLayer中。前面說CanvasLayer裡面是CanvasItem,那CanvasItem是啥呢,實際上我們已經大量地在使用它們了。所有從Node2D(包括它自己)派生出來的節點都是CanvasItem。
視差背景
如果你希望背景保持不動那上面這樣就可以了。但是如果希望實現更復雜的背景效果,特別是視差效果那還需要藉助ParallaxBackground。
ParallaxBackground也是CanvasLayer的一種。但是它有一個專屬的子節點ParallaxLayer。ParallaxLayer可以設置其下節點相對的移動(滾動,或者說卷軸的捲動,scroll)速度(Motion中的Scale屬性)。而一個ParallaxBackground可以有多個ParallaxLayer,為不同的ParallaxLayer設置不同的移動速度就可以達到遠中近景的效果。
例如我在背景前面加了一箇中景,然後讓中景的移動速度更快,這樣就可以達到這種效果:
重複
你可能已經發現,如果選擇使用視差背景,那麼我們的素材還是會穿幫,因為它只有那麼寬。縮放之後高度可以接受,但是寬度還是隻有那麼一點。
ParallaxLayer除了提供控制滾動速度的功能之外,還提供了一個Mirroring選項。老實說這個選項應該改名,雖然名字叫鏡像,但是它的功能實際上是複製、重複,甚至平鋪也比較合適。連文檔中也註釋到“和名字鏡像無關,只是重複”。
它的值是一個二維向量,指的是在水平和垂直方向上的何處開始重複繪製其中的內容。如果你的背景沒有縮放,那麼直接填入它的寬度你就可以看到它會緊接著背景在右邊又出現一個同樣的背景圖片。
如果你縮放過背景,這裡介紹一個好用的東西。和其它很多軟件一樣,在編輯器中輸入數字時可以直接輸入表達式。比如我之前把背景放大到1.5倍,那麼這裡我可以直接:
現在視口中的背景顯示成了這樣:
看起來背景只重複了兩次。但實際上在遊戲中我們會發現無論怎麼走(水平方向),我們都會看到背景無限重複。
為了節約資源,實際上這種無限重複的效果就是兩個重複的背景來回換實現的。計算機上很多無限重複的東西實際上都是幾個東西來回換造成了你覺得它有無限多個的假象!但是,很多東西都是這樣,只要看起來是這樣,那就是這樣!
無限的深淵
問題來了。之前我們為了在玩家落入深淵殺死玩家的KillArea,由於它只有畫面那麼寬,我們現在移動到更廣闊的世界中,就碰不到它了,掉下去就真的是無盡的深淵,死都死不了了。
聰明的你可能會想到,是不可以讓它也跟著背景那樣無限重複呢?理論上可以,實際上本來也應該可以(不過要記得調整ParallaxLayer垂直方向上的Motion Scale不為0):
可以看到KillArea本身也被複制了,但是碰撞並沒有發生。這似乎是這和這一系列問題中提到的bug相關的,並且目前沒有完全解決。所以,這條路至少暫時行不通。
好在,我們有更合適的東西可以用。
我們想要的就是有一個無限寬的Area2D來檢測玩家碰到了它。我們這裡實際上唯一需要修改的就是KillArea的CollisionShape的Shape而已。
還記得我們有很多種Shape可以選擇嗎?現在我們要用的是這個WorldBoundaryShape2D。顧名思義,它是用來表達常見的世界邊界。可能是碰到就會死的那種邊界,也可能是空氣牆。
修改之後我們看到,之前的矩形變成了這樣一個玩意兒:
一條水平線上面有一條垂直的線。雖然看起來這條水平線是有限的,但是實際上它只是便於查看,本身是無限的。直線在數學上就是無限長的!線段才是有限的。而垂直那條線,是這個WorldBoundayShape2D的法線——準確地說是法向量的示意。
點開剛剛新建的WorldBoundaryShape2D資源,我們可以看到Normal(法線)屬性。WorldBoundary實際上只有在一邊發生碰撞時才會發出相應的信號,它就像是隻有一邊一樣。法向量會向我們表明”哪面朝外“。默認的(0, -1)就是上方為法向。在2D空間中,直線的法線會和它垂直。而法向量會體現它的方向。當我們的玩家從上方碰到KillArea時就會被殺死。
由於我們只是改了一下CollisionShape,所以代碼完全不用改!回到主場景中,如果你嘗試了把KillArea放到了ParallaxLayer中先把他拖出來,如果調整了它的Scale也把它變回來。現在它已經是一個無限寬的KillArea了!
現在無論走多遠,落到下面時也會被殺死了!
這裡啟用了顯示CollisionShape,可以看到即使WorldBounday看起來很短但實際上是無限長