Godot入門到棄坑:神筆馬良


3樓貓 發佈時間:2024-03-13 20:35:23 作者:cameLcAsE Language

在前面的文章中,我們的玩家角色已經大致完成,操作和動畫都可以正常工作。可惜我們場景中的地板和柱子還是用的非常粗糙的佔位素材,接下來我們就用素材包中的素材打造一個更像樣的場景。

添加背景

我們先添加一個背景圖片,現在先簡單地放一個Sprite2D然後把背景素材PNG/environment/back給它。注意,在2D場景中,默認情況下後加入場景樹的節點會顯示在更上面。這裡的“上面”指的是“離屏幕外面更近”。所以我們要把背景sprite放在最前面(編輯器場景樹從上往下的“上面”),保證它在最下面。
用場景視圖中的錨點對它進行縮放(最好是用四角上的錨點等比縮放),讓它覆蓋住表示遊戲視口邊緣的邊框為佳。
目前來說這個背景的位置一旦調整好,我們就不會那麼頻繁地去修改它。因此可以點擊一下這個鎖定按鈕防止無意中移動了它:
鎖定之後就無法在編輯器中被選中和移動。
最後可以直接刪掉之前的Platform場景節點。

Tile和TileMap

很多2D遊戲(不管是不是像素風格)都會用到一種很有歷史氣息、很簡單粗暴,但是很好用的方法來構造場景。它們會把場景中的很多東西視作是一些基本元素排列組合構造起來的。這些(通常)大小固定的小方塊被稱為tile,tile的本意是修房子時貼在地板、牆壁、屋頂上的那些各種材質的磚(或者瓦)。在遊戲引擎中一般會提供各種工具來處理含有tile的spritesheet,開發者可以直接在編輯器中用這些tile來繪製場景。
在Godot中,我們需要一個叫TileMap的節點來完成這樣的工作。為場景添加一個TileMap節點。注意其在節點樹的位置,讓它在背景上面,玩家下面。
TileMap需要一種叫TileSet的資源,由於我們可能會在多個場景中用到TileMap,並且它們都會重複用到一些TileSet,因此這裡我們不直接在檢視面板中新建(當然你也可以這樣做),而是直接在文件系統中新建TileSet便於後續複用。
簡單複習一下。在文件系統任意你喜歡的位置打開右鍵菜單選擇Create New(新建),然後選擇Resource(資源)。搜索找到TileSet並創建。名字可以叫他environment。然後我們就可以把它拖到場景中的TileMap節點的TileSet屬性欄中。
選中TileMap節點的時候,下方的面板中會出現兩個TileMap特有的面板,一個是TileSet,一個是TileMap。我們首先需要在TileSet面板中配置TileSet,它們會為TileMap提供可用的tile。
打開TileSet面板,點擊左側的加號,選擇Atlas(本意為地圖冊)選項,在彈出的窗口中選擇要用到的spritesheet。這裡選擇素材包中的PNG/layers/environment中的tileset。Godot會自動處理素材識別其中的tile,彈出的窗口直接選Yes。
此時一組新的tile已經加入tileset,右側可以看到切分好的各個tile。如果覺得這個面板太小不好操作,可以點擊右下方這個像“此面向上”的圖標展開面板:
上方的幾個按鈕可以對TileSet進行不同的操作。目前選中的是Setup(配置),是對整個atlas的基本設置。可以調整tile的大小和邊距等屬性。由於這個素材是比較規整的,所以無需額外調整。
Select(選擇)選項啟用後可以選中某一個tile進行一些屬性的調整。Paint(繪製)選項可以為各個tile繪製額外的屬性。我們在後面會用到。
現在TileMap面板中也出現了剛才添加的tileset。我們可以用這些tile來繪製場景了。默認情況下選中TileMap節點時場景中會出現以tile大小為格子大小的網格(grid)。要繪製場景,在上方選擇一個需要的繪製工具即可在格子上繪製。這些工具都是各種圖像編輯軟件中很常見的操作,這裡一句話介紹一下。
鉛筆圖標是最簡單直白的,選中一個tile,用鉛筆在格子上點一下就畫上去。右邊的線段和方框是繪製一條線和繪製一個矩形。油桶就是填充一個空白。滴管,可以選取已經繪製的tile然後用它繼續繪製。橡皮擦,嗯。順時針逆時針就是撤消重做。兩個箭頭一個是水平翻轉一個是垂直翻轉。骰子圖標是隨機放一個tile。
如果你不確定某個tile是啥東西,可以參考素材包中的environment-preview圖片。隨心所欲地繪製自己的場景吧。不過大概要畫一個類似平臺的東西便於後續內容的學習。

碰撞

