目錄html
你們都知道,藍圖是UE4提供的極其容易上手的一種可視化腳本,更具體的就不說了。
純靠藍圖搭建的UE4遊戲是存在的,可是這類遊戲每每優化不好(除非遊戲玩法自己的性能需求不高)。更合適的流程每每須要程序員編寫C++代碼建立一些藍圖可用元素,而設計師再經過藍圖快速搭建遊戲。程序員
藍圖命名:"BP"+類別縮寫+"_"+名字架構
例如: BPA_Player編輯器
藍圖類別 | 前綴 |
---|---|
藍圖Actor | BPA_ |
藍圖結構 | BPS_ |
藍圖枚舉 | BPE_ |
藍圖接口 | BPI_ |
藍圖函數庫 | BFL_ |
藍圖宏庫 | BML_ |
在Project Settings -》 Packaging -》 Experimental 下面有個選項:Nativize Blueprint Assets函數
若是勾選這個選項,那麼在打包時會將藍圖腳本編譯成C++代碼。性能
C++中經常使用UE4中的一些宏來設置想要暴露於藍圖的類、屬性、方法等(實質內部是UE4的反射機制)。測試
通常使用UCLASS([specifiers])暴露類至藍圖,並添加頭文件#include "XXX.generated.h",在類裏第一行使用GENERATED_BODY()。優化
UCLASS([specifier, specifier, ...], [meta(key=value, key=value, ...)]) class ClassName : public ParentName { GENERATED_BODY() }
經常使用[specifiers]參數:.net
至於meta(元數聽說明符)參數,主要是規範(限制)用,相對沒有specifiers經常使用。此處不作多講,下文同理,感興趣能夠參考具體官方文檔。設計
//例子: #include "AMyActor.generated.h" UCLASS(Blueprintable) class AMyActor : public AActor { GENERATED_BODY() };
使用UPROPERTY([specifiers])宏暴露屬性至藍圖。
UPROPERTY([specifier, specifier, ...], [meta(key=value, key=value, ...)]) Type VariableName;
經常使用[specifier]參數:
此處注意EditAnyWhere和BlueprintReadWrite的區別,前者表示在虛幻編輯器中能夠在「屬性」窗口中對該屬性值進行編輯。然而若須要在藍圖腳本編輯器中設置該屬性,則須要使用BlueprintReadWrite,至關於爲該屬性自動添加了get和set方法。
//例子: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Character") float health;
使用UPROPERTY([specifiers])宏暴露屬性至藍圖,如:
UFUNCTION([specifier, specifier, ...], [meta(key=value, key=value, ...)]) ReturnType FunctionName([Parameter, Parameter, ...]);
經常使用[specifier]參數:
注意:對於BlueprintNativeEvent函數,須要一些特殊處理:
// 例子: // 頭文件 UFUNCTION(BlueprintNativeEvent) void CountdownHasFinished(); virtual void CountdownHasFinished_Implementation(); // 源文件 void ACountdown::CountdownHasFinished_Implementation() { CountdownText->SetText(TEXT(「Go!」)); } void ACountdown::BeginPlay() { Super::BeginPlay(); CountdownHasFinished(); }
結構體可包含變量,包括UProperty變量、函數和運算符,且只需用USTRUCT([specifiers])標記該結構體和內置一句GENERATED_USTRUCT_BODY(),無需繼承。
USTRUCT([Specifier, Specifier, ...]) struct StructName { GENERATED_USTRUCT_BODY() };
經常使用[specifier]參數:
與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 };
接口類有助於確保一組(可能)不相關的類實現一組通用函數。在某些遊戲功能可能被大量複雜而不一樣的類共享的狀況下,這很是有用。
例如,某個遊戲可能有這樣一個系統,依靠該系統輸入一個觸發器體積能夠激活陷阱、警告敵人或向玩家獎勵點數。這能夠經過針對陷阱、敵人和點數獎勵器執行「ReactToTrigger」函數來實現。
然而,陷阱可能派生自「AActor」,敵人可能派生自專門的「APawn」或「ACharacter」子類,點數獎勵可能派生自「UDataAsset」。全部這些類都須要共享功能,但它們沒有除「UObject」以外的共同上級。在這種狀況下,推薦使用接口。
UINTERFACE([specifier, specifier, ...], [meta(key=value, key=value, ...)]) class UClassName : public UInterface { GENERATED_BODY() };
經常使用[specifier]參數:
//例子: #include "ReactToTriggerInterface.generated.h" UINTERFACE(Blueprintable) class UReactToTriggerInterface : public UInterface { GENERATED_BODY() };
一個將藍圖和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 官方文檔 | 中文文檔 | 虛幻架構 建立和實現遊戲性類的參考
虛幻引擎4 官方文檔 | 英文文檔 | 虛幻架構 建立和實現遊戲性類的參考
[UE4]使用C++重寫藍圖,SpawnObject根據類型動態建立UObject
系列其餘文章:Aery的UE4 C++開發之旅系列文章