ObjectDeliverer ログファイル保存機能と再生機能

通信内容を記録する 通信機能を実装していると、必ずといっていいほど後から再現させたい事があります。 特に通信相手の環境がなかなか用意できない時などは必須の機能です。 そこでObjectDelivererにログファイルへの通信内容の保存と、その再生機能を実装しました。 サンプル 保存と再生機能を使ったサンプルです。 ここでの通信内容は800px x 450px 32bitARGBのキャプチャされたビットマップなので、60fps(=16.66ms)毎に1440000byte = (800 x 450 x 4)のデータが送受信されています。 データの流れとしては [WPFアプリ]→(共有メモリ)→[UE4アプリ]→(ログファイル) という流れを繰り返しています。 記録用の動画キャプチャも並列して動かしていたため、さすがにカクついていますがなんとか動きはしています。 {{< x user=“ayuma_x” id=“1093157903744196610” >}} ログファイルへの保存機能 ObjectDelivererのStart時にProtocolLogWriterをセットして使用します。 Sendを呼ぶことでログファイルへデータが書き込まれます。 ログファイルには書き込み毎に開始時からの経過時間もmsecオーダーで記録されます。 ログファイルの再生機能 ObjectDelivererのStart時にProtocolLogReaderをセットして使用します。 Startを呼ぶとログファイルに記録された時間を経過する毎にReceiveDataイベントが呼ばれます。 そのため通常の相手がいる通信の時と同じように受信データの処理を行う事ができます。 まとめ ログファイルへの保存と再生機能を実装することができて、当初ObjectDelivererに実装したいと考えていた機能もだいぶ消化することができました。 あとはMessage Packを使ったシリアライズを行ってのObjectの送受信が実装できればひと段落です。

2019-02-07 · 1 分 · 37 文字 · ayumax

BlueprintNativeEventではまった

はまったこと 今日の記事は先日起こった問題を覚書として記録しておきます。 ちなみにまだちゃんとした原因は分かっておらず、逃げの対策をとってる状態です。 今作成しているPluginで、C++でもBlueprintでも関数をオーバーライドしたいなあと思って色んな ところで下のようなパターンを実装してました。 UFUNCTION(BlueprintNativeEvent, Category = "ObjectDeliverer") void Start(); virtual void Start_Implementation(); 上記の例ではC++の別のクラスからStart()が呼ばれる想定です。 このクラスを継承したC++のクラスではStart_Implementation()をオーバーライドして、BlueprintではStart()をオーバーライドする事でどっちでクラスを継承してもOKだよってのを狙ってました。 実装して動かしてみるとしばらくはちゃんと動いているように見えていたのですが、ちょいちょいStart()を呼ぶタイミングで落ちることに気づきました。 {{< x user=“ayuma_x” id=“1090251296286724096” >}} デバッグして追ってみると、Start()の中ではProcessEventが呼ばれておりリフレクションで関数は実行されてるって事が分かりました。 その中のcheck(!IsUnreachable)で落ちているって事は分かったのですがなぜそうなっているのか全く分かりません。 IsUnreachableってことはGC対象になってしまったのか???と思いコードをチェックしたのですが、ちゃんとUPROPERTY()で指定した変数で保持してます。 一応念のため、別のアプローチもとってみようと思い、下の記事の内容も試してみましたが現象は変わりませんでした。 UPROPERTY を使わずにオブジェクトをガーベジ コレクションの対象から外す方法 - Qiita BlueprintNativeEventを使っているところでも2通りのタイミングで落ちていて、起動後しばらくしたら落ちる箇所と、BeginDestroy時がありました。 BeginDestroyはまだ終了時なのでここじゃこういった関数呼び出しはしちゃいけないのかなと思って、何となく理解できるのですがもう一つのパターンが分かりません。 ただちょっと普通じゃないのはFRunnableThreadを使って走らせているワーカースレッド内で落ちるのでその辺りも関係しているのかなとも思ったり。 結局原因が分からず直し方も分からないという情けない結論に至ったため、Blueprintでのオーバーライドは諦めてBlueprintNativeEventを外し通常のvirtual関数にしました。 その後は全く落ちなくなったのでとりあえずは良いのですが、モヤモヤしてるのでちゃんと時間をとってどこかで調べたいと思ってます。 その後落ちないって事はGCで回収されてないって事なので余計によくわからない。。。

2019-01-31 · 1 分 · 37 文字 · ayumax

ObjectDelivererの共有メモリを使った映像同期

