原文:Unreal Engine 4 Blueprints Tutorial
作者:Tommy Tran
譯者:Shuchang Liu
在本篇教程里,你將學會如何用藍圖系統創建玩家角色,設置輸入,並編寫角色通過觸碰,收集道具的游戲邏輯。
藍圖是Unreal Engine 4的一套可視化腳本系統,通過藍圖可以快速制作游戲原型。你不再需要一行行的編寫代碼,取而代之的是可視化操作:拖拽節點,在UI里設置節點參數,給節點進行連線。
除了作為一款非常便捷的原型工具,藍圖也降低了非開發人員制作游戲邏輯的門檻。
在本篇教程,你將使用藍圖來:
- 設置垂直視角攝像機
- 創建具備基本移動的玩家控制器角色
- 設置玩家輸入
- 創建可被角色觸碰並收集的道具
注意:本篇教程假定你已了解Unreal Engine 4的基本操作界面。你也應該熟悉基本的藍圖概念,比如組件和節點。如果你需要復習以上內容,點擊入門教程。
注意:本篇教程只是Unreal Engine 4系列教程的其中一篇:
起步入門
下載示例項目並解壓。進入項目文件夾,雙擊BananaCollector.uproject打開項目。
注意:如果你看到了項目是由較早的引擎版本創建的提示,這很正常(因為引擎經常更新版本)。你可以選擇以拷貝副本的形式打開,也可以直接轉換項目版本打開。
你可以看到以下的場景。我們創建的角色會在場景內移動並收集道具。

為了方便起見,我已經給項目文件做了文件夾分類,如下圖所示:

你可以點擊紅框處按鈕來顯示/隱藏側邊欄列表。
創建玩家
點擊進入Content Browser界面的Blueprints文件夾。點擊Add New按鈕並選擇Blueprint Class。
由於玩家需要能夠獲取輸入,Pawn類比較適合。在彈出窗口選擇創建Pawn,將其命名為BP_Player。

注意:選擇Character類創建也可以,它還默認引入了移動組件。不過,既然我們想要自己動手實現移動,Pawn類已經足夠了。
關聯攝像機
攝像機是玩家觀察游戲世界的方式。我們要創建一個垂直視角觀察玩家角色的攝像機。
在Content Browser 雙擊BP_Player打開藍圖編輯器。
為了創建攝像機,在Components面板點擊Add Component並選擇Camera。

為了讓攝像機視角自頂向下,我們需要先把它放在玩家角色上方。選中攝像機組件,再切換到Viewport頁簽。
按下W鍵激活移動操作桿,將攝像機移動到(-1100, 0, 2000)。或者,你可以在Location字段輸入坐標點來移動攝像機。

如果攝像機被移出界面外,按下F鍵就可以重新定位看到攝像機。
接着,按下E鍵激活旋轉操作桿,將攝像機沿Y軸旋轉-60度。

玩家表示
我們用一個紅色方塊來表示玩家,所以需要使用Static Mesh組件進行展示。
首先,在Components面板左鍵點擊空白處取消選中Camera組件。否則,新加的組件會成為攝像機的子節點。
點擊Add Component並選擇Static Mesh。

為了展示紅色方塊,選擇Static Mesh組件,隨后在Details頁簽Static Mesh屬性點擊下拉框,選擇SM_Cube。

你在關卡場景里應該就能看到如下畫面(如果看不到,你可以在Viewport窗口按下F鍵聚焦畫面)

現在,是時候在關卡里動態生成玩家了。點擊Compile按鈕並回到主編輯器。
生成玩家
為了讓玩家能夠控制角色,你需要明確兩件事:
- 玩家所要控制的Pawn類
- 角色應該在哪里生成
你可以通過創建Game Mode類(游戲模式類)來實現第一點。
創建游戲模式
游戲模式是一個類,用於控制玩家進入游戲的方式。比如,在多人游戲里,可以使用游戲模式,決定每個玩家在哪里生成。更重要的是,游戲模式決定了玩家使用哪個角色。
在Content Browser里點擊進入Blueprints文件夾。點擊Add New按鈕並選擇Blueprint Class。
從彈出窗口里選擇Game Mode Base,並命名為GM_Tutorial。

現在,你需要指定哪個Pawn作為默認類。雙擊GM_Tutorial打開藍圖編輯器。
留意Details面板的Classes部分。點擊Default Pawn Class屬性的下拉框,並選擇BP_Player。