現在啟動遊戲,之前在tilemap上繪製的tile並不會和玩家發生碰撞,玩家會直接掉下去。這是因為我們還沒有給tileset中的tile添加和物理碰撞相關的數據。選中TileMap節點,在檢視面板中點擊當前的TileSet,可以看到Physics Layers(物理層)部分目前為空。我們先了解一下什麼是物理層。

物理層

Godot中和涉及物理系統的節點一般都有一個用於控制碰撞檢測的層次的屬性。在不同節點中名字可能不一樣,但是都以這樣的形式在檢視面板中示人,比如選中Player場景中的CharacterBody2D節點打開Collision部分:
CharacterBody2D的這兩個屬性實際上是來自CollisionObject2D的`collision_layer`(碰撞層,下文簡稱layer或層)和`collision_mask`(碰撞掩碼,下文簡稱mask或掩碼)。
layer指的是該物件“所在”的層。mask指的是該物件要“掃描”(檢測)的層。文檔中總結地很好,引用之:
物件A可以檢測到和物件B發生接觸,當且僅當B位於任意一個A所檢測的層中。
無論是從文檔描述還是編輯器界面上都可以看出,無論是層還是掩碼,都可以選擇多個。一個東西可以位於多個層,也可以檢測來自多個層的其他東西。默認狀態下,layer和mask都設置在了1層。之前我們用來撞的柱子默認的layer和mask也是1,因此肯定會被玩家檢測到而發生配置。
例如這樣的場景中。玩家的layer和mask都是默認的1。第一個柱子layer為2,mask為1,但是玩家角色的mask為1,所以它掃描不到這個柱子。碰撞沒有發生。第二個柱子layer為1,mask為空,但是玩家的layer為1,因此還是沒有碰撞。第三個柱子,mask為2,但是layer為1,所以被玩家掃描到了,發生了碰撞。
在Area2D中由於需要發生重疊,這種層次設置可能會更加有用。我們可以設置層次讓某個東西可以檢測到指定的一類東西進入了範圍中。

位運算光速入門

如果查看文檔,我們會發現layer和mask的類型都是int,但是為什麼編輯器中有32個層呢?
實際上涉及層的操作時都會按照位運算(bitwise operation)來進行操作。Godot中的int類型是64位有符號整數。這裡的“64位”不是說它的最大值是“99(中間省略60個9)99”,而是說它的二進制表示最多可以有64位。它的取值範圍是`-2^63`到`2^63 - 1`,即`-9223372036854775808` 到`9223372036854775807`。
我們平時用的十進制,怎麼把一個數用二進制表示呢?簡單起見我們只用比較小的正整數來舉例。以十進制數11為例,它是什麼意思呢?它實際上是1x10¹+1x10⁰。任意非0數的零次冪就是1。也就是說十進制數的每一位上的數字,就代表幾個10的n-1次冪,n表示它從右往左數在第幾位(1開始)上。
按照類似的道理,二進制數11的意思是就是說1x2¹+1x2⁰,對應的十進制數就是3。
十進制數11等於是8+0+2+1,也就是1x2³+0x2²+1x2¹+1x2⁰,二進制就從左往右按照每一位上的因數寫作1011。
在GDScript中,整數字面量除了可以按照我們日常習慣的十進制來書寫,也可以用二進制格式來書寫。二進制格式的整數字面量以0b開頭:
assert是什麼函數你可能忘了,它是所謂斷言函數,它的參數是一個布爾類型的表達式,其值為false時會直接報錯中斷遊戲運行。
位運算最終要在二進制表示的數上操作,由於二進制只有1和0兩個數字,所以它的位運算和布爾運算非常類似。布爾運算的與、或、非、反在位運算中也存在:
注意位運算符和布爾運算符的區別。Godot建議在布爾運算中建議使用英文單詞and、or、not,但是它也支持在其他編程語言中常用的&&、||、!。這些運算在位運算中類似的運算使用的是&、|、~。
按位或(|),相當於每個二進制位上的數相加(不發生進位),也就是說兩者中任一一個為1結果為1,同時為1也位1。
按位與(&),相當於每個二進制位上相乘,任一一個為0結果也為0。兩者都是1才為1。
按位取反(~),每一位都把1變成0,把0變成1。~0為啥結果為-1呢?說白了它就是一個64位都為1的二進制數。計算機中的整數是用補碼錶示的。簡單來說就是,負數的最高位為1,這一位代表-(2^63),後續位正常計算。當然如果要人來算一個負數的二進制表示要用到一些簡便算法,那就是“取反加一”。
例如十進制+2,二進制表示為0b10,取反得(62個1)01。加1得(62個1)10(進一位,不是按位與運算),取反得到(前面62個1)01:
注意“加一”和“按位與”的區別,按位與不會出現進位,但是算術加法會產生進位。
這樣一來就可以理解這個layer和mask了。它們會按照其值的後(低)32位來進行操作,編輯器中某個編號的層是否點亮就是對應某個位的1和0。
那掩碼為啥叫掩碼(mask),說白了就是要把它和另外一個值重合上去、覆蓋上去進行比對(還記得冒險小虎隊那個解密卡嗎)。當一個東西要和另一個東西碰上時,要發生什麼事,就是拿另一個東西的mask來和這個東西的layer進行按位與,對上的地方依然是1,如果一個位都沒對上,那麼結果就是0,也就不需要進行後續的操作了!
實際上Godot(以及其他各種遊戲引擎)中都會用類似的原理來處理各種層,並且不只是物理系統的交互。