サンプル解説 今回の記事は昨日書きました共有メモリを使ったサンプルの実装解説記事です。 {{< x user=“ayuma_x” id=“1090203272730664960” >}} WPFアプリ側 WPF側は自分のウィンドウの表示内容を共有メモリに書き込むことをやっています。 全ソースは以下のリポジトリにあります。 共有メモリの作成 “SharedMemory"という名前で共有メモリを作成し、メモリサイズは800 * 450 * 4 + 5にしています。 これは横800pixel, 縦450pixel、32bit BGRAのビットマップが格納できるサイズです。 最後の+5はObjectDelivererの共有メモリで使用するヘッダ要素分になります。 private MemoryMappedFile share_mem; private MemoryMappedViewAccessor accessor; share_mem = MemoryMappedFile.CreateOrOpen("SharedMemory", 800 * 450 * 4 + 5); accessor = share_mem.CreateViewAccessor(); WPFのフレーム描画タイミングの仕掛け CompositionTarget.Renderingイベントを監視することで、毎描画フレームに処理ができるようにします。 CompositionTarget.Rendering += CompositionTarget_Rendering; 表示内容をビットマップに書き込み Rootというのは表示内容を取得するコントロールにつけたNameです。 まず、VisualTreeHelper.GetDescendantBoundsで表示領域の大きさを取得し、その大きさあったRenderTargetBitmapを作成します。 次にDrawingVisualを作成してからビットマップに表示内容を書き込みます。 書き込みが完了したらビットマップのピクセルバッファの中身をbyteの配列にコピーします。 このbyteの配列が今回欲しいものです。 var visual = Root; var bounds = VisualTreeHelper.GetDescendantBounds(visual); var bitmap = new RenderTargetBitmap( (int)bounds.Width, (int)bounds.Height, 96.0, 96.0, PixelFormats.Pbgra32); var dv = new DrawingVisual(); using (var dc = dv.RenderOpen()) { var vb = new VisualBrush(visual); dc.DrawRectangle(vb, null, bounds); } bitmap.Render(dv); bitmap.Freeze(); var buffer = new byte[800 * 450 * 4]; bitmap.CopyPixels(buffer, 800 * 4, 0); 共有メモリへの書き込み 最後に共有メモリへの書き込みを行います。 ...

2019-01-30 · 2 分 · 232 文字 · ayumax

ObjectDelivererへ共有メモリプロトコル追加

共有メモリ機能 自作のUE4用ライブラリObjectDelivererに共有メモリプロトコルを追加実装しました。 これにより、従来から実装済みのTCP/IP, UDPに加えて共有メモリを使ったデータ転送が行えます。 ただし共有メモリはWindows Onlyです。。。 使い方 ObjectDelivererのプロトコルとして実装してあるため、ObjectDelivererManagerのStartメソッドに渡すプロトコルを切り替えるだけで使用できます。 deliverer->Start(UProtocolFactory::CreateProtocolSharedMemory("memory_test", 1024), UPacketRuleFactory::CreatePacketRuleSizeBody()); CreateProtocolSharedMemoryには共有メモリ名とメモリサイズを渡します。 この2つはデータのやり取りを行う相手と同じ値である必要があります。 仕様 データの送受信プロトコルとして使うため、作成した共有メモリの中身は定期的にポーリングしています。 ただし毎回データアクセスを行うと処理負荷が上がるため、以下のような仕組みで対応しています。 ブロック サイズ(byte) 内容 カウンター 1 データの書き込みを行う度にインクリメント サイズ 4 データ部のサイズ データ n 書き込まれるデータ 定期的なポーリング時はまず共有メモリの先頭1byteを読み込んで、前回読み込み時とカウンタの値が異なっているかをチェックします。 異なっていれば新しいデータが書き込まれていると判断して、次の4byteを読み込みデータサイズを確認した後データ本体を読み込みます。 そのためObjectDelivererを使ってないプロセスと共有メモリを使ったやり取りをする場合は、この仕様に合わせる必要があります。 サンプル 今回作成した共有メモリプロトコルを使って、以下のムービーのようなサンプルを作りました。 {{< x user=“ayuma_x” id=“1090203272730664960” >}} WPFで作ったアプリケーションに毎フレーム自分のウィンドウの表示内容を共有メモリに書き込ませ、UE4側ではObjectDelivererを使ってデータを受信します。 受信したデータ(ピクセルバッファ)を使ってテクスチャの中身を更新して、UE4の表示側をWPF側にミラーリングしています。 共有メモリのため同じマシンに2つのアプリケーションがないと使えませんが、パッと見た感じ遅延もなく同期できています。 参考までにここで作成したWPFアプリは↓です。 課題 長時間上記ミラーリングしていると共有メモリの読み込み時にUObject::ProcessEventの!IsUnreachable()のチェックにひっかかり落ちてしまう現象がでちゃってます。 これが治らないと使えないなー。 暫定ですが、BlueprintNativeEventを使っているところを全て取り除くことで治りました。 ただしこれではBlueprintでオーバーライドできなくなるので、そのあたりまた検討します。