其次,關卡還需要知道當前使用哪個游戲模式。你可以在World Settings里指明這一點。點擊Compile並關閉藍圖編輯器。
每個關卡都有自己的設置。你可以通過Window\World Settings查看設置。同樣地,也可以在Toolbar選擇Settings\World Settings進行查看。

Details頁簽旁邊就會出現World Settings頁簽。點擊該頁簽的GameMode Override字段,選擇GM_Tutorial。

現在可以看到Game Mode類已經修改成GM_Tutorial。

最后,我們還需要指定玩家的生成位置。通過在關卡里放置Player Start(玩家出生點)即可。
放置玩家出生點
在生成玩家的過程中,游戲模式會尋找關卡中是否有玩家出生點。如果有,游戲模式就會在該點生成玩家。
為了放置玩家出生點,在Modes面板搜索Player Start。左鍵拖拽 Player Start至Viewport面板。
你可以隨意放置玩家出生點。放好后,點擊Toolbar的Play按鈕。玩家就會在出生點生成。

如果想要退出游戲,點擊Toolbar的Stop按鈕或按下Esc鍵皆可。如果你看不到鼠標指針,按下Shift+F1。
如果玩家不能控制移動,那其實算不上游戲,對吧?我們的下個任務就是設置輸入控制。
設置輸入
將某個按鍵指定成觸發某個行為,稱之為按鍵綁定。
在Unreal里,你可以通過按鍵觸發event(事件)的方式來實現按鍵綁定。事件節點是一類當接收到特定行為時,觸發執行的節點(比如在這個例子里,當你按下綁定按鍵時,觸發執行某個事件節點)。當某個事件被觸發時,任何跟事件節點連接的節點就會被執行。

這種按鍵綁定的方式非常有好處,因為這意味着你不需要硬編碼按鍵對應的邏輯。
比如,你綁定了鼠標左鍵並命名為Shoot事件。任何有射擊能力的Actor都可以使用Shoot事件,用於檢測玩家何時按下了鼠標左鍵。如果你想修改射擊對應綁定的按鍵,只要在輸入設置里修改即可。

如果以硬編碼方式實現,你需要遍歷每個Actor,分別修改射擊對應綁定的按鍵。
軸映射和操作映射
在Edit\Project Settings里可以看到輸入設置。點擊Engine部分的Input選項進入設置界面。

Bindings就是用於設置輸入的部分。

Unreal提供了兩類按鍵綁定方法:
- Action Mapping(操作映射):這類綁定只有兩種狀態:按下或者沒按下。行為只會在按下或釋放按鍵的時候觸發。這類綁定適用於沒有中間狀態的行為,比如槍械射擊。
- Axis Mapping(軸映射):這類綁定輸出一個稱之為Axis Value(下文有更多介紹)的數字。每幀都會觸發對應的軸事件。這類綁定通常用於搖桿或者鼠標。
在這篇教程里,我們會使用Axis Mapping。
創建移動映射
首先,你需要創建兩組Axis Mapping。這樣就可以實現多按鍵觸發同一個事件。
為了創建一組新的Axis Mapping,點擊Axis Mappings右側的+號。創建兩組Axis Mapping,分別命名為MoveForward和MoveRight。

MoveForward負責前后移動。MoveRight負責左右移動。
我們需要把移動映射到四個按鍵上:W,A,S和D。現在只有兩個插槽用於映射按鍵。通過點擊每組映射文本框右側的+號,添加映射插槽。

接着點擊每組插槽的下拉框,將W和S鍵映射到MoveForward,A和D鍵映射到MoveRight。

接着,我們需要設置Scale字段。
Axis Value和Input Scale
在你設置Scale字段前,我們需要了解它是怎么跟axis values搭配起作用的。
Axis Value的輸出數值取決於輸入類型和使用方式。按鈕點擊會輸出1。搖桿根據推動方向和力度,輸出-1~1之間的數值。

你可以使用Axis Value控制角色的移動速度。比如,如果你把搖桿推到了最邊上,Axis Value是1。如果你只推一半,Axis Value就是0.5。
將Axis Value跟某個速度變量進行相乘,你就能夠調整搖桿的速度。

你也可以通過Axis Value判斷出方向。如果你將角色速度與一個Axis Value正數相乘,得到的就是正數偏移。角色速度與一個Axis Value負數相乘,得到的就是負數偏移。將偏移值與角色位置相加,就能知道角色往哪個方向移動了。

