C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

UE4/C++ で lambda-expression なタスクをお手軽に非同期処理に投げる方法

// 最小限のコード例
// グラフの意味はさておき、単発のタスクを
// とりあえずなんでもよいのでUE4のタスクプールに投げたい場合はこれだけでも動作する
FFunctionGraphTask::CreateAndDispatchWhenReady( []{}, TStatId(), nullptr );
// 解説用に関連して扱う必要のある型が分かりやすいように少し分解したコード例
TFunction< void() > f0 = []{};
FGraphEventRef t0 = FFunctionGraphTask::CreateAndDispatchWhenReady( f, TStatId(), nullptr );

// 第3引数に nullptr ではなく前提条件とする FGraphEventRef を渡すとタスクグラフが構築される
// 第4引数に ENamedThreads を明示的に与えるとスレッドプール内のどのスレッドに実行させたいか、優先度をどうするかなど設定できる
TFunction< void() > f1 = []{};
ENamedThreads p1 = AnyBackgroundThreadNormalTask;
FGraphEventRef t1 = FFunctionGraphTask::CreateAndDispatchWhenReady( f, TStatId(), t0, p1 );

// メインスレッドで実行しないとクラッシュするような処理をさせたい場合には明示的に GameThread を指定しないと
// ありがちなスレッドとコンテキストによる「たまに落ちる」的なバグを埋め込む事があるので注意。
// 例: UProceduralMeshComponent の CreateMeshSection とか
TFunction< void() > f2 = []{};
ENamedThreads p2 = ENamedThreads::GameThread;
FGraphEventRef t2 = FFunctionGraphTask::CreateAndDispatchWhenReady( f, TStatId(), t1, p2 );

// 完了チェックしたい場合
if ( t2.IsComplete() )
  somthing();

ラムダ式UE4のタスクプールに非同期で投げる方法は簡単な非同期処理の実装にお手軽で嬉しい。( C++ 標準の promise / future / thread に対応する UE4 標準の TPromise / TFuture / FRunnableThread の使い方 - C++ ときどき ごはん、わりとてぃーぶれいく☆ での実装例のようにタスクにクラスを定義する必要が無い)

Note: ただし、このお手軽な方法は「それほど重くない処理」または「ゲームスレッド以外でのんびり実行してくれればよい処理」などでは便利に使えるが、「ゲームスレッドで実行する必要があり、1フレームに近いか、あるいは1フレームを超える処理時間を要する重い処理」や「ゲームスレッドで実行する必要があり、1つあたりは0.1msで処理できるがほぼ同時に1000タスク発生する処理」のようなタスクの非同期処理には実質的に使えない。タスクの粒度によらず前提条件さえ満たしていれば溜まっているタスクを Tick 1フレームで全てビッグバン実行してしまうため、メインスレッドや描画スレッドが想定するフレームレートをこなせなくなるようなタスクを投げたい場合には、フレームあたりのタスクの実行粒度を制御する工夫が必要となる・w・

参考

UE4/C++: UMaterialInstanceDynamic を生成したり UPrimitive 的な何かにセットしたりするコードスニペット的なメモ

// ぱたーん1: マテリアルインスタンスダイナミックを複数の何かのマテリアルとして使いまわしたい場合

// 1.1. AActor 系のどこかなどで、とりあえずマテリアルインスタンスダイナミックをマテリアルアセットから作る:
auto m = UMaterialInstanceDynamic::Create( Cast< UMaterial >( StaticLoadObject( UMaterial::StaticClass(), nullptr, TEXT( "/Game/MyDir/MyMaterial" ) ) ), this );
// 1.2. UPrimitiveComponent 系の何か(UStaticMeshComponent など)にマテリアルをセットする:
constexpr auto element_index = 0;
mesh->SetMaterial( element_index , m );
// ぱたーん2: 使い回さず1つの UPrimitiveComponent 系の何かで使う程度の場合