2019-01-29 · 1 分 · 47 文字 · ayumax

ObjectDelivererでUE4のプロセス間通信を簡単に

UE4でのプロセス間通信 私はUnreal Engineで開発したアプリケーションとC#等で作ったアプリを連携させて使うことが多いのですが、そこでよく使うのはTCP/IPやUDPなどのソケット通信が一番多いです。 今までは必要になるたびにその時の仕様に合わせた機能を作っていました。 ただそれだとその分の実装を毎回することで面倒だなという思いもありますし、通信部分はデリケートなのでちゃんと作らないと不具合も入りやすいです。 そこでここらでプラグイン化することで、使い回しやすさを狙いました。 まだ実装予定で未実装の項目はあるのですが、 現状の実装は以下のリポジトリから確認できます。 簡単な使い方はREADMEを見ていただければ分かると思いますが、苦手な英語で書いているので読みづらいと思います。。。 なので今回この記事でもう一回書きます。 コンセプト 今回作ったプラグイン(ObjectDeliverer)では以下の3点をコンセプトにしてます。 コンパクトさ UE4のSocket通信、特にTCP/IPの実装は結構ローレベルな実装になっておりC#とかに比べると使うのに実装しなければいけない量が多いです。 そこである程度の縛りは生まれてしまいますが、とにかく使うときの実装量をコンパクトにすることをコンセプトに設計しました。 Blueprintで使えること UE4の良さであるBlueprintで使えることをもう1つのコンセプトにしました。Blueprint対応を捨ててC++のみで使えるプラグインにしたほうが出来る事は多くなるのですがここはこだわってみました。 Blueprintの線が切れないこと ObjectDelivererでは複数の通信プロトコルをサポートしてます(今後増やしていく予定)が、それらを別々の型として実装してしまうと、実装済みのBlueprintにおいてプロトコルの型を切り替えたときにBlueprintの断線が起こってしまいます。 そこでそういった実装後の仕様変更時になるべく断線がおこらないような設計を心がけました。 機能説明 ObjectDelivererの機能を簡単に説明します。 通信プロトコル 現在以下の4つのプロトコルに対応してます。 TCP/IP Server(複数のクライアント接続に対応) TCP/IP Client UDP(送信のみ) UDP(受信のみ) TCP/IPはサーバー、クライアントどちらも送受信両方に対応してます。 サーバーにて複数のクライアントが接続している状況では、サーバーからの送信はブロードキャストする設定がデフォルトです。 UDPは迷ったのですが、UDPを使うユースケースでは送受信を同時に行う事は少ないだろうと考え別々にしています。 もちろん2つ同時に使用すれば送受信にも対応できます。 データ分割ルール UDPでは1回の受信データが1データとして使われることが多いですが、TCP/IPでは送受信データが途中までしか届かないことや、複数データが結合して届くこともあるため分割ルールが必要です。 そこで(私が)よく使う分割ルールを現状3つ実装してあります。 固定サイズ ヘッダー + 本体 終端記号式 固定サイズは予め決めたサイズ分で毎回区切る方式です。 送受信データサイズが固定サイズ未満の場合でも、固定サイズ分送る必要があります。 逆に1回に送りたいデータサイズが固定サイズ以上になる場合は対応していません。 次のヘッダー + 本体形式では、データの先頭に本体サイズを格納します。 そのため受信側は一旦サイズ領域を読み取って次に続く本体部分のサイズを認識し、本体を読み込むといった挙動になります。 サイズ部分のバイト数とエンディアンは外側から変更可能です。 個人的にはこれが一番使い勝手が良いと思っています。 最後の終端記号式は、よくある「改行コードで区切る」といったパターンに対応するために作りました。 送受信データ形式 ObjectDelivererではデフォルトではバイト配列形式(TArray)でのデータフォーマットで送受信を行います。 ただDeliveryBoxというオプションを使うことで、文字列やオブジェクトをそのまま送受信することが可能です。 現状は以下の2パターンに対応しています。 UTF-8 String Json文字列形式を介したUObject 使い方 GitHubのリポジトリをクローンしていただき、Pluginsフォルダをプロジェクトフォルダにコピーしていただくと使用可能です。 Editor上でObjectDelivererプラグインを有効にしてください。 Blueprintでの利用方法 ObjectDelivererManagerのインスタンスを作成します。 必要な場合はDeliveryBoxのインスタンスを作成します。 ObjectDelivererManagerにProtocolとPacketRuleをセットしてStartメソッドをコールします。 ...