小知識:關於bit的中文說法

bit在(簡體)中文中一般有兩種常用說法。一種就是“位”,另一種是“比特”,繁體中文一般稱“位元”。比特毫無疑問是音譯,比如辦寬帶給你說“100兆寬帶”指的是100Mbps(百萬比特每秒),這裡的b就是比特。注意這裡的b和B是不一樣的。一般來說B指的是Byte(字節),一字節為8比特。
不過我們談論二進制算術的時候一般都是說“位”。

繪製TileSet屬性

言歸正傳。TileSet可以定義多個物理層,然後將其作為數據附加到各個tile上(這個過程也被視作繪製)。點擊tileset資源後選擇Physics Layers欄目,點擊Add Element新建一個物理層,layer和mask默認為1就好。
接下來就可以給具體的tile繪製物理層屬性了。在TileSet面板中,啟用Paint來繪製屬性。點擊Select a property editor選擇一種屬性編輯器。這裡我們選擇Physics Layer 0,這就是我們剛才新建的物理層。然後點擊我們想要附加屬性的tile,就能夠給它加上物理層屬性了。繪製後的tile會覆蓋上一層綠(藍)色:
這個和CollisionShape2D(準確地說是CollisionPolygon2D)類似,定義了一個檢測碰撞的形狀。繪製工具默認會給tile畫上一個四邊形覆蓋整個tile。對於一些複雜的形狀你也可以自行編輯多邊形。
這裡的圖標分別用於添加頂點、編輯頂點、刪除頂點。編輯完形狀之後可以給tile畫上更復雜的碰撞形狀。如果怕畫得不準可以啟用吸附:
經過編輯,我可以給這樣的tile畫上更貼合的形狀:
被畫上Physics Layer屬性的tile就相當於位於對應物理層,擁有對應的layer和mask屬性。
繪製完成之後,再次啟動遊戲,我們的玩家角色就可以站在這些tile上了。

圖案

在一個平臺跳躍遊戲中,類似的平臺必然會重複出現多次。如果我們每次都要重複選擇這幾個tile然後挨個畫就會很麻煩。
在Godot中可以把一系列tile組合成一個圖案(pattern)。如你所見pattern要翻譯成中文不止一個意思。前面提到的模式匹配的模式也叫pattern。找規律的規律也叫pattern。
由於之前你可能使用了繪製工具在場景中繪製,因此要選擇已繪製的tile就要切換到選擇工具:
選中的tile會顯示為藍色邊框框住。不過我們的背景也是淺色的不太清楚。選擇時可以拖動選擇多個。選中後按下Ctrl+C複製,然後在Patterns標籤頁下面粘貼過去。
這樣一來我們就可以直接把圖案放到TileMap上了。選中Pattern然後選中需要的繪製工具就可以複用圖案了。

地形

平臺跳躍遊戲中的這種平臺往往是長短不一的。圖案可以用,但是每個圖案都是固定的,沒法調整長度
Godot的解決方案是地形(terrain)。它可以配置相互關聯的tile,讓它們能夠根據周圍的情況自動調整。
先來看個簡單的例子。這樣的平臺是由三個不同的tile構成。最左側,中間,最右側。要加長它,左右兩端的tile不需要變化,只需要把中間的tile多重複幾次即可。
為了避免每次都手動繪製,我們創建一個新的地形。地形數據是TileSet的一個屬性,選中TileMap的TileSet,我們可以看到TerrainSet(地形集合)部分。TerrainSet由多個Terrain組成。一個TileSet可以有多個TerrainSet。
新建TerrainSet後再新建一個Terrain:
地形的Mode(模式)有三種選項。默認是“匹配側面和角落”,另外兩個選項是隻匹配側面和只匹配角落。模式會決定一個地形中tile依照哪裡的情況來調整週圍八個格子中的tile。目前默認也沒有問題。
而Terrain Sets中的各個地形有兩個屬性可以調整,一個是名字一個是顏色。顏色建議調整成泥巴色以外的顏色,因為素材中的很多東西就是泥巴色的。
我們需要為TileSet中的各個tile設置它屬於哪個地形。找到代表平臺的這三個tile。我們準備把它們全部設為同一種地形中的tile,因此可以把它們全部選中然後批量編輯。此處我們在Select和Paint模式下都可以操作。
在Select模式下選中這三個tile,左側面板中找到Terrain欄目後就可以選擇Terrain Set和其下的具體Terrain。不過這裡地形集合相關的數據顯示為數字。-1表示“沒有設置”。上面提到TileSet的Terrain Sets是一系列Terrain Set,各set中有一系列Terrain。這裡的數字就是各元素的索引。我們目前只有一個地形集合,其中也只有一個地形,所以都調整為0就表示將它們設置為了對應地形中的tile:
如果你已經跟著我像這樣設置了它們的所屬地形數據,那麼現在切換到Paint模式就可以看到它們身上有個數字顯示它們屬於0號地形。
如果使用繪製模式,在左側選中Terrains屬性,然後選擇地形集合和地形即可像繪製物理碰撞形狀那樣把地形數據一個一個點給它們。