// 2.1. いきなり作れてセットもできる
auto m = mesh->CreateAndSetMaterialInstanceDynamicFromMaterial( 0, Cast< UMaterial >( StaticLoadObject( UMaterial::StaticClass(), nullptr, TEXT( "/Game/MyDir/MyMaterial" ) ) ) );
// 2.optional. この方法でも使い回せる
mesh->SetMaterial( 1, m );
// appendix: UPrimitiveComponent は GetMaterial メンバー関数も持っているので、マテリアルを保持しなくて良い程度の用途はこれでも実装できる
mesh->SetMaterial( 1, mesh->GetMaterial( 0 ) );

参考

UE4 Plugin: SVG Importer Plugin

概要

UE4SVG をテクスチャーで扱いたかったけれど、 svgpp など使ってちまちま実装するよりも $19.99 で SVG Importer Plugin を買った方が楽そうだし、マーケットプレイスでも ☆5 が幾つもついているので試してみようかな、と思い試してみた記録。

SVG Importer Plugin

試した

SVG Importer Plugin はエンジンプラグインなので、 Epic Game Launcher からエンジンにプラグインを install し、プロジェクトのプラグイン設定で有効化すると使用可能になる。

プラグインが有効なプロジェクトで UE4Editor が動作中に .svg をプロジェクトの Content ディレクトリーへ放り込む、或いは UE4Editor の UI から明示的に Import 操作を行うと SVG Import Options が出現する

f:id:USAGI-WRP:20180510161355p:plain

ここで重要なのは "Import Type" (RGBAの絵として読み込むか、ディスタンスフィールドとして読み込むか)と、 "Import Resolution"(後から変えられない取り込み時の解像度)の2つ。大きく拡大して表示されるRGBAテクスチャーにしたい場合は "Import Resolution" を 4096x4096 にしておくとよい。

↓インポートに成功するとSVGテクスチャーのアセットが生成される

f:id:USAGI-WRP:20180510162042p:plain

↓適当なマテリアルアセットを作成し、テクスチャーにインポートしたSVGテクスチャーアセットを設定

f:id:USAGI-WRP:20180510162140p:plain

↓プレビュー等も問題なく動作

f:id:USAGI-WRP:20180510162335p:plain

↓4096x4096でインポートしておけばRGBAテクスチャーで拡大されても綺麗(アタリマエじゃが😅)

f:id:USAGI-WRP:20180510162407p:plain

↓32x32など小さい解像度でインポートしてしまうとそれ以上にはどうにもならなくなる

f:id:USAGI-WRP:20180510162532p:plain

↓32x32はアライ

f:id:USAGI-WRP:20180510162613p:plain

解像度は 4096x4096 でインポートしておけば、SVGテクスチャーアセットの設定で任意に小さく書き出す事もできるので、インポート時は特に理由が無ければ最大解像度でインポートしておくと良さそう。テクスチャーの書き出しも各種圧縮形式も簡単に選択するだけで対応できて便利も好い。

SVGテクスチャーアセットの設定からフィルターを変更すればドット感の高い低解像度テクスチャーにもできる

f:id:USAGI-WRP:20180510163622p:plain

↓同じSVGを無理やりディスタンスフィールドとしてインポートしてディスプレイスメントマップしてみたもの(無理やりだけどたぶんインポート機能そのものは問題なく扱える。)

f:id:USAGI-WRP:20180510165705p:plain

黄色のクルマのSVGの出典

UE4-4.19 で Material Layers を試用する手順

概要

従来の UE4 では「マテリアルのレイヤーブレンド機能」=「マテリアル関数の合成機能」でした。4.19以降で試験実装された "Material Layers" を使うと「マテリアル関数」ではなく、新たなアセットとして追加される「マテリアルレイヤー」と「マテリアルレイヤーブレンド」を用いた新しいマテリアルのレイヤー合成を試用できます。と、いうわけで基礎的な使い方を確認しつつ試用してみます。

