UnrealEngineをWebSocketのClientにする

わ、前回更新から1年以上経ってしまった....

はじめに

UnrealEngineと書いたのは、UE4・UE5どちらでも大丈夫だと思うため。
というわけで、Go製のWebServerに対してUnrealEngineからWebSocketを経由で通信するサンプルを作りました。

github.com

これの解説を行います。(今回はUE5でいきます)

完成イメージ

プラグインにGameInstanceSubsystemを継承したSubsystemを作成し、その中でWebSocketをServerへつなげて、Serverからメッセージを受け取ります。
それをEventDispatcherにしたので、以下の画像のように使うことができます。

解説

WebServer

github.com

github.com/gorilla/websocketのcommand exampleを参考に書きました。
ここの解説は上記のリンクに譲ろうと思います。

UnrealEngine

まず初めに、.Build.csファイルの依存モジュールにWebSocketsを追加します。

PublicDependencyModuleNames.AddRange(
  new string[]
  {
    "Core",
    // ... add other public dependencies that you statically link with here ...
    "WebSockets", // 例えばこんな感じ
  }
);

続いてSubsystemを作成します。 とりあえずは常時存在してほしいので、GameInstanceSubsystemを継承したクラスで作成します。

headerの完成形はこちら

UnrealWebSock_Sample/WebsocketSampleSubsystem.h at master · crssnky/UnrealWebSock_Sample · GitHub

一部解説していきます。
9行目:IWebSocketを前方宣言します。
11行目:このプラグイン専用のLogカテゴリを宣言します。 12行目:メッセージを受信した際に発火するイベントを宣言します。 22,33行目:UGameInstanceSubsystemのInit, Deinit関数をOverrideします。
25,26行目:最後に受け取ったメッセージを取得する関数を宣言します。
29,30行目:BlueprintからBindできる、メッセージを受信した際に発火するイベントオブジェクトを宣言します。
33~36行目:ソケットオブジェクトのためのイベント関数を宣言します。
38行目:ソケットオブジェクトを宣言します。
39行目:最後に受け取ったメッセージを保持する。


sourceの完成形はこちら

UnrealWebSock_Sample/WebsocketSampleSubsystem.cpp at master · crssnky/UnrealWebSock_Sample · GitHub

void UWebsocketSampleSubsystem::Initialize(FSubsystemCollectionBase& Collection)

Socketオブジェクトを作成し、各種イベントに関数をバインドしていきます。   オブジェクト作成には、WebServerのURIやサブプロトコルwsを入れます。
(本来ならばサブプロトコルはちゃんとするべきですが、テスト用でサーバー側に処理がなく、以下のようなエラー文を経由するため、何かしらを入れておく必要があります)

UE_LOG(LogWinHttp, Warning, TEXT("Attempted to create a WinHttp WebSocket with an empty protocols list"));

設定が終わったら、最後に接続します。

void UWebsocketSampleSubsystem::Deinitialize()

GameInstanceが消える前に、Socketを切断してメモリ領域を開放します。

FString UWebsocketSampleSubsystem::CurrentMessage() const

最後に受け取ったメッセージを取得する関数です。

void UWebsocketSampleSubsystem::OnSocketConnected() const

Socketが接続したときに呼ばれる関数です。
とりあえず確認用に、ログを吐きます。

void UWebsocketSampleSubsystem::OnSocketConnectioinError(const FString& err) const

Socketの接続に失敗したときに呼ばれる関数です。
とりあえず確認用に、Warningレベルで理由を出力します。

void UWebsocketSampleSubsystem::OnSocketClosed(const int statusCode, const FString& reason, const bool wasClean) const

Socketが切断したときに呼ばれる関数です。 とりあえず確認用に、ログを吐きます。

void UWebsocketSampleSubsystem::OnSocketMessageReceived(const FString& msg)

Socketがメッセージを受信したときに呼ばれる関数です。
受け取ったメッセージを保持し、そのメッセージをイベント発行します。

おわりに

本当に、簡単にWebsocketを繋げてメッセージを受信することができました。
ミニマムで言えば、InitializeDeinitializeOnSocketMessageReceivedのみを実装するだけで良いので、本当に簡単です。
何かしらのWebサービスと繋げることも難しくありません。何かの可視化ツールとしても使ってみるのはいかがでしょうか?

