PythonからUE5にテキストを送信してみた
はじめに
Python→UE5への通信をしてRPC的なことをやってみたかったので、前座として単純にテキストを送信するプログラムを作ってみました。 今回は、クライアントサーバー型を採用しています。
サーバー : UE5
クライアント: Pythonスクリプト
上記のような分担になります。
サーバー実装
まずはソケットの作成を行う必要があります。 以下C++関数例です。
bool AMyRemoteControlServer::CreateSocket() { if (IpAddress.IsMulticastAddress()) { Socket = FUdpSocketBuilder(TEXT("Multicast")) .WithMulticastLoopback() .WithMulticastTtl(1) .JoinedToGroup(IpAddress) .BoundToPort(Port) .Build(); } else { Socket = FUdpSocketBuilder(TEXT("Unicast")) .BoundToAddress(IpAddress) .BoundToPort(Port) .Build(); } return true; }
IpAddressとPortについては以下のような形になっています。
UPROPERTY(BlueprintReadWrite, EditAnywhere) int32 Port {7000}; FIPv4Address IpAddress;
※IpAddressの初期値は、127.0.0.1(ローカルホスト)に設定
次はレシーバーの実装です。 これを実装することで、クライアントからメッセージを受け取ることができます。
bool AMyRemoteControlServer::CreateReceiver() { //レシーバー作成 Receiver = new FUdpSocketReceiver(Socket, FTimespan::FromMilliseconds(1), TEXT("Receiver")); //レシーバーチェック if (!Receiver) { return false; } //受け取った時のイベントバインド Receiver->OnDataReceived().BindUObject(this, &AMyRemoteControlServer::OnDataReceived); //受付開始 Receiver->Start(); return true; }
レシーバーに登録するイベントは以下のように実装しています。
void AMyRemoteControlServer::OnDataReceived(const FArrayReaderPtr& Reader, const FIPv4Endpoint& Sender) { //メッセージデータの作成 FMessageData Data; for (int i = 0; i < Reader->Num(); i++) { Data.RecvName.AppendChar(Reader->GetData()[i]); } //BPイベント呼び出し BP_OnDataReceivedEvent(Data); }
※FMessageDataはFString変数(RecvName)のみが実装されています。
ゲーム終了時に作成したソケット情報やレシーバーの削除を行います。クラッシュの原因になったりするので必要です。
void AMyRemoteControlServer::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); //レシーバー削除 delete Receiver; Receiver = nullptr; //ソケット削除 if (Socket) { Socket->Close(); ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(Socket); } }
最後に注意点として後でレベルに配置するので、 Actorを継承したクラスで実装しておいた方がいいです。
クライアントの実装
import socket import time M_SIZE = 1024 # Serverのアドレスを用意。Serverのアドレスは確認しておく必要がある。 serv_address = ('127.0.0.1', 7000) # ①ソケットを作成する sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) message = 'test' i=0 #メッセージ送信 while True: send_len = sock.sendto(message.encode('utf-8'), serv_address) print(message) i=i+1 if i==10: break time.sleep(1) #終了処理 print('closing socket') sock.close() print('done')
IPアドレスとポート番号はサーバーに合わせています。
実行前準備
まずは、サーバー実装を行ったクラスを継承したBPを作成し、 上記のように、作成したソケット作成関数やレシーバー作成関数呼び出しをBeginPlayイベントで行います。
次にレシーバーに登録したイベントで呼ばれているBPイベント「BP_OnDataReceivedEvent」の実装を行います。
最後に上記準備を整えたサーバーアクターをレベルに配置します。
実装結果
UE5でPIEを実行して、Pythonスクリプトを実行した結果です。 うまくいけば下記のように、表示されるはずです。
まとめ
通信処理に慣れないこともあり、結構実装に時間かかりました。 受信できないときは、ReceiverのStart関数が呼ばれているかの確認をしてください。 その他だと、ソケットの設定に問題があることが多かったです。 次は、この機能を使って実際にUE5でキャラクターコントロールしてみたいと思います。
pybind11を使ってPythonからC++呼び出しを行う②(UEから呼び出し編)
はじめに
[Python]pybind11を使ってPythonからC++呼び出しを行う① (モジュール作成まで)
前回の続きで、今回はUnrealEngineからPythonを使用してC++関数を呼び出してみたいと思います。
モジュールを認識させる
まずは前回作成したpydファイル(モジュール)を認識させる必要があります。 今回はUnrealEngineに認識させる必要があるということで、 公式ドキュメントによると、いくつか自動的に認識させるためのパスが追加されているようです。
今回はプロジェクト以下にデフォルトできるContentフォルダ以下にPythonフォルダを作成しました。 [ProjectName]/Content/Python
このPythonフォルダに前回作成したpydファイルをコピーします。
これで準備は完了です。
UnrealEngine上から呼び出してみる
Unreal側のPython環境が整っていること前提ですが、 実際にUnrealEngineから簡単に呼び出してみます。
上記画像の箇所に下記テストスクリプトを入れると...
import TestAdd result = TestAdd.add(1,1); print(result);
実行結果は以下のようになるはずです。
以上、簡単ですがPythonから呼び出しができました。
まとめ
UnrealEngineから呼び出しまでの流れは、簡単に実装できました。 pybind11のドキュメント通りにやってみただけなので、 ここからさらに色々工夫したら楽しいことができそうで、ワクワクしてます!w
pybind11を使ってPythonからC++呼び出しを行う① (モジュール作成まで)
はじめに
RPCを使ったデバッグ効率化技術に興味があったので、 手始めにPythonからC++の呼び出しを行うためのモジュール作成を行いました。
pybind11インストール
pipコマンドを使用して、インストールを行いました。
pip install pybind11
「Requirement already satisfied」が表示される場合は、 すでにインストールされていると思います。 「Python」と出る場合は、うまくいってませんでした。 対処法については、以下の通りです。
① コマンドを変えてみる
python -m pip install pybind11
上記のように変更してみると、成功することがある。
② Pythonの環境を再構築してみる
Pythonの再インストールで解決しました。 必要なファイルが十分にインストールされていない可能性があるかもしれません。
自分の場合は、上記のいずれかで解決しました。
ソースコードの準備
以下のソースコードの準備を行います。
▼TestAdd.cpp
#include <pybind11/pybind11.h> namespace py = pybind11; //2つの引数を足した結果を返す int add(int i, int j) { return i + j; } //新たなモジュールを作成する PYBIND11_MODULE(TestAdd, m) { m.doc() = "pybind11 TestAdd plugin"; m.def("add", &add, "A function that adds two numbers"); }
ビルドを行う
■ VisualStudioでビルドする場合
この段階でビルドを行うとエラーが起きると思います。 VisualStudioを使用してビルドする場合は、 下記の設定が必要となります。
上記設定を行うとエラーは解消され、ビルドは成功します。 モジュール作成まではしたことがないのでここまで。
■ Cmakeを使用する場合(推奨)
基本的にはこっちの方法を使用しています。 まずはCMakeLists.txtの作成が必要です。
cmake_minimum_required(VERSION 3.0...3.26) project(TestAdd) set(PYBIND11_CPP_STANDARD -std=c++14) find_package(pybind11 REQUIRED) pybind11_add_module(TestAdd SHARED TestAdd.cpp)
※環境に応じて適切に変更してください。
次に、以下のコマンドを使用してビルドを行います。 ※CMakeLists.txtがあるフォルダで以下のコマンド入力
mkdir build && cd build cmake .. cmake --build .
- buildフォルダを作成して、そのフォルダ内に移動する
- cmakeでビルドに必要な情報を生成する
- cmake --build でビルドを行う
以上がビルド手順となります。
まとめ
ここまでだけでも慣れていないと結構苦戦します。 次はPython側で今回作成したpydファイル(モジュール)を使ってみたいと思います。
MotionWarping使ってみた
はじめに
気になっていたMotionWarpingをいまさら触ったので、 簡単な導入方法の紹介になります。
開発環境
UnrealEngine5.1
事前準備
今回はThirdPersonプロジェクトをベースに検証しています。 また、古代の谷からアニメーションを持ってきています。
実装結果
それでは、実装方法についての紹介に入ります。
障害物のオブジェクトを取得する
上記のような形で障害物オブジェクトを取得します。 レイトレースでヒットしたものを取得するシンプルなものです。
MotionWarpingの準備
ThirdPersonCharacterにMotionWarpingコンポーネントを追加します。
モンタージュの仕込み
まずは古代の谷からもってきたアニメーションでアニメーションモンタージュを作成します。 そこに「MontageWarping」Notifyを追加し、以下のように設定します。
MotionWarping処理追加・モンタージュ再生
大体の大まかな処理は以下のようになっています。
ThirdPersonのJumpの処理の流れの後に、MotionWarpingの移動量計算、その後にモンタージュを再生しています。
MotionWarpingの移動量計算の関数内部は以下のような感じです。
以上で簡単な実装紹介終わりです。
まとめ
かなり簡単にパルクール実装ができました。 ただ、クォリティとしてはかなり改善の余地がある状態なので、今後も色々試してみる予定です。
WorldPartition機能を使ってみる
WorldPartitionが有効化されている別レベルを使ってもいいですが,
ウィンドウからワールドパーティションを選択します。
Floorメッシュが映り込んでいるのがわかります。
まだまだ調べたらたくさん知らない情報がありそうです。
WorldPartitionについて
VisualStudioのファイル生成に失敗する件について
配布用ビルドまでして,自前のプロジェクトや古代の谷のSlnファイルを作ろうとしたらエラー出たので色々調べて対処した件。
UnrealBuildTool.exeの場所が変わったせいです。
UE4の時 → Engine\Binaries\DotNET
UE5 → Engine\Binaries\DotNET\UnrealBuildTool
UE5で「GenerateVisualStudioProjectFiles」を実行すると,
UE4の時のパスを見に行っているので,見つかるわけがないということですね。