なお、この機能の利点は複雑なマテリアルを作成したい場合に、マテリアル1つで全てを実装しようとするとグラフが混沌となってしまう問題をレイヤーと合成それぞれを独立したアセットに分割する事で解消しつつ、従来のマテリアル関数を組み合わせる方法に比べてもマテリアルインスタンスでの一元的なパラメーターの取り扱いが可能となるなど便利も向上する、ということで試験実装が進められたようです。

実際、すっきりするし使いやすい。(この機能追加のために 4.19 からは MaterialInstance 系の実装が C++er 的にはしれっと変更されていて 4.18 までの C++ コードは少し変更してあげないと翻訳不能になっていたりとかあります。 Blueprint で本記事くらいの機能と動作の確認をした上で実装変更をドキュメントなど眺めると、なるほどそれでこうなったのか、とわかります。知らんと翻訳エラーの発生と対応方法に若干戸惑うかも。)

準備

  1. 4.19 以降のエンジンとプロジェクトを用意する
  2. UE4Editor のメニュー "Edit" から "Project Settings" の "Engine - Rendering" の "Experimental" にある "Support Material Layers" チェックボックスをON

f:id:USAGI-WRP:20180510090224p:plain

Note: OFF -> ON にすると UE4Editor の再起動を促されます。

試用

手順

2つのマテリアルレイヤー la lb と、1つのマテリアルレイヤーブレンド blend を使うマテリアル m を作成し、 mインスタンス i を適当な Static Mesh の Material に設定するまで:

  1. "Content Browser" の "Add New" から "Materials & Textures" -> "Material Layer" でマテリアルレイヤーアセット la を作る
  2. 同様に "Material Layer" アセットの lb も作る
  3. 同様に "Material Layer Blend" アセットの blend も作る
  4. 同様に "Material" アセット m も作る
  5. m の出力ノードの "Details" の "Material" の "Use Material Attributes" チェックボックスを ON にする
  6. m を "Content Browser" のコンテキストメニューから "Create Material Instance" してマテリアルインスタンスアセット i を作る
  7. 適当な "Static Mesh" なり "SK_Mannequin" などを配置して "Mesh" の "Material" に i を設定

動作確認として la lb blend それぞれに適当なパラメーターを噛ませた出力を実装:

  1. la lb に自動的に追加されている "SetMaterialAttributes" ノードの "Details" の "Material Attributes" の "Attribute Set Types" に設定したいマテリアル属性の項目を追加し、出現するピンへ "Texture Sample" や "VectorParameter" などのノードを追加してパラメーターを接合
  2. blend に自動的に追加されている "BlendMaterialAttributes" ノードに "ScalarParameter" ノードを追加して接合し、適当な既定値を設定
  3. m に "Material Attribute Layers" ノードを適当な名前で追加し、出力ノードへ接合
  4. m に追加した "Material Attribute Layers" ノードの "Details" の "Layers" の "Bacbkground" の "Layer Asset" に la を設定
  5. m に追加した "Material Attribute Layers" ノードの "Details" の "Layers" に "Layer 1" を追加し、 "Layer Asset" に lb を、 "Blend Asset" に blend をそれぞれ設定
  6. i の "Layer Parameters" に la, lb blend で追加した設定可能な "Parameters" 系のノードの値を適当に調整

動作確認:

  1. i を設定した "Mesh" を眺めるなりプレイするなりして la をベースに lbblend するマテリアルのインスタンスが意図通り動作する事を確認

補足図

↓ "Support Material Layers" を ON にしたプロジェクトでは作成できるアセットに "Material Layer" と "Material Layer Blend" が増える

f:id:USAGI-WRP:20180510095353p:plain

↓ "Material Layer" アセット作成直後のグラフ

f:id:USAGI-WRP:20180510100043p:plain

↓ "Material Layer Blend" アセット作成直後のグラフ

f:id:USAGI-WRP:20180510100436p:plain

↓ "Material" アセット m の "Details" の "Use Material Attributes" チェックボックス

f:id:USAGI-WRP:20180510101127p:plain

↓ "Material" アセット m に "Material Attribute Layer" ノードを追加し、 "Use Material Attributes" で変化した出力のピンと接合