Rust製pastelコマンドにimas色機能を追加する

はじめに

Rust製pastelコマンドとは以下のやつです。

github.com

(gifはリポジトリから拝借)

pastel is a command-line tool to generate, analyze, convert and manipulate colors. It supports many different color formats and color spaces like RGB (sRGB), HSL, CIELAB, CIELCh as well as ANSI 8-bit and 24-bit representations.

つまり、CLI上で色に関することをいろいろできるということですね。
その"いろいろ"を実現するために、様々なサブコマンドがあります。
そこにimasを追加して、アイドルやユニットのイメージカラーを取得できるようにしてみました。

pastel 0.8.1
A command-line tool to generate, analyze, convert and manipulate colors

USAGE:
    pastel [OPTIONS] <SUBCOMMAND>

OPTIONS:
    -m, --color-mode <mode>              Specify the terminal color mode: 24bit, 8bit, off, *auto*
    -f, --force-color                    Alias for --mode=24bit
        --color-picker <color-picker>
            Use a specific tool to pick the colors [possible values: gpick, xcolor,
            grabc, colorpicker, chameleon, kcolorchooser]
    -h, --help                           Prints help information
    -V, --version                        Prints version information

SUBCOMMANDS:
    color         Display information about the given color
    list          Show a list of available color names
    random        Generate a list of random colors
    distinct      Generate a set of visually distinct colors
    sort-by       Sort colors by the given property
    pick          Interactively pick a color from the screen (pipette)
    format        Convert a color to the given format
    paint         Print colored text using ANSI escape sequences
    gradient      Generate an interpolating sequence of colors
    mix           Mix two colors in the given colorspace
    colorblind    Simulate a color under a certain colorblindness profile
    set           Set a color property to a specific value
    saturate      Increase color saturation by a specified amount
    desaturate    Decrease color saturation by a specified amount
    lighten       Lighten color by a specified amount
    darken        Darken color by a specified amount
    rotate        Rotate the hue channel by the specified angle
    complement    Get the complementary color (hue rotated by 180°)
    gray          Create a gray tone from a given lightness
    to-gray       Completely desaturate a color (preserving luminance)
    textcolor     Get a readable text color for the given background color
    imas          Get a Im@s idol's image color from im@sparql.      <---  こんな感じ!!!
    help          Prints this message or the help of the given subcommand(s)

↑ サブコマンドに追加

そして、出来上がったものがこちら

github.com

f:id:crssnky:20210801232936p:plain

こんな感じになります。
それでは解説していきます。

Rustからim@sparqlを叩く

im@sparqlへのクエリは以下になります。

REFIX imas: <https://sparql.crssnky.xyz/imasrdf/URIs/imas-schema.ttl#>
PREFIX imasrdf: <https://sparql.crssnky.xyz/imasrdf/RDFs/detail/>

SELECT distinct ?c 
WHERE {
  imasrdf:{} imas:Color ?c
}

今回は軽めの実装にするため、im@sparql内のリソース名を入力してもらうようにします。ですのでimasrdf:{}{}には、アイドルやユニットのリソース名(Kisaragi_Chihayaやnoctchillなど)が入ります。

シリアライズの詳しい内容は過去の記事へ。だいたい一緒です。

crssnky.hatenablog.jp

imas.rsファイルをcommandsフォルダに作成して処理を作ります(コード)。
HTTP GETするURLは以下のように作ります。form_urlencodedを利用すれば、クエリストリングを良い感じにしてエンコードまでしてくれます。
また、ureq::get().call()であれば同期処理でコールします。今回のようなものは、コールバック書かなくて良いのでラクですね。

    let encoded_query = form_urlencoded::Serializer::new(String::new())
      .append_pair("output", "json")
      .append_pair("force-accept", "text/plain")
      .append_pair("query", &query)
      .finish();
    let sparql_url = format!(
      "https://sparql.crssnky.xyz/spql/imas/query?{}",
      encoded_query
    );

    let res = ureq::get(&sparql_url).call();
    if res.ok() {
・・・
    }

im@sparqlのレスポンスを解釈する