設置地形匹配位

只是設置tile所屬地形還沒法讓它們自動調整。只有在設置好tile的“地形匹配位”(terrain peering bits)之後它們才能自動調整。
地形匹配位會決定一個tile在其周圍八個格子處於何種狀態時才會出現。儘管稱作“位”,但實際上對應位置上的匹配位是一個數字,它對應的是地形編號。說起來有點抽象,我們實際來看一下。
地形匹配位也可以在Select和Paint兩種模式下設置,不過在繪製模式中要直觀一些。
在繪製模式中,將鼠標指針放到已經設置了所屬地形的tile上面會出現用於指示匹配位的小方塊。左鍵單擊可以設置匹配位,右鍵可以取消:
可以設置的匹配位會根據地形集合的匹配模式不同而不同。“僅角落”和“僅側面”模式會只有四個可以設置的地方,分別對應四個角和四個側面:
現在把這三個tile的匹配位設置為如圖所示的樣子:
左端tile僅設置右側,中間設置左右兩側,右端設置左側。
那麼這是什麼意思呢?點亮的匹配位意味著要這個方向出現了對應地形中的tile,該tile才能出現。以最左端的tile為例,由於它是最左端的tile,是起始tile,它必須要只有右側有tile時才能出現,而中間的tile要左右兩側有tile時才能出現。右端tile同理。
接下來實際看一下地形如何工作。選中TileMap面板,選擇Terrains標籤頁。可以看到左側已經出現了Terrain0(如果你修改了地形名稱,這裡在索引0左側會顯示為名稱)。選中它,右側出現了其中包含的tile:
如果選擇單個tile進行繪製,實際上和之前的繪製方式沒啥區別。但是要注意到右側像“格鬥遊戲出招表中的搖桿方向”和“雙頭貪吃蛇”一樣的兩個圖標。
“搖桿”圖標是連接繪製工具,它會工具所選繪製位置周圍的tile來確定要繪製哪個tile並調整週圍的tile。貪吃蛇是路徑繪製工具,會根據“筆觸”連接路徑上繪製的tile。接下來具體來看。
我們先用路徑模式繪製平臺。現在我們只需要選中一個格子,然後橫向拉一條線出來,平臺的左端、中間、右端就自動調整好了:
連接模式對於這個簡單的、橫向的平臺來說用處不大,不過也可以演示一下:
可以看到首先放置了一個tile,啟用連接模式後,選中空的格子時會根據周圍情況自動繪製符合條件的tile。在後面繪製右側tile時也會自動調整左側的tile。
怎麼樣,是不是很好玩。接下來看一個更復雜的例子。
雖然這幾個tile貌似不是這麼用的,但是用來演示還是很合適。它們大致可以組成一個方框形狀的地形,中間是泥巴填起來的。這樣設置匹配位:
我們就可以隨便拖一個矩形區域出來:
設置tile的地形屬性時,可以給它的匹配位設置為和它自身不同的地形。另外在Select模式中也可以手動填寫:
這裡有對應八個方向的條目。其中的值是一個整數,對應地形編號。
Godot 4中的TileMap的地形功能實際上和3.x中類似的auto tiling功能有很大的差異,有些地方甚至顯得不如之前靈活。比如由於匹配位是一個“位”,導致它沒法匹配多種地形中的tile,而一個tile也沒法設置屬於多種地形。相關缺失的功能需要插件來補充,但插件的使用不在本文的介紹範圍內,有興趣可以自行研究。關於地形和TileMap的相關內容也可以參考文檔
本篇文章中我們學習瞭如何用素材結合TileMap節點來繪製場景,也學會了如何調整各tile的屬性讓它們跟易於使用。動手繪製一個自己的場景吧!



© 2022 3樓貓 下載APP 站點地圖 廣告合作:asmrly666@gmail.com