f:id:USAGI-WRP:20180510101255p:plain

la に動作確認用に例として "TextureSampler2D" パラメーターノードを "SetMaterialAttributes" の "BaseColor" へ追加したグラフ

f:id:USAGI-WRP:20180510121802p:plain

lb に動作確認用に例として "VectorParameter" パラメーターノードを "SetMaterialAttributes" の "BaseColor" へ追加したグラフ

f:id:USAGI-WRP:20180510122102p:plain

blend に動作確認用に例として "ScalarParameter" パラメーターノードを "BlendMaterialAttributes" の "Alpha" へ追加したグラフ

f:id:USAGI-WRP:20180510122120p:plain

m に追加した "Material Attribute Layers" ノードの "Details" の "Layers" に la lb blend を設定

f:id:USAGI-WRP:20180510122223p:plain

i の "Layer Parameters" で la, lb, blend のパラメーターを調整

f:id:USAGI-WRP:20180510122357p:plain

↓ マネキンのマテリアルに i を設定した状態での動作確認

f:id:USAGI-WRP:20180510114251p:plain

参考

  1. Material Layering Feedback for 4.19 - Unreal Engine Forums
    • 試験実装中のマテリアルレイヤー機能の公式スレッド
  2. http://historia.co.jp/archives/955/
    • 従来から使用できるマテリアル関数ベースのマテリアルレイヤー合成機能の参考

UE4/C++: 4.18 以降発生するクリップボードのAPI仕様変更に伴う警告と必要な修正

状況

FGenericPlatformMisc::ClipboardCopy または FGenericPlatformMisc::ClipboardPaste を使用している場合、UE4-4.18 以降で API の仕様変更に伴う警告が翻訳時に発生する。

warning C4996: 'FGenericPlatformMisc::ClipboardCopy': FPlatformMisc::ClipboardCopy() has been superseded by FPlatformApplicationMisc::ClipboardCopy() Please update your code to the new API before upgrading to the next release, otherwise your project will no
longer compile.
warning C4996: 'FGenericPlatformMisc::ClipboardPaste': FPlatformMisc::ClipboardPaste() has been superseded by FPlatformApplicationMisc::ClipboardPaste() Please update your code to the new API before upgrading to the next release, otherwise your project will
no longer compile.

必要な修正

実装クラスが変更されただけではなく、変更先のヘッダーが CoreMinimal.h に含まれていない点、加えて実装クラスが提供されるモジュールも独立しているためモジュールの追加も必要となる点に注意。

  1. FGenericPlatformMiscFPlatformApplicationMisc に置き換える。
  2. HAL/PlatformApplicationMisc.h を include 追加。
  3. <project>.Build.csApplicationCore モジュールを追加。

something.cpp:

// ...
#include "HAL/PlatformApplicationMisc.h"
// ...
  // copy to clipboard
  FString hoge = TEXT( "hoge" );
  // old
  FGenericPlatformMisc::ClipboardCopy( *hoge );
  // new
  FPlatformApplicationMisc::ClipboardCopy( *hoge );
// ...
  // paste from clipboard
  FString hoge;
  // old
  FGenericPlatformMisc::ClipboardPaste( hoge );
  // new
  FPlatformApplicationMisc::ClipboardPaste( hoge );

<project>.Build.cs:

    PublicDependencyModuleNames.AddRange
      ( new string[]
        { // ...
        , "ApplicationCore"
        // ...
        }
      );

UE4/C++: 4.18 -> 4.19 エンジンアップデートトラブル; `UMaterialInstance::GetVectorParameterValue` の仕様変更と "Material Layers"

現行プロジェクトに必要なプラグインの 4.19 対応も進んできたので、手元のプロジェクトを 4.18 から 4.19 へエンジンアップデートしてみました。エンジンの仕様変更によるトラブルがあったので対処方法と併せて紹介します。

4.18 -> 4.19 における UMaterialInstance::GetVectorParameterValue の仕様変更