受け取ったJSONを構造体に変換し、カラーコードを抜き出してColor構造体に変えているだけです。u8::from_str_radixを使えば、Hex文字列から数値に変換してくれます。

      let json_str = res.into_string().unwrap();
      let res_json: Response = serde_json::from_str(&json_str).unwrap();
      let json = res_json.results.bindings;
      if json.len() > 0 {
        for data in json {
          let color_string = &*(data.c.value);
          if color_string.len() >= 6 {
            let red: u8 = u8::from_str_radix(&color_string[0..2], 16).unwrap();
            let green: u8 = u8::from_str_radix(&color_string[2..4], 16).unwrap();
            let blue: u8 = u8::from_str_radix(&color_string[4..6], 16).unwrap();
            let color = Color::from_rgb(red, green, blue);
            out.show_color(&config, &color)?;
          }
        }
      }

imasコマンドを追加する

まずは、mod.rsファイルです(コード)。
上部のmod郡にimasを追加します。また、imas:ImasCommandを使うよう指定します。

mod imas;
use imas::ImasCommand;

あとは、imasというサブコマンドを実行したときに、imas:ImasCommandが使われるようimpl Commandpub fn from_string()に指定します。

      "colorcheck" => Command::Generic(Box::new(ColorCheckCommand)),
      "imas" => Command::Generic(Box::new(ImasCommand)),   // これ
      _ => unreachable!("Unknown subcommand"),