由於鍵盤按鍵只能輸出0或1的Axis Value,你可以使用Scale來將其轉換成負數。通過Axis Value和Scale值相乘就可以做到這點。
如果正數(Axis Value)和負數(Scale)相乘,按鍵輸出就是負數。
因此我們點擊修改按鍵S和A的Scale字段文本為-1。

接着,有趣的部分來了:讓角色動起來!關閉項目設置面板,雙擊BP_Player打開藍圖編輯器。
角色移動
首先,你需要獲取移動映射的事件。在事件圖表空白處點擊右鍵打開節點列表。從彈出菜單中搜索MoveForward。添加處於Axis Events下的MoveForward節點。注意我們要添加的是Axis Events下的紅色節點,而非Axis Values下的綠色節點。
添加MoveRight節點的步驟同上。

現在,我們需要設置下MoveForward節點。
使用變量
為了實現移動,我們需要指定角色的運動速度。最簡單的方法是通過變量保存移動速度。
要新建變量,我們點擊My Blueprint頁簽Variable部分的+號。

選中新建的變量,觀察Details頁簽。將變量名重命名為MaxSpeed。隨后,點擊Variable Type的下拉框選擇Float,將變量類型修改為Float。

接着,需要給變量設置默認值。在這之前,先點擊Toolbar上的Compile按鈕。
保持變量選中,在Details頁簽的Default Value區域,將MaxSpeed的默認值設為10。

接着,將MaxSpeed變量從My Blueprint頁簽拖拽至Event Graph。並選擇彈出菜單的Get。

我們現在需要將MaxSpeed與Axis Value相乘來得出最終移動速度和方向。添加float * float節點,並連接Axis Value和MaxSpeed。

獲取角色朝向
為了向前移動,首先需要知道角色的朝向。幸運地是,Unreal里有這樣的節點。我們需要添加Get Actor Forward Vector節點。

接着,添加Add Movement Input節點。該節點通過獲取方向和數值,將其轉換成移動偏移值。我們按下圖連接各個節點:

白線代表節點執行順序。換言之,玩家進行操作,觸發相應事件並執行InputAxis MoveForward節點,隨后執行Add Movement Input節點。
Add Movement Input節點需要如下輸入:
- Target:默認為self,在這里就是玩家角色自己(紅色方塊)。
- World Direction:目標移動方向,在這里就是玩家角色的朝向。
- Scale Value:玩家移動距離,在這里為Max Speed * Axis Value(值范圍為-1~1)。
MoveRight事件的設置步驟同上,除了要把Get Actor Forward Vector節點替換為Get Actor Right Vector節點。試着不對照前面的操作指引,自己動手看看!

添加移動偏移
為了讓角色真正動起來,我們還需要將Add Movement Input節點計算得出的偏移值,累加到角色當前位置上。
基本上我們的策略就是讓游戲角色每幀移動一點點,所以我們需要在Event Tick事件添加運動節點,每幀進行調用。
現在看看Event Graph有沒有Event Tick節點,它應該處於灰化狀態,如果沒有就創建對應節點。

為了獲取偏移值,創建Consume Movement Input Vector節點。要累加偏移值到玩家角色上,還需要添加AddActorLocalOffset節點。隨后如下圖連接節點:

這樣意味着在游戲每幀,你會獲取角色的移動輸入,並將其累加在角色當前位置上。
點擊Compile,回到主編輯器並點擊Play。你就能操控紅色方塊四處移動了!

這里有個小問題。高配機器有可能運行幀率更高。由於Event Tick事件每幀調用,那同等時間內運動節點執行的次數更多。導致的結果就是高配機器角色移動得更快,反之亦然。
為了避免這個問題,角色運動必須是幀率無關的。
注意:我已經做了按鍵綁定用於展示幀率有關會產生的影響。按下數字鍵0將幀率調成60,按下數字鍵1將重置幀率。控制角色四處移動,看看不同幀率對移動速度的影響。
幀率無關
幀率無關意味着不管幀率是多少,相同時間的游戲運行,會產生完全一致的結果。幸運地是,在Unreal里要做到這點非常容易。
退出游戲,並打開BP_Player。接着,觀察Event Tick節點的Delta Seconds字段。

Delta Seconds為當前幀的Event Tick調用與上一幀Event Tick調用的時間間隔。通過將偏移值與Delta Seconds相乘,角色運動就是幀率無關的。
比如,你的角色最高速度是100。如果距離上一幀Event Tick已經過了1秒,則角色移動100個單元。如果只過了0.5秒,則角色運動50個單元。