UMaterialInstance::GetVectorParameterValue の引数仕様が後方互換性なしで変更されていました。

Engine/Source/Runtime/Engine/Classes/Materials/MaterialInstance.h:

// 4.18
virtual ENGINE_API bool GetVectorParameterValue(FName ParameterName, FLinearColor& OutValue) const override;
// 4.19
virtual ENGINE_API bool GetVectorParameterValue(const FMaterialParameterInfo& ParameterInfo, FLinearColor& OutValue, bool bOveriddenOnly = false) const override;
  1. 第一引数が変更: FName ParameterName -> const FMaterialParameterInfo& ParameterInfo
  2. 第三引数が追加: bool bOveriddenOnly = false

FMaterialParameterInfo は 4.18 には存在しない 4.19 で追加された型で MaterialInstance.h と同じ場所に追加された MaterialLayersFunctions.h で定義されていた。

Engine/Source/Runtime/Engine/Classes/Materials/MaterialLayersFunctions.h:

// 本来のソースからメンバー変数と ctor を引用、日本語で概説を付けた。
USTRUCT(BlueprintType)
struct ENGINE_API FMaterialParameterInfo
{ GENERATED_USTRUCT_BODY()
  
  // 最低限これだけ与えればよい。 4.18 まで直接与えていた FName のパラメーターに相当
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=ParameterInfo)
  FName Name;
  
  // グローバルパラメーターか、あるいはレイヤーパラメーターまたはブレンドパラメーターかを設定。 4.19 で追加。
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=ParameterInfo)
  TEnumAsByte<EMaterialParameterAssociation> Association;
  
  // グローバルパラメーターの場合は INDEX_NONE, レイヤーパラメーターまたはブレンドパラメーターの場合は有効なインデックス値を設定する。 4.19 で追加。
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=ParameterInfo)
  int32 Index;
  
  // ctor pattern 1
  FMaterialParameterInfo(const TCHAR* InName, EMaterialParameterAssociation InAssociation = EMaterialParameterAssociation::GlobalParameter, int32 InIndex = INDEX_NONE);
  // ctor pattern 2
  FMaterialParameterInfo(FName InName = FName(), EMaterialParameterAssociation InAssociation = EMaterialParameterAssociation::GlobalParameter, int32 InIndex = INDEX_NONE);
  
  // ほかは省略

4.18 まで FName ParameterName として直接扱っていたパラメーターは 4.19 では FMaterialParameterInfo::Name に放り込んで扱うようだ。 4.18 まで第一引数を明示的に FName 型で与えていなかった場合には 4.18 から 4.19 へプロジェクトのエンジンアップデートを行う場合にこの絡みを使っている C++ ソースコードがあれば手書きの対応が必要となる。 第三引数については省略すれば INDEX_NONE がデフォルトパラメーターからセットされるため、さしあたり気にする事はない。

// 4.18 までこんな具合にコードしていた場合は
material->GetVectorParameterValue( "my_vector_param", buffer );
// 4.19 では最低限こんな具合で第一引数を明示的に FName 型で与えるようにすればコンパイル可能になる
material->GetVectorParameterValue( FName( "my_vector_param" ), buffer );

おまけ1: EMaterialParameterAssociation

UENUM()
enum EMaterialParameterAssociation
{
  LayerParameter,
  BlendParameter,
  GlobalParameter,
};

おまけ2: グローバルパラメーター、レイヤーパラメーター、ブレンドパラメーターとは何か。どうしてこうなった?

UE4 では 4.19 の新機能として "Material Layers" を実装した。

従来は1つ1つのマテリアルの単位で、その中でテクスチャーサンプリングしてパラメーターを合成したりするだけだったが、 この新機能により「複数のマテリアル群からなるマテリアル(="Material Layers")」を直接的にまとめて扱えるようになった。