次にcli.rsファイルです(コード
サブコマンドがたくさん並んでいますので、そこにimasコマンドの引数やヘルプを書いておきます。

.subcommand(
    SubCommand::with_name("imas")
        .about("Get a Im@s idol's image color from im@sparql.")
        .long_about("Get a Im@s idol's image color from im@sparql.\n\n\
        Example:\n  \
          pastel imas Kisaragi_Chihaya")
        .arg(color_arg.clone()),
)

おわりに

以上が解説です。これを応用すれば、色に関する様々なアプリケーションを作成できると思います。
特に元から実装されている可視化部分がよく出来ています。imasコマンドも色を取ってきて、Color構造体に入れるだけで良い感じに可視化されるため、色を取得する部分だけでもカスタムすると面白いと思います。

HoloLens2で北朝鮮のミサイル実験を可視化 with UE4

デモ

発表資料

(16:9のスライドは埋め込みだと崩れちゃうか~) speakerdeck.com

はじめに

Tokyo HoloLens ミートアップ vol.26ではありがとうございました。
発表ではめちゃくちゃ駆け足になってしまったため、詳細に解説していきます。
内容としては、UE4を利用してHoloLens2で北朝鮮のミサイル実験を可視化した話です。
記事がめちゃ長くなりそうだったので、各要素ごとで記事にしています。
(弾道ミサイルのことはよく分からなくても記事が読めるように...)

HoloLens2とは

www.microsoft.com

みなさんご存知のMicrosoftが提供するMixedRealityデバイスです。
ゴーグル型のこのデバイスを着けることで、複合現実を利用することができます。
PC等に接続せずスタンドアロンで動作し、デバイスに外向きに取り付けられたカメラで周囲の形状や装着者の手の動きを認識します。
それこそが"複合現実"です。

データ

crssnky.hatenablog.jp

地球儀

crssnky.hatenablog.jp

UEGeoCoordinates Plugin

crssnky.hatenablog.jp

MRTK For Unreal UX Tools

crssnky.hatenablog.jp

まとめ

HoloLens2、UnityもいいけどUE4もよろしくね!

UE4向けMRTK UX ToolsとUMGでらくらくUI作り

はじめに

https://crssnky.hatenablog.jp/entry/2021/BMD

↑の子記事となっています。記事の背景を知る場合はこちらから参照ください。

MRTK UX Toolsとは

github.com

Mixed Reality Tool Kitはみなさんご存知かと思いますが、それのUE4版の主なツールキットです。
UE4ではUX Toolsの他に、Graphics ToolsというMR向けマテリアルキットがあります。

UMGとは

docs.unrealengine.com

誤解を恐れずに言えば、GUIとBlueprintで記述可能なUIシステムです。
ゲーム内はもちろん、UE4 Editorの拡張機能を作る場合にも使えます。

デモ

f:id:crssnky:20210711165616p:plain

こういうハンドメニューだったり、

f:id:crssnky:20210711165943p:plain

UMGのListViewだって置けちゃいます。
今回は、この2つのUIを例に解説します。

ハンドメニュー

https://github.com/microsoft/MixedReality-UXTools-Unreal/blob/public/0.12.x/Docs/HandMenu.md

公式ドキュメントとやっていることは変わらない。

f:id:crssnky:20210711171047p:plain

見た目とコンポーネントはこのようになっています。
SceneComponentはRootをUxtUIElementにします。
ここに、UIの部品を入れていくわけですが、単純なSceneComponentによるPivotを一個挟むことを個人的にはオススメします。
というのも、位置を調整できるようにしないと、デフォの位置では下記画像のように手に埋まってしまう可能性があります。

f:id:crssnky:20210711172447p:plain

X軸方向にズラしましょう。僕は8くらい移動させています。


UxtBackPlateはUIの背景用のコンポーネントです。ただのStaticMeshComponentではなく、UX Toolsのカスタム?品です。
下記画像を見てください。有る無しでは、UIのまとまり感がぜんぜん違うと思います。

f:id:crssnky:20210711173031p:plain


各種ボタンについては、ChildActorComponentで他のBlueprintファイルを埋め込む形を取っています。
それらの解説は次の章へ

各種ボタン

https://github.com/microsoft/MixedReality-UXTools-Unreal/blob/public/0.12.x/Docs/PressableButton.md

ボタンに関しても、公式ドキュメントと変わらない。

f:id:crssnky:20210711174707p:plain

UxtPressableButtonを継承したBlueprintで作ります。
そうすれば、各種プロパティをいじるだけで良い感じのボタンになるでしょう。
いじるのは以下のプロパティです。BlueprintのDetailsにあります。

f:id:crssnky:20210711175413p:plain

ボタンのサイズやアイコン、ラベルを好きに変えていきましょう。

f:id:crssnky:20210711180106p:plain

ボタンにはたくさんのイベントが用意されています。困りませんね。

ListView

https://github.com/microsoft/MixedReality-UXTools-Unreal/blob/public/0.12.x/Docs/WidgetComponent.md

UMGであるListView(Widget)を入れる方法の公式ドキュメントはこちら↑

え?BlueprintだけでListViewを作る方法がわからないって?だったら下記のForumを参考にすると良いですよ。僕はこれに何度も救われています(いつまで経っても覚えていない)

forums.unrealengine.com

ListViewのUMGファイルを作ったあとということで進めていきます。

f:id:crssnky:20210711180956p:plain

見た目とコンポーネントはこのようになっています。
UxtFollowは、HoloLens2の向きにUIを追従させるためのActorComponentであり、SceneComponentではないのですが、なぜかSceneComponentの場所に入っています。なんでやねん。

f:id:crssnky:20210711182624p:plain

ちゃんと、Non-scene componentって書いてある↑


UxtWidgetは、UMGに触れられるように、手の当たり判定とUMGをつなぐためのコンポーネントです。
これと、Widgetコンポーネントを配置することで、触れられるUMGを表示して操作することができます。WidgetコンポーネントはちゃんとScreenSpaceではなく、WorldSpaceに配置するように設定してくださいね。
ちなみに、ListViewのスクロールは右のスクロールバーで行います。スマフォでよくあるListView全体をぐいーってスライドしてもスクロールしませんからね!(僕はその罠に嵌りました)


UxtBackPlateは前述の通り背景用です。これが無ければ本当に背景がありません。


CloseButonは、このBlueprintを閉じるためのボタンです。UMG側に用意するとWidgetだけ閉じられてしまうので、Blueprint自体をDestroyできるようにBlueprint側で用意します。
(今思えば、Widget側でイベント発行してBlueprintで受信すれば良かったか?)

f:id:crssnky:20210711182843p:plain

おわりに

以上、簡単なボタンを持つUIやUMGを埋め込んだUIを作る方法でした。正直、UMGが使えてしまうのであればなんでもできちゃうので、HoloLens2のUIに関してはできないことが無いと思います。
とはいえ、UX Toolsの標準的な見た目のみを使っているため、見た目に凝ったUIに関しては制約があるかもです....

UE4のUEGeoCoordinates PluginでGeocentricしてみる

はじめに

https://crssnky.hatenablog.jp/entry/2021/BMD

↑の子記事となっています。記事の背景を知る場合はこちらから参照ください。

UEGeoCoordinates Pluginとは

github.com

EpicGamesが作っているUE4の公式プラグインです。
最初に言うと、このプラグインUE4.26向けにプレビュー版として公開されており、次期バージョンのUE4.27では"GeoReference Plugin"として標準搭載されています。
もちろん、今回お話する内容はUE4.26向けのプレビュー版です。4.27ではたぶん違います。ソース見たら違ったし...

UEGeoCoordinates Pluginは、おそらく"Project Anywhere"のために作られたと予想します。現実の座標(緯度経度など)を表すためのクラスや倍精度浮動小数点を使った座標クラスなど、シミュレーション用途で必要そうなものが揃っているので。
このプラグインの使い方や現実での座標の考え方は、プラグインリポジトリにPDFがあるのでそれを見ると分かります。座標の考え方や座標参照系など、馴染みが薄いものが分かりやすい英語と図で解説されているのでここで書く必要は無いでしょう。

Geocentricしてみる①

Geocentric coordinate systemとは、いわゆる地心座標系です。
地球の中心を(0,0,0)とし、北極点をZ軸方向、本初子午線をX軸方向、東経90度方向をY軸方向とする座標系です。単位はmで、3D界隈の人にはとっつきやすいものかと思います。
(Y軸が上方向でしょ。など、軸に関しては言いたいこともあるかもですが...)

これをね、そのままUE4に適用しちゃえば良いって話ですよ。
BP関数はこちら↓
f:id:crssnky:20210711011839p:plain

Geographical coordinate systemは、いわゆる地理座標系です。ですので、この関数の入力は緯度, 経度, 高度(Lat, Lon, Alt)です。

プラグインのSubsystemからGeographic to Geocentric関数を呼び出すだけで変換できます。ただ、出力は倍精度浮動小数点を使った座標クラスであり、単精度浮動小数点数を使うVector3では使えません。自作の関数で、泣く泣く精度を下げています。

Geocentricしてみる②

Blueprint内で緯度経度からXYZに変換する方法は分かりました。しかし、Level内に原点を設定する必要があります。
f:id:crssnky:20210711012603p:plain

GeoreferecingActorをLevelに置きましょう。これ自体の座標は関係ありませんが、Origin Projected Coordinatesや各種CRS(座標参照系)を指定してあげる必要があります。
f:id:crssnky:20210711012839p:plain

Origin Projected Coordinatesは原点にしたいLevel内の座標を指定します。何も考えず、(0,0,0)でも問題ないです。
CRS(座標参照系)は、プラグインのPDFの通り、https://epsg.io/ で探しましょう。"CRS Tokyo"などでGoogleで調べても良いかもしれません。
入れたい緯度経度が日本周辺だったため、上記画像の値は東京となっています。

GeoreferencingSubsystemはこのActorの設定値を読み取り、適切な変換を行います。

おわりに

変換すれば、以下のように良い感じで緯度経度をUE4のワールド座標に変換することができます。
(これは発射点と着地点の間に最高点を入れて補間してるけどね)

UE4でHoloLens2でも使える、昼夜を表す地球儀を作ろう

はじめに

https://crssnky.hatenablog.jp/entry/2021/BMD

↑の子記事となっています。記事の背景を知る場合はこちらから参照ください。

また、昼夜を表せられる地球儀をサクッと作りたい方向けです。
そんなに精密なものではありません。

デモ

このような感じで、昼夜のテクスチャを向きで切り替える地球儀です。

テクスチャ

地球の画像と言えば、やはりNASAのBlueMarbleでしょう。

NASA Visible Earth - Home

この中から、昼の地球と夜の地球を選びます。
夜の地球は一枚しかない(Earth’s City Lights)ので良いのですが、昼の地球(Blue Marble Next Generation)はいろいろな季節のものがあります。お好きなものを選びましょう。

モデル

球体であればなんでもいいです。
僕はBlenderで65280頂点の球体を作りました。それくらいでもHoloLens2は余裕みたいです。

マテリアル

UE4のお時間です。全容はこちら↓
f:id:crssnky:20210710230552p:plain

おそらくこれはBestWayじゃないだろうけども、許してね。

まず下段ここ
f:id:crssnky:20210710231538p:plain ここですが、ラフネスを求めています。
海の部分だけ強く反射してほしい気分だったので、なんか良い感じに求めています。
(理論は無く、本当に"良い感じで"求めました)
そうすると、以下のような感じになります。
f:id:crssnky:20210710231249p:plainf:id:crssnky:20210710231330p:plain

次は上段ここ f:id:crssnky:20210710232749p:plain ここは、外から入力された光の向き(Vec4)と、ローカル座標の内積を取得しています。
マテリアルに当たる光をDirectionalLightにすることで、昼夜の判定を模擬します。
光と内積については、以下ブログの"セルシェーダーと内積"の章を見ると分かりやすいです。

UE4 誰でもわかるセルシェーダー入門 - Let's Enjoy Unreal Engine

最後はここ f:id:crssnky:20210710233912p:plain 内積による昼夜の判定から、ifノードで表示するテクスチャを選択しています。
Emissiveにも入れることでDirectionalLightしか無い宇宙空間でも、光の当たらない方を見せることができます。

Blueprint

コンポーネントはこんな感じです↓
f:id:crssnky:20210710235148p:plain

SceneComponent系は、StaticMeshComponentだけです。作った球体メッシュに作ったマテリアルを貼ってあげましょう。

ActorComponent系は、HoloLens2による入力を受けるためのコンポーネントです。主にTransformを変えるためのものです。説明は割愛。

マテリアルで取得するための光の向きはBlueprintで入れます。
f:id:crssnky:20210711000616p:plain このようなBP関数を作り、ConstructionScriptはもちろん、Tickなどで定期更新してあげましょう。常にTickを呼ぶのはアレなので、僕はHoloLens2がGrabしているだけTickするようにしています。

おわりに

以上です。簡単!環境によっては、画像をDLする時間やマテリアルをビルドする時間の方が手を動かす時間より長いかもしれませんね。
説明は割愛しましたが、HoloLens2のMRTKのコンポーネントを入れることで、自分の手で動かしたり回したりできます。まさに地球儀です。
ここになにか情報を重畳してあげることで、MRでリッチな自分だけの地球儀を作れるようになると思います。

cereal-UE4でJSONデータを読み込む

はじめに

https://crssnky.hatenablog.jp/entry/2021/BMD

↑の子記事となっています。記事の背景を知る場合はこちらから参照ください。

データ

github.com

実は、北朝鮮のミサイル実験のJSONデータはこのリポジトリにまとまっています。
このリポジトリは、元々Webアプリケーションとしてミサイル実験の可視化を行うプロダクト(ReadMe参照)のものですが、そこに使われているものがよくまとまっており、定期的に更新もされているため非常に使いやすいです。
本プロジェクトでは、

  • test.ja.json(各実験概要)
  • missile.ja.json(ミサイルリスト)
  • facility.ja.json(施設...ですが、分かりやすく言えば発射地点リスト)

を読み取ります。

ライブラリ

読み取りに使うのは、cerealというC++JSONライブラリです。UE4で扱うため、cereal-UE4も追加で入れます。
使い方は、伊藤兎さんのcereal-UE4に関する記事を読むと分かりやすいです。

GitHub - USCiLab/cereal: A C++11 library for serialization

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

usagi.hatenablog.jp

元データは以下のような形のため、C++で非常に解釈しづらいです。

{
    "er-scud": {"name": "スカッドER", "type": "MRBM"},   // keyに名前が入っている
    "hwasong-12": {"name": "火星12号", "type": "IRBM"}, 
    ....
}

ですので、自前で良い感じに整形してから(前処理として)、C++で読み込んでいます。

{
  "root": {
    "missiles": [
      {
        "key": "er-scud",   // 名前をvalue側に移した
        "name": "スカッドER",
        "type": "MRBM"
      },
      {
        "key": "hwasong-12",
        "name": "火星12号",
        "type": "IRBM"
      },
      ....
}

これならば、以下のC++で読めますね。

USTRUCT(BlueprintType)
struct FMissile {
    GENERATED_BODY()
        UPROPERTY(BlueprintReadWrite, Category = "MissileTest")
        FString key;
    UPROPERTY(BlueprintReadWrite, Category = "MissileTest")
        FString name;
    UPROPERTY(BlueprintReadWrite, Category = "MissileTest")
        FString type;
};
template<typename T>
void serialize(T& a, FMissile& in) {
    a(
        make_nvp("name", in.name),
        make_nvp("key", in.key),
        make_nvp("type", in.type)
    );
}

おわりに

UE4JSONを解釈するための、個人的BestWayでした。伊藤兎さんの記事にある通り、標準機能はBestではないためこちらを利用することをおすすめします。