目錄
- 藍圖
- 藍圖命名規范
- 藍圖優化
- 暴露C++至藍圖
- 暴露C++類
- 暴露C++屬性
- 暴露C++函式
- 暴露C++結構體/列舉
- 暴露C++介面
- 藍圖和C++的結合方案
- 使用繼承重寫藍圖
- 使用組合重寫藍圖
- 方案比較
- 參考
藍圖
大家都知道,藍圖是UE4提供的極其容易上手的一種可視化腳本,更具體的就不說了,
純靠藍圖搭建的UE4游戲是存在的,但是這類游戲往往優化很差(除非游戲玩法本身的性能需求不高),更合適的流程往往需要程式員撰寫C++代碼創建一些藍圖可用元素,而設計師再通過藍圖快速搭建游戲,
藍圖命名規范
藍圖命名:"BP"+類別縮寫+"_"+名字
例如: BPA_Player
| 藍圖類別 | 前綴 |
|---|---|
| 藍圖Actor | BPA_ |
| 藍圖結構 | BPS_ |
| 藍圖列舉 | BPE_ |
| 藍圖介面 | BPI_ |
| 藍圖函式庫 | BFL_ |
| 藍圖宏庫 | BML_ |
藍圖優化
在Project Settings -》 Packaging -》 Experimental 下面有個選項:Nativize Blueprint Assets

如果勾選這個選項,那么在打包時會將藍圖腳本編譯成C++代碼,
暴露C++至藍圖
C++中常常使用UE4中的一些宏來設定想要暴露于藍圖的類、屬性、方法等(實質內部是UE4的反射機制),
暴露C++類
一般使用UCLASS([specifiers])暴露類至藍圖,并添加頭檔案#include "XXX.generated.h",在類里第一行使用GENERATED_BODY(),
UCLASS([specifier, specifier, ...], [meta(key=value, key=value, ...)])
class ClassName : public ParentName
{
GENERATED_BODY()
}
常用[specifiers]引數:
- Blueprintable:將該類公開為可接受的用于創建藍圖的基類,默認值為不可藍圖化(NotBlueprintable),但以其他方式繼承時除外,這是由子類繼承的,
- BlueprintType:
將該類公開為可用于藍圖中的變數的型別, - NotBlueprintable:指定此類不是用于創建藍圖的可接受基類,否定指定了可藍圖化關鍵字的父類的效果,
至于meta(元資料說明符)引數,主要是規范(限制)用,相對沒有specifiers常用,此處不做多講,下文同理,感興趣可以參考具體官方檔案,
//例子:
#include "AMyActor.generated.h"
UCLASS(Blueprintable)
class AMyActor : public AActor {
GENERATED_BODY()
};
可參考:虛幻4檔案——游戲性類
暴露C++屬性
使用UPROPERTY([specifiers])宏暴露屬性至藍圖,
UPROPERTY([specifier, specifier, ...], [meta(key=value, key=value, ...)])
Type VariableName;
常用[specifier]引數:
- EditAnywhere:可通過“屬性”視窗在原型和實體上進行編輯,
- VisibleAnywhere:該屬性在“屬性”視窗中可見,但無法編輯,與EditAnywhere不兼容,
- BlueprintReadOnly:此屬性可以由藍圖讀取,但不能修改,
- BlueprintReadWrite:此屬性可以通過藍圖讀取或寫入,
- BlueprintAssignable:應公開屬性以在藍圖中分配,
- BlueprintCallable:應公開屬性以在藍圖圖表中呼叫,
- Category = “XXX”:給該屬性分類以便在虛幻編輯器中查詢,
此處注意EditAnyWhere和BlueprintReadWrite的區別,前者表示在虛幻編輯器中可以在“屬性”視窗中對該屬性值進行編輯,然而若需要在藍圖腳本編輯器中設定該屬性,則需要使用BlueprintReadWrite,相當于為該屬性自動添加了get和set方法,
//例子:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Character")
float health;
可參考:虛幻4檔案——屬性
暴露C++函式
使用UPROPERTY([specifiers])宏暴露屬性至藍圖,如:
UFUNCTION([specifier, specifier, ...], [meta(key=value, key=value, ...)])
ReturnType FunctionName([Parameter, Parameter, ...]);
常用[specifier]引數:
- BlueprintCallable:表示此函式可以直接在藍圖中執行,函式的實作只能在C++中進行,
- BlueprintImplementableEvent:該函式的具體實作只能在藍圖中進行,對于沒有回傳值的函式,可以當做一種事件來處理,不必有具體的實作,而對于有回傳值的函式,則需要在藍圖編輯器中的左邊欄查找該函式并進行覆寫,其呼叫還只能在C++原生代碼中進行,
- BlueprintNativeEvent:藍圖可以呼叫該函式,該函式的默認實作在C++中已經完成了,但是藍圖可以對該函式進行覆寫重寫,這個引數可以實作最靈活的函式呼叫,
注意:對于BlueprintNativeEvent函式,需要一些特殊處理:
- 要宣告一個新的虛函式,函式名為 FunctionName_Implementation;
- 對該函式的C++實作要轉而對該虛函式進行;
- 無論C++或者藍圖呼叫該函式時,都是直接使用函式的原名,
// 例子:
// 頭檔案
UFUNCTION(BlueprintNativeEvent)
void CountdownHasFinished();
virtual void CountdownHasFinished_Implementation();
// 源檔案
void ACountdown::CountdownHasFinished_Implementation() {
CountdownText->SetText(TEXT(“Go!”));
}
void ACountdown::BeginPlay() {
Super::BeginPlay();
CountdownHasFinished();
}
可參考:虛幻4檔案——函式
暴露C++結構體/列舉
結構體可包含變數,包括UProperty變數、函式和運算子,且只需用USTRUCT([specifiers])標記該結構體和內置一句GENERATED_USTRUCT_BODY(),無需繼承,
USTRUCT([Specifier, Specifier, ...])
struct StructName {
GENERATED_USTRUCT_BODY()
};
常用[specifier]引數:
- BlueprintType:表示結構體可以在藍圖中使用,
與UObject不同的是,UStruct不會被垃圾回收,必須自行管理其生命周期,UStruct應該是純傳統資料型別,包含UObject反射支持,可以在虛幻編輯器、藍圖操控、序列化、聯網等中編輯,
//例子:結構體
USTRUCT(BlueprintType)
struct FCharcterStatus {
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Character")
float health;
};
列舉則直接用UENUM([specifiers])標記,
//例子:列舉
UENUM(Meta = (Bitflags))
enum class EColorBits
{
ECB_Red,
ECB_Green,
ECB_Blue
};
可參考:虛幻4檔案——結構體
暴露C++介面
介面類有助于確保一組(可能)不相關的類實作一組通用函式,在某些游戲功能可能被大量復雜而不同的類共享的情況下,這非常有用,
例如,某個游戲可能有這樣一個系統,依靠該系統輸入一個觸發器體積可以激活陷阱、警告敵人或向玩家獎勵點數,這可以通過針對陷阱、敵人和點數獎勵器執行“ReactToTrigger”函式來實作,
然而,陷阱可能派生自“AActor”,敵人可能派生自專門的“APawn”或“ACharacter”子類,點數獎勵可能派生自“UDataAsset”,所有這些類都需要共享功能,但它們沒有除“UObject”之外的共同上級,在這種情況下,推薦使用介面,
UINTERFACE([specifier, specifier, ...], [meta(key=value, key=value, ...)])
class UClassName : public UInterface
{
GENERATED_BODY()
};
常用[specifier]引數:
- BlueprintType:表示介面可以在藍圖中使用,
//例子:
#include "ReactToTriggerInterface.generated.h"
UINTERFACE(Blueprintable)
class UReactToTriggerInterface : public UInterface
{
GENERATED_BODY()
};
可參考:虛幻4檔案——介面
藍圖和C++的結合方案
一個將藍圖和C++結合的常見方案是先大量使用藍圖制作專案,后續再用C++把復雜的藍圖重寫一遍,從而提升優化效果,
然而現在藍圖有Nativize Blueprint Assets的功能,可以將藍圖編譯成C++代碼,所以個人認為絕大部分藍圖并不需要重寫,直接Nativize Blueprint Assets即可,而對于某些包含復雜邏輯計算的藍圖,則才適合用C++來替代藍圖,
推薦看參考里面的[UE4]使用C++重寫藍圖,SpawnObject根據型別動態創建UObject博客來加深理解,
使用繼承重寫藍圖
-
創建一個C++類作為藍圖的父類(C++類繼承藍圖一樣的父類),在UE4中修改藍圖的父類,
-
用C++中實作好的方法逐個替換藍圖中方法,每次替換一個方法就必須要運行游戲進行詳細測驗,防止修改太多萬一出錯無法定位問題所在(盡量避免出現要同時替換2個以上藍圖方法才能正常運行游戲,這一點非常重要,同樣也是防止修改太多萬一出錯無法定位問題所在)
如下圖所示:保留原藍圖的實作,方便C++代碼查錯,

