はくとうのゲーム開発ライフ

UE4周りの技術メモ

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を継承したクラスで実装しておいた方がいいです。



クライアントの実装

前述のとおり、Pythonスクリプトで実装します。

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 .
  1. buildフォルダを作成して、そのフォルダ内に移動する
  2. cmakeでビルドに必要な情報を生成する
  3. cmake --build でビルドを行う

以上がビルド手順となります。



まとめ

ここまでだけでも慣れていないと結構苦戦します。 次はPython側で今回作成したpydファイル(モジュール)を使ってみたいと思います。

MotionWarping使ってみた

はじめに

気になっていたMotionWarpingをいまさら触ったので、 簡単な導入方法の紹介になります。



開発環境

UnrealEngine5.1



事前準備

今回はThirdPersonプロジェクトをベースに検証しています。 また、古代の谷からアニメーションを持ってきています。



実装結果

それでは、実装方法についての紹介に入ります。



障害物のオブジェクトを取得する

上記のような形で障害物オブジェクトを取得します。 レイトレースでヒットしたものを取得するシンプルなものです。



MotionWarpingの準備

ThirdPersonCharacterにMotionWarpingコンポーネントを追加します。



モンタージュの仕込み

まずは古代の谷からもってきたアニメーションでアニメーションモンタージュを作成します。 そこに「MontageWarping」Notifyを追加し、以下のように設定します。



MotionWarping処理追加・モンタージュ再生

大体の大まかな処理は以下のようになっています。

ThirdPersonのJumpの処理の流れの後に、MotionWarpingの移動量計算、その後にモンタージュを再生しています。

MotionWarpingの移動量計算の関数内部は以下のような感じです。

以上で簡単な実装紹介終わりです。



まとめ

かなり簡単にパルクール実装ができました。 ただ、クォリティとしてはかなり改善の余地がある状態なので、今後も色々試してみる予定です。



WorldPartition機能を使ってみる

はじめに

 
UE5のWorldPartition機能の編集方法の一部のメモです。
今回はロード・アンロードを実際エディタ上で試してみます。
 
 
 
開発環境

・UnrealEngine5 EarlyAccess
・VisualStudio2019
 
 
 
 
WorldPartition機能を使ってみる

 
まず,Defaultレベルを作成します。
※この過程は飛ばして,
    WorldPartitionが有効化されている別レベルを使ってもいいですが,
    この記載している画像と若干のずれが発生する可能性があります。
 
ここで新規に作ったレベルを必ずセーブしてください。

f:id:Sleepycat:20210605131613p:plain

 
 
 
次に,WorldPartition機能を編集するには,
ウィンドウからワールドパーティションを選択します。
 

f:id:Sleepycat:20210605131632p:plain

 
 
Defaultレベルで作った場合は以下のようになります。
 

f:id:Sleepycat:20210605131656p:plain



何がなんだかわけがわからない...
少し中心部分を拡大(マウスホイール)し,
エディタビューポートにあるFloorメッシュを動かしてみます。

f:id:Sleepycat:20210605131725p:plain

そうすると,上記の画像のように「ワールドパーティション」タブに
Floorメッシュが映り込んでいるのがわかります。
 
Floorメッシュを動かして,下記のようにしました。

f:id:Sleepycat:20210605131748p:plain

 
上記の状態で左下のセルをドラッグして選択後,右クリックします。

f:id:Sleepycat:20210605131809p:plain

ここで選択したセルをロード・アンロードを押すことで,
Floorメッシュがロードされたりアンロードされたりします。
※何も変わらないよ!という方は一度「現在のレベルを保存」してから実行してみてください。
 
▼アンロード時

f:id:Sleepycat:20210605131830p:plain

 
▼ロード時

f:id:Sleepycat:20210605131858p:plain

 
こんな感じでドラッグで選択したセル単位で,ロード・アンロードすることができます。
 
 
 
 
最後に

 
今回はWorldPartitionについて書きましたが,
まだまだ調べたらたくさん知らない情報がありそうです。
何かここにないことで便利な機能あったら,ぜひ教えてください!
 
それではまた(^_^)/
 

WorldPartitionについて

はじめに

 
UE4ではサブレベル単位でレベルストリーミングを行って,ロード時間対策を行っていたが,
UE5でWorldPartitionが実装されたことにより,簡単にメッシュなどの読みわけができるようになった(らしい)
ということで,触ってみたので後で忘れないように簡単に設定方法をまとめました。
※随時新しいことが判明次第,追記予定
 
 
 
開発環境

・UnrealEngine5 EarlyAccess
・VisualStudio2019
 
 
 
 
WorldPartitionを有効にする

 
編集 > プロジェクト設定からプロジェクト設定画面を開く
 
エンジン > WorldPartitionにある「Enable World Partition」にチェックを入れる
 

f:id:Sleepycat:20210605131244p:plain

 
 
これだけでWorld Partitionは有効になりますが,落とし穴があります。
以下の画像は上記設定を行う前に作成したレベルのWorldSettingです。
※設定後に改めてレベルを開きました。

f:id:Sleepycat:20210605131353p:plain

 
分かりにくいかもしれないですが,
「ワールドパーティションの有効化」にチェックが入らないし,入れられない...
 
 
 
自分が調べた限りでは,新規にレベルを作らないと反映できなさそうです。 
WorldPartitionを有効化した後に作成したレベルのWorldSettingです。
 

f:id:Sleepycat:20210605131438p:plain

 
しっかり設定されていますね。
既に作ったレベルに設定ができないのは少し残念なところ。
※やり方わかってないだけかも( ;∀;)
 
 
以上でWorldPartitionを有効にする設定を終わりです。
 
 
 
 
 
 
 
 
WorldPartitionコマンドについて

 
wp.Runtime.ToggleDrawRuntimeHash2D
  → 現在読み込まれている部分と読み込まれてない部分の判別に使える。
 
 
 
 
 
 
最後に

 
今回はWorldPartitionの設定回りをメインに書きました。
以下の記事にはチュートリアル的なものを記載してます。

 

sleepycat.hatenadiary.com

 

 
 
 

VisualStudioのファイル生成に失敗する件について

はじめに

 
ソースビルドからエンジンを作成後,
配布用ビルドまでして,自前のプロジェクトや古代の谷のSlnファイルを作ろうとしたらエラー出たので色々調べて対処した件。
※画像とるのを忘れましたが,UnrealBuildTool.exeが見つからないというエラーでした。
 
 
 
解決方法

 
以下のコマンドを叩いて解決できます。
 
UnrealBuildTool.exe -projectfiles -project="[PROJECT_FILE_PATH]" -game -rocket -progress
 
※UnrealBuildTool.exeは「Engine\Binaries\DotNET\UnrealBuildTool」以下にあります。



なぜ生成できないのか

 
理由は簡単です。
UnrealBuildTool.exeの場所が変わったせいです。

UE4の時 → Engine\Binaries\DotNET
UE5        → Engine\Binaries\DotNET\UnrealBuildTool

UE5で「GenerateVisualStudioProjectFiles」を実行すると,
UE4の時のパスを見に行っているので,見つかるわけがないということですね。