如果角色運動是幀率相關的,那就會產生不過幀率高低,每幀固定移動100單元的結果。
為了將偏移值與Delta Seconds相乘,添加vector * float節點。之后,如下圖進行連線:

由於每幀間隔時間很短,角色移動起來會很慢,所以可以將MaxSpeed的默認值調大至600。

至此恭喜你,成功解鎖了幀率無關成就!

你可能注意到了,方塊現在會穿透其他物體。為了解決這個問題,我們需要用上碰撞檢測。
角色碰撞
為了能夠互相碰撞,角色需要一個代表其碰撞區域(通常稱之為碰撞體)的東西。你可以使用以下任一種:
- Collision mesh(碰撞網格):這個通常在網格導入時就會生成(如果你勾選了允許)。用戶也可以用3D軟件創建自定義碰撞網格。紅色方塊導入時就自動生成了碰撞網格。
- Collision component(碰撞組件):一般由三種形狀:盒子,膠囊體和球體。你可以通過Components面板進行添加。通常用於簡單碰撞。
下面就是一個人物和其碰撞體的例子。

當一個碰撞體碰到了另個碰撞體,碰撞就產生了。

設置碰撞
你可能會奇怪,方塊有碰撞體,卻沒有檢測到碰撞。當你移動方塊時,Unreal只認為方塊的根組件可產生碰撞。由於方塊的根組件並沒有任何碰撞體,所以就穿透了其他物體。
注意:一個root組件沒有碰撞體的角色,同樣可以擋住其他角色。但如果你移動這個角色,它是不會與任何物體產生碰撞的。
所以,為了使用碰撞網格,StaticMesh必須作為根組件存在。我們通過在Components面板左鍵拖拽StaticMesh至DefaultSceneRoot。釋放鼠標,StaticMesh現在就成了根組件了。

最后還要做一件事情。切換到Event Graph頁簽,將AddActorLocalOffset節點的Sweep輸入勾選為true。

簡單來講,AddActorLocalOffset會將角色從舊位置瞬移到新位置上。Sweep可以確保舊位置和新位置之間的物體都能與角色進行碰撞檢測。
回到主編輯器並點擊Play。現在方塊能與關卡場景產生碰撞了!

創建道具
任何東西都能成為供玩家觸碰拾取的道具。這里我們使用BP_Banana作為道具。
為了檢測方塊是否碰到了道具,我們需要一個在產生碰撞時,觸發執行的事件節點。我們可以使用碰撞響應來生成事件。
碰撞響應也決定了角色之間的碰撞后行為。有以下三類碰撞響應:Ignore,Overlap和Block。以下是它們互相之間的作用結果:

雖然可以使用Overlap或Block的任一種,本篇教程只展示如何使用Overlap。
設置碰撞響應
退出游戲並打開BP_Banana。選擇StaticMesh組件並觀察Details面板,Collision部分可以設置碰撞響應。

如圖所示,大部分設置都是灰置的。為了編輯它們,左鍵點擊Collision Presets的下拉框,選擇Custom選項。

現在,你需要指定道具和方塊之間的碰撞響應。
組件有一項屬性稱之為對象類型。對象類型是一種給角色進行簡單分類的方法。你可以在這里了解更多關於對象類型的內容。
由於方塊的類型是WorldDynamic,我們需要修改該類型對象的碰撞響應。在Collision Responses設置下,左鍵點擊WorldDynamic中間的勾選框,將碰撞響應改成Overlap。

碰撞處理
為了獲取碰撞情況並進行處理,我們需要用到overlap事件。在Components面板右鍵點擊StaticMesh。從彈出菜單中,選擇Add Event\Add OnComponentBeginOverlap。

Event Graph界面就會出現OnComponentBeginOverlap (StaticMesh)節點。

最后,添加DestroyActor節點,並連接OnComponentBeginOverlap (StaticMesh)節點。顧名思義,該節點會將目標角色從游戲中移除。由於我們沒有顯式指定Target,所以它會銷毀調用該節點的角色。

放置道具
關閉藍圖編輯器,並確保Content Browser打開了Blueprints文件夾。
通過左鍵拖拽 BP_Banana至Viewport界面來在關卡放置香蕉道具。

點擊Play按鈕,開始收集香蕉吧!

后續學習
你可以在這里下載完整項目。
你現在距離成為Unreal Engine專家又近了一步了。希望這篇教程沒有難倒你。
如果你還想繼續學習Unreal Engine 4引擎,點擊下篇教程,我會進一步講解Unreal Engine 4引擎的材質系統。