UE4 BPのネイティブ化を試してみる

# 概要
UnrealEngine のブループリントネイティブ化を試した時のメモ書きです。


## 修正履歴
| 日付 | 内容 |
|:-----|:-------|
|2020/03/10| ネイティブ化フラグC++定義について追記 |


## 環境
Windows10
Visual Studio 2017
UnrealEngine 4.24

## 参考
以下を参考にさせて頂きました、ありがとうございます。

[ブループリントのネイティブ化](https://docs.unrealengine.com/ja/Engine/Blueprints/TechnicalGuide/NativizingBlueprints/index.html)
[SEGA TECH BLOG - C++化も試してみる。](http://techblog.sega.jp/entry/2018/11/26/100000#C%E5%8C%96%E3%82%82%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)


# 設定手順

## プロジェクト設定
[プロジェクト設定] -> [パッケージング] -> [Blueprints]
の [Blueprint Nativization Method] を [Inclusive]か[Exclusive]にする。

![ProjectSetting.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/1bdc34b6-7b27-75ed-06dc-d419d7a8b051.jpeg)

[Inclusive]はBP全部、[Exclusive]はネイティブ化対象のBPを指定できるようです。

### Native化対象ファイルの指定
[Exclusive]を指定した場合、どのBPをネイティブ化するかの指定が必要です。
プロジェクト設定の[List of assets to native]にてファイルを指定するか、対象のBPのクラス設定の[ネイティブ化]にチェックを入れる必要があります。

▼アクターの場合
![BPSetting_Actor.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/db7b9f17-5673-1c55-4f47-049df0665831.jpeg)

▼UMGの場合
![BPSetting_UMG.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/814d3919-23f8-c8ab-bad7-ed044b5b53b1.jpeg)

これで基本的には設定完了です。

## ネイティブ化されたソース
ネイティブ化されたソースファイルは、
Intermediate\Plugins\NativizedAssets\[プラットフォーム名]\Game\Source\NativizedAssets
以下に作成されるようです。

インクルードが大量に書かれたソースが見えます。
機械的な変換のためコードを読むのはキツイです。

```cpp:ネイティブ化されたソース例
#include "NativizedAssets.h"
#include "DebugWidgetBP__pf3061712538.h"
#include "GeneratedCodeHelpers.h"
#include "Runtime/UMG/Public/Blueprint/WidgetTree.h"
#include "Runtime/UMG/Public/Components/CanvasPanel.h"
#include "Runtime/UMG/Public/Components/CanvasPanelSlot.h"
#include "Runtime/UMG/Public/Components/Border.h"
#include "Runtime/UMG/Public/Components/BorderSlot.h"
#include "Runtime/UMG/Public/Components/VerticalBox.h"
#include "Runtime/UMG/Public/Components/VerticalBoxSlot.h"
#include "Runtime/UMG/Public/Components/HorizontalBox.h"
#include "Runtime/UMG/Public/Components/HorizontalBoxSlot.h"
#include "Runtime/UMG/Public/Components/TextBlock.h"
#include "Runtime/Engine/Classes/Engine/Font.h"
#include "Runtime/UMG/Public/Components/ProgressBar.h"
#include "Runtime/UMG/Public/Components/Button.h"
#include "Runtime/UMG/Public/Components/RichTextBlock.h"
#include "Runtime/Engine/Classes/Engine/DataTable.h"

// ...以下省略
```


# トラブルシューティング
いくつか問題に遭遇したのでその時のメモ書きです。

## UMGをネイティブ化した場合のワーニングについて
UMGでのネイティブ化で以下のようなワーニングが出る場合があります。

```text:OutputLog
LogBlueprint: Warning: [Compiler UI_MyTest] Nativization and Fast Widget Creation is not supported at this time.
LogBlueprint: Warning: [Compiler UI_MyTest] [2941.39] Compile of UI_Crystal successful, but with 1 Warning(s) [in 4 ms] (/Game/UI_MyTest.UI_MyTest)
```

UMGでのクラス設定 [Widget Blueprint Options]の[Force Slow Construction Path]にチェックを入れると消えます。

![UMG_Option.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/f05d33f8-9aef-85da-d1b4-5e9002dbb08a.jpeg)

このチェックを入れるとWidgetの初期化時のツリー参照が遅くなる？ようで、生成タイミングが遅くなります。C++へのネイティブ化での高速化とのトレードオフになるようです。

### Force Slow Construction Path での注意点
Widgetに複数のWidgetを参照する場合にて、[Force Slow Construction Path]にチェックのオン/オフされているwidgetが混じっているような場合、早く生成されるwidgetと遅く生成されるwidgetが混じり生成タイミングのズレが起こってしまいます。
その時に早く生成されるwidgetが遅く生成されるwidgetを参照してしまうとまだ生成されていないオブジェクトにアクセスしてしまい、ストップすることがありますので注意が必要です。


## C++アクセス子でのエラーについて
C++で実装されているメソッドを呼ぶようなBPをネイティブ化した場合、そのメソッドがprotectedだった場合、当たり前ですがコンパイルエラーになります。

以下、プラグインでエラーがでた一例：

![plugin_gameplayabilities.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/309bf1bb-b2ae-d033-bf1a-cb1800ec1b03.jpeg)

GameplayAbilitiesプラグインを使った時、[EndAbility]というメソッドをBPから呼び出していますが、プラグインのC++実装をみると protected で実装されています。
この場合、プラグインソースを修正するか、該当するクラスを継承して呼び出し用のメソッドを実装するなどして回避する必要があります。


```cpp:GameplayAbility.h
UCLASS(Blueprintable)
class GAMEPLAYABILITIES_API UGameplayAbility : public UObject, public IGameplayTaskOwnerInterface
{
// 省略

protected:

/** Call from kismet to end the ability naturally */
	UFUNCTION(BlueprintCallable, Category = Ability, DisplayName="EndAbility", meta=(ScriptName = "EndAbility"))
	virtual void K2_EndAbility();

};
```

# その他
## ネイティブ化フラグのC++定義
ブループリントネイティブ化に絡みそうな設定は以下の場所に定義されています。チェック用コードを書く際には参考にすると良いと思います。

▼ネイティブ化フラグ
"Engine/Source/Runtime/Engine/Classes/Engine/Blueprint.h"

```cpp:Blueprint.h
	// 排他的ネイティブ化が有効になっている場合、このアセットはネイティブ化されます。 すべてのスーパークラスもネイティブ化する必要があります。
	UPROPERTY(transient)
	EBlueprintNativizationFlag NativizationFlag;
```

▼ForceSlowConstructionPathフラグ
"Engine/Source/Editor/UMGEditor/Public/WidgetBlueprint.h"

```cpp:WidgetBlueprint.h
	UPROPERTY(EditAnywhere, AdvancedDisplay, Category=WidgetBlueprintOptions, AssetRegistrySearchable)
	bool bForceSlowConstructionPath;
```



# まとめ
ネイティブ化した場合の速度について検証しきれていませんが、BPが遅いケース（参照が多い、ループが多い等）では効果は発揮されるはずです。
ただし、パッケージ化した場合の処理時間はC++への変換時間分、伸びてしまうようです。
実行速度が欲しい場合は試してみる価値はありそうですが、いろいろ落とし穴もあり悩ましいところです。