使用組合重寫藍圖
-
創建一個繼承自UObject的C++類,一般加后綴Helper,并且加上BlueprintType標簽,共藍圖作為變數型別使用,
-
在藍圖中添加一個名為MyHelper的變數,型別是第一步創建的C++型別,
-
在使用helper物件之前,必須先實體化,接著要初始化helper的成員變數值,其中當前藍圖物件的參考(也就是self)要傳遞給me引數,這是關鍵,用helper的成員物件保存起來,
-
最終用helper物件的C++方法一個一個替換原來用藍圖寫的功能,

方案比較
兩個方案都要注意的事項:
- C++類中的方法、成員變數與藍圖一一對應,并且方法和成員變數名稱不能與藍圖的重復,
- 在替換藍圖的過渡時期,代表相同含義的藍圖變數和C++類變數可能同時存在,那么給變數賦值時,應注意2個變數都要同時賦值,以保證藍圖方法和C++方法都能正常運行,
- A藍圖不能直接使用B藍圖的變數,A藍圖把要公開的變數封裝在函式內回傳,并且只回傳UE4自帶的基礎變數型別,不能回傳自定義型別,以方便C++重寫時回傳C++中的成員變數,
參考博客原文的結論是:使用繼承和組合都可以實作C++重寫藍圖,但是組合比繼承要更好,耦合度更低,
參考
虛幻引擎4 官方檔案 | 虛幻架構 創建和實作游戲性類的參考
[UE4]使用C++重寫藍圖,SpawnObject根據型別動態創建UObject
UE4初學筆記
系列其他文章:Aery的UE4 C++開發之旅系列文章
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/6746.html
標籤:其他