2019-01-28 · 1 分 · 156 文字 · ayumax

UE4 lambdaを使って自動テストを楽に書く

UE4は開発環境としてテストをサポートしており、別途ツールを使わなくても自動テストが書けるようになっています。 またLatent コマンドを使うことで、複数フレームにまたがるテストも記述できこれはかなり便利です。 自動化に関するテクニカルガイド ただ複数のLatentコマンドを使って遅延評価させようと思うと、 やりたい評価の種別だけDEFINE_LATENT_AUTOMATION_COMMAND マクロを定義しなくてはいかず面倒だなあとずっと思っていました。 たとえば以下のような感じ。 DEFINE_LATENT_AUTOMATION_COMMAND_TWO_PARAMETER(FHogeObjCommand, FAutomationTestBase*, Test, UMyObject*, Obj); bool FHogeObjCommand::Update() { check(Test); check(Obj); Test->TestEqual(TEXT("check"), Obj->Property, 1); return true; } DEFINE_LATENT_AUTOMATION_COMMAND_TWO_PARAMETER(FHogeObjCommand2, FAutomationTestBase*, Test, UMyObject*, Obj); bool FHogeObjCommand2::Update() { check(Test); check(Obj); Obj->Hoge(); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FHogeTest, "Hoge.HogeTest", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FHogeTest::RunTest(const FString&amp; Parameters) { auto obj = NewObject&lt;UMyObject>(); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(1.0f)); ADD_LATENT_AUTOMATION_COMMAND(FHogeObjCommand(this, obj)); ADD_LATENT_AUTOMATION_COMMAND(FHogeObjCommand2(this, obj)); ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(1.0f)); ADD_LATENT_AUTOMATION_COMMAND(FHogeObjCommand(this, obj)); } なので、毎回複数定義するのは疲れるため以下のようなLatentコマンドを定義したところ、ほぼこれでいけるようになりました。 ...

2019-01-16 · 1 分 · 142 文字 · ayumax

Unreal.jsでロジックを動的に変更する

この記事はUnreal Engine 4 (UE4) Advent Calendar 2018の20日目の記事です。 前日の記事は0xUMAさんの UE4 で自動テストを試してみる。 でした。 概要 UE4からWindows向けのパッケージを作成したあとで動作パターンを切り替えたい時があり、はじめの頃は外部テキストファイル(xmlやjsonなど)でパラメーターを変更できるようにして対応していました。 ですが、そのうちロジック自体も変更したいという要望がでてきました。 そこでUnreal.jsを使ってJavaScriptでロジックの外出しを実施した事を書きます。 この記事で作成したサンプルプロジェクトは以下に置いてあります。 対応方法選定 UE4ではEditorからはC++コードを変更してもホットリロードできるので、直ぐに(ちょっとは待たされる)動作確認できますがパッケージ化した後には使えません。 そこでUE4のRuntimeで動作可能なスクリプト機能のプラグインがないか調べたところ、以下の3つを見つけました。 Unreal.js UnrealEnginePython MonoUE [MonoUE](https://mono-ue.github.io/) MonoUEは個人的に好きなC#なので興味はありましたが、スクリプトとしてお手軽に使えなさそうだったので候補から外しました。 あと残った2つを考えましたが、個人的な好みでJavaScriptの方が良いかなと思いUnreal.jsを採用しました。 Unreal.jsとは Unreal.jsについての基本的な情報入手はこちらが参考になりました。 2年ほど前の記事ですが現状の4.21の環境でもほぼそのまま利用できとても助かりました。 Unreal.js 入門 Unreal.js 入門 - Qiita 導入 マーケットプレイスよりUnreal.jsを追加します。 その後UE4のプロジェクトをEditorから開き、Edit->PluginsからUnreal.jsを有効にします。 その後Editorを再起動すると使用可能になります。 試しにActorをレベルに配置してJavaScript Componentを追加し、Script Source FileにSample.jsと入力します。 Content/ScriptsにSample.jsを作り以下の内容を記述します。 console.log("test") この状態でPlayして、Output Logに"test"と表示されていたら正常に動作しています。 パッケージ後で確認 File -> Package Project -> Windows -> Windows(64-bit)でWindows用のパッケージを作成します。 パッケージ作成後、以下のディレクトリを作成したパッケージのContentにコピーします。 [Engine Install Path]\Engine\Plugins\Marketplace\UnrealJS\Content\Scripts ...

2018-12-20 · 2 分 · 357 文字 · ayumax