この新機能に対応するため、従来はマテリアルごとに 4.19 からグローバルパラメーターと明示的に呼び分けられるようになったマテリアル内のシンボル名のほか、 「レイヤーのインデックス何番目の」という情報が必要な "Material Layers" タイプのマテリアルへ従来のマテリアルと統合的に対応するため 単なる FName だけのパラメーターから FMaterialParameterInfo への拡張が行われた。

"Material Layers" 参考

UE4/C++: JSON/XML/portable-binary 等の汎用シリアライズライブラリー cereal に UE4 の各種型を非侵入型アダプターで対応させるライブラリー cereal-UE4 を公開しました

cereal-UE4

GitHub - usagi/cereal-UE4: cereal ( C++ serialization library ) adapter for UE4 ( Unreal Engine ) types

cereal について

近年モダンな C++er にしばしば使われていると思われる JSON / XML / Binary ( platform native の endian に依存しない portable も可 ) に対応するシリアライザーのライブラリーです。ヘッダーオンリーライブラリーなので UE4 のプロジェクトへ取り込む際にも手間がほぼかかりません。

cereal-UE4 を作った経緯

  • UE4 標準の JSONリアライザーがしんどい。
    • FJsonSerializable 高レベル JSON シリアライズAPIhistoria - FJsonSerializebleマクロを使ってみる で紹介されているように使用可能な状況ではとても楽、便利だが…
      • 標準提供されている実装の範囲で対応している型が非常に限定的。例えば UE4 では頻出する FVectorFLinearColor などのシリアライズも対応できない。
      • FJsonSerializable を継承して型の対応を増やす事はできるが、わりと手間がかかる。
      • 継承の使用を前提とした侵入型シリアライザーなので UE4 の仕組み上 USTRUCT 型に適用できない(致命的にダメその1😅)。
      • 仮想関数を持つ FJsonSerializable の継承を前提とした設計のため、この方法でシリアライズ対応した型は仮想関数テーブルが必要となり非 POD 型(厳密には非標準レイアウト型)となるため、 TArray<FVector> のように連続でデータメンバー"だけ"がアライメントされ"密"に並んで欲しいデータ構造に適用できない(致命的にダメその2😅)
    • UE4/JSON/DOM 低レベルAPI はスマートポインターと参照が入り乱れた実装が必要となるため、手書きは複雑で疲れるし、コード量も大きくなり保守性も悪くなる。

と、言うわけで、 UE4JSONリアライザーは現状わりと残念なので、 UE4 の外の世界では C++er にわりと人気の高い(≃楽に使えてとても便利)な cereal を UE4 に対応する補助的なライブラリーを実装する事にしました。

cereal-UE4 の仕組み

cereal には大きく分けて侵入型と非侵入型の2つの方法で任意のユーザー定義型へのシリアライザー対応を追加できます。そこで、 cereal-UE4 では非侵入型で UE4 で使われる一般的な多くの型へ対応するシリアライザーをたくさん書いて、 cereal + cereal-UE4UE4 のプロジェクトへ追加すれば簡単に cereal による UE4 型に対応した JSON / XML / Binary のシリアライズが可能なよう実装しました。全ての実装は template 関数なので、実際に使用する型のシリアライザーのみフットプリントに影響します。

もちろん、 cereal-UE4 もヘッダーオンリーライブラリーです。 cereal と併せて簡単に UE4 プロジェクトへ導入できます。

使い方

UE4 のプロジェクトのディレクトリーの適当なところへ Thirdparty など適当なディレクトリーを用意し、

  1. cereal
  2. cereal-UE4

GitHub から clone なり zip なりで調達、配置します。

<MyProject>
|-Thirdparty
|  |-cereal
|  |-cereal-UE4
|- MyProject.uproject
|- ...

それから、 MyProject.Build.cs に、

using System.IO;

を冒頭に追加しつつ、

public class MyProject 内の public MyProject( ReadOnlyTargetRules Target ) コンストラクターの中に、

