產生怪物

在這一部分中,我們將沿著一條路徑隨機刷怪。在最後,怪物們就會在遊戲區域到處亂跑了。

image0

按兩下*檔案系統*停靠面板中的 Main.tscn 打開 Main 場景。

在繪製路徑之前,我們要修改遊戲的解析度。我們的遊戲預設的視窗大小是 1152x648。我們要把它設成 720x540,一個小巧的方塊。

選擇 [專案] -> [專案設定] 來打開這個對話框。現在就試試吧。

image1

如果您還開著「輸入對應」,請切換到「通用」分頁。

在左側選單中,向下找到 顯示 -> 視窗。在右側,將 寬度 設定為 720,並將 高度 設定為 540

image2

建立動畫

與 2D 遊戲教學中所做的一樣,你要設計一條路徑,使用 PathFollow3D 節點在路徑上隨機取位置。

不過在 3D 中,路徑繪製起來會有一點複雜。我們希望它是圍繞著遊戲視圖的,這樣怪物就會在螢幕外出現。但繪製的路徑也同樣不會在相機預覽中出現。

為了確認視野的邊界,我們可以使用一些暫用網格。你的檢視區應該還是分成上下兩部分,下方是相機預覽。如果不是,請按 Ctrl + 2`(macOS 上為 :kbd:`Cmd + 2)將視圖分割為兩個。選取 Camera3D 節點,並勾選下方檢視區的 預覽(Preview) 方框。

image3

預留位置型

讓我們來新增一些占位網格。為 Main 節點新建一個 Node3D 節點作為子項,命名為 Cylinders。我們會用它將圓柱體進行群組。新增一個 MeshInstance3D 節點作為其子項

image4

在「屬性檢視器」中,將一個「圓柱網格」(CylinderMesh) 指定給「網格」(Mesh) 屬性。

image5

使用視口左上角的功能表,將上面的視口設為正交頂視圖。或者你也可以按小鍵盤的 7。

image6

地面柵格可能有一點分散注意力。你可以在工具列的*視圖*功能表中點擊*查看柵格*進行開關。

image7

你現在要沿著地平面移動圓柱體,看底部視口的相機預覽。我推薦使用網格捕捉來做這件事。你可以通過點擊工具列上的磁鐵圖示或按 Y 鍵來切換。

image8

將圓柱體移到相機視圖的左上角,使其正好在視野之外。

image9

我們將建立網格的副本,並將它們放置在遊戲區域周圍。按 Ctrl + D`(在 macOS 上則為 :kbd:`Cmd + D)來複製節點。你也可以在*場景*面板中右擊節點,選擇*製作副本*。沿著藍色 Z 軸向下移動副本,直到它正好在相機的預覽範圍之外。

按住 Shift 鍵選擇兩個圓柱體,並點擊未選擇的那個圓柱體,然後複製它們。

image10

拖拽紅色的 X 軸,將它們移動到右側。

image11

白色的有點難以看清是吧?讓我們給它們一個全新的材質,讓它們凸顯出來。

在 3D 中,材質可以定義表面的外觀屬性,比如顏色、如何反射光照等。我們可以用材質來修改網格的顏色。

我們可以同時更新所有四個圓柱體。在*場景*面板中選中所有網格實例。要實作全選,可以先點擊第一個,然後按住 Shift 點擊最後一個。

image12

屬性面板 中,展開 Material 區段,並將 StandardMaterial3D 指定給 0 號插槽。

image13

../../_images/standard_material.webp

點擊球體圖示來打開材質資源。你會看到材質的預覽和一長串充滿屬性的部分。你可以用這些來建立各種表面,從金屬到岩石或水。

展開「Albedo」區塊。

../../_images/albedo_section.webp

將顏色設為與背景色存在對比的顏色,比如亮橙色。

image14

我們現在可以使用圓柱體作為參考。點擊它們旁邊的灰箭頭,將它們折疊在*場景*面板中。你也可以通過點擊 Cylinders 旁邊的眼睛圖示來切換它們的可見性。

image1

新增一個 Path3D 節點作為 Main 的子節點。在工具列中會出現四個圖示。點擊*新增點*工具,即帶有綠色“+”號的圖示。

image1

備註

滑鼠懸停在任意圖示上,就可以看到描述該工具的工具提示。

按一下每個圓柱體的中心以建立一個點。然後,按一下工具列中的*閉合曲線*圖示以關閉路徑。如果有任何一點偏離,您可以按一下並拖動它以重新定位它。

image1

你的路徑應該會像這樣。

image1

為了在路徑上取樣隨機位置,我們需要一個 PathFollow3D 節點。將一個 PathFollow3D 新增為 Path3D 的子節點。將這兩個節點分別重新命名為 SpawnLocationSpawnPath 。這樣更能清楚描述它們的用途。

image1

這樣,我們就可以著手編寫刷怪機制了。

產生怪物

Main 節點上按右鍵,然後附加一個新的腳本。

我們首先將一個變數匯出到*屬性檢視器*中,這樣我們就可以把 mob.tscn 或者其他任何怪物賦值給它。

extends Node

@export var mob_scene: PackedScene

我們希望以固定的時間間隔生成生物。為此,我們需要返回場景中並新增計時器。但是,在此之前,我們需要將 mob.tscn 檔分配給 mob_scene 屬性

回到 3D 螢幕,選中 Main 節點。將 mob.tscn 從*檔案系統*面板拖到*屬性檢視器*的 Mob Scene 槽中。

image2

Timer ,命名為 MessageTimer

image2

屬性檢視器 中,將它的 等待時間 設定為 0.5 秒,並開啟 自動啟動,這樣當我們執行遊戲時它就會自動開始。

image2

計時器在每次到達 Wait Time 時都會發出 timeout 訊號。計時器預設會自動重啟,迴圈觸發訊號。我們可以將 Main 節點連接到這個訊號,每 0.5 秒生成一隻怪物。

保持選中 MobTimer,在右側的*節點*面板中按兩下 timeout 訊號。

image2

將它連接到 Main 節點。

image2

這樣你就會被帶回腳本,其中新建了一個空的 _on_mob_timer_timeout() 函式。

讓我們來編寫刷怪的邏輯吧。我們要做的是:

  1. 實體化並建立新的房間。

  2. 在生成路徑上隨機選取一個位置。

  3. 獲取玩家的位置。

  4. 呼叫小怪的 initialize() 方法,傳入隨機位置和玩家的位置。

  5. 將腳本附加到節點。

func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on the SpawnPath.
    # We store the reference to the SpawnLocation node.
    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    # And give it a random offset.
    mob_spawn_location.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

正如上面所示, randf () 會生成一個介於 01 之間的隨機值,這個數值是 PathFollow 節點的 progress_ratio 屬性所期望的:0 代表路徑的開始點,1 代表路徑的終點。 我們之前設定的路徑是圍繞著相機視口的,因此任何 0 到 1 之間的隨機值都代表著沿著視口邊緣的隨機位置!

請注意,如果您從主要場景中移除 Player 節點,那麼接下來這行

var player_position = $Player.position

會產生錯誤,因為沒有名為 $Player 的節點!

這是目前完整的 main.gd 腳本,僅供參考。

extends Node

@export var mob_scene: PackedScene


func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on the SpawnPath.
    # We store the reference to the SpawnLocation node.
    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    # And give it a random offset.
    mob_spawn_location.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

F6 即可測試該場景。你應該會看到有怪物刷了出來,然後會進行直線運動。

image2

目前,它們會在路線的交叉點撞到一起滑來滑去。我們會在下一部分解決這個問題。