{
  var base_path = Path.GetDirectoryName( RulesCompiler.GetFileNameFromType( GetType() ) );
  string third_party_path = Path.Combine( base_path, "..", "..", "Thirdparty");
  PublicIncludePaths.Add( Path.Combine( third_party_path, "cereal", "include") );
  PublicIncludePaths.Add( Path.Combine( third_party_path, "cereal-UE4", "include") );
}

こんな具合で Thirdparty/cereal/includeThirdparty/cereal-UE4/includeUE4 プロジェクトの C++ コードファイルのコンパイルの際にインクルードパスに追加される設定を追加します。

あとは、ただの struct でも class でも USTRUCT でも UCLASS でも気にせず、シリアライズしたいクラス、例えば以下のような FMySomething 型に対して、

// これはあなたのお好きなように用意する
USTRUCT( BlueprintType ) struct FMySomething
{ GENERATED_BODY()
  UPROPERTY( BlueprintReadWrite ) FString String;
  UPROPERTY( BlueprintReadWrite ) FDateTime DateTime;
  UPROPERTY( BlueprintReadWrite ) FVector Vector;
  UPROPERTY( BlueprintReadWrite ) TArray< int32 > ArrayI32;
};
// お好きなように cereal の非侵入型シリアライザーを追加する。
// 型の定義の直後に .h へ書いてもいいし、シリアライズを特定の .cpp でしか行わないのならその .cpp へ書いてもいい
// cereal
#include "cereal/cereal.hpp"
#include "cereal/archives/json.hpp"
// cereal-UE4
#include "cereal-UE4.hxx"
// std::stringstream
#include <sstream>

template < typename A >
void serialize( A& a, FMySomething& in )
{
  a
  ( cereal::make_nvp( "String", in.String )
  , cereal::make_nvp( "DateTime", in.DateTime )
  , cereal::make_nvp( "Vector", in.Vector )
  , cereal::make_nvp( "ArrayI32", in.ArrayI32 )
  );
}
// これはどこか、 BeginPlay と EndPlay とか、お好きなタイミングでシリアライズして構わない。
// もちろん、 FMySomething はローカル変数でなくてもよいし、クラスのメンバーでもなんでもよい。
FMySomething MySomething;
MySomething.String = "Hello, こんにちは.";
MySomething.DateTime = FDateTime::UtcNow();
MySomething.Vector = FVector::UpVector();
MySomething.ArrayI32 = { 123, -456, 789 };

// JSON へシリアライズ
std::stringstream buffer;
{
  cereal::JSONOutputArchive a( buffer );
  a( cereal::make_nvp( "MySomething", MySomething ) );
}
const auto json = FString( buffer.str().data() );
UE_LOG( LogTemp, Log, TEXT( "%s" ), *json )

// おまけ: FString をファイルへ書き出したければこんな具合
if
( !   FFileHelper::SaveStringToFile
      ( json
      , *( FPaths::ProjectDir / TEXT( "MySomething.json" ) )
      , FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM
      )
)
{
  UE_LOG( LogTemp, Fatal, TEXT( "Save Error" ) );
}

これで次のような MySomething.json が書き出される

{
    "MySomething": {
        "String": "Hello, こんにちは.",
        "DateTime": "2018-03-29T17:59:50.595Z",
        "Vector": {
            "X": 0.0,
            "Y": 0.0,
            "Z": 1.0
        },
        "ArrayI32": [
            123,
            -456,
            789
        ]
    }
}

読み出し(デシリアライズ)したい場合は

FMySomething from_json;
std::stringstream buffer;
buffer << TCHAR_TO_UTF8( *json );
cereal::JSONInputArchive a( buffer );
a( from_json );

これで from_json には to_json と同じ中身が復元される。

おわりに

cereal-UE4 は MIT ライセンスの OSS として GitHub へ公開しました。 UE4/C++er が JSON / XML / Binary のシリアライザーについて、標準実装では不足、あるいは面倒そうな状況に対し、 cereal-UE4 が助けとなれば幸いです。対応する型や、すべての型での実装例はリポジトリーの README.md や example/ を参照して下さい。対応型の不足やバグ修正があれば PR 下さい。