Editor Utility Widget と Python で CSV とやり取りする

いろんな方法があると思うのですが、自分でやってみた記録として。

環境: UE5 Preview 2

Plugin の有効化

UE5 Preview 2 の場合、最初から有効化されていました。

  • Editor Scripting Utilities
  • Python Editor Script Plugin

目標

簡単な例として、レベルに配置されているアクターの location を CSV に吐き出し、それを CSV から読み込み再配置できるだけというものにします。

CSV ファイルも固定パス・固定名とします。

Editor Utility Widget の作成

f:id:you1dan:20220319073416p:plain

今回は WBP_AssetLocationStore としました。

UI

必要最低限だけ配置しています。

f:id:you1dan:20220319073940p:plain

ひとまず実行・配置

ここまでで、Editor Utility Widget を実行し、レベルエディタに配置しておくことにします。 f:id:you1dan:20220319074124p:plain

f:id:you1dan:20220319074133p:plain

イベントハンドルとログ出力

単純にハンドルしておきます。

f:id:you1dan:20220319074905p:plain

Log String でもいいですが、ここでは Print String で。

f:id:you1dan:20220319074920p:plain

Output Log ウィンドウで一旦 Clear しておくと確認しやすいです。ログの状況を確認したい場合は Window メニューから Output Log をもう一つだしておいてもいいかもしれません。

f:id:you1dan:20220319075150p:plain

それぞれのボタンをクリックするとログが流れることが確認できました。

f:id:you1dan:20220319075354p:plain

Python を呼び出す

Python を使用したエディタのスクリプティング | Unreal Engine ドキュメント

プロジェクトのフォルダの下にある「Content/Python」サブフォルダ。 が読み込まれるとなっているので、そこにスクリプトを配置することにします。

f:id:you1dan:20220319080028p:plain

ひとまず内容は以下のようにして、save.py, load.py命名します

import unreal

unreal.log("save")

Python Script を Blueprint から呼出す

f:id:you1dan:20220319080338p:plain

どうやら失敗しました。読み込めていないようです。

f:id:you1dan:20220319080448p:plain

ここでは前に進めるためにプロジェクト固有でロードパスを追加します。
ドキュメントにもありますが、エディタの再起動が必要です。

f:id:you1dan:20220319080635p:plain

Python Script のログも表示されるようになりました。

f:id:you1dan:20220319081119p:plain

Actor の Location を CSV で読み書きする

対象の ActorBP_Cube として作成します。
Static Mesh を持つだけの単純なものです。

f:id:you1dan:20220319083912p:plain

雑にレベルに配置します。

f:id:you1dan:20220319084245p:plain

CSV に保存するスクリプト

保存先は、Content/Data/CubeLocation.csv にします。事前にディレクトまで作成しておきます。
CSV は 4 column で、アセット名,X座標,Y座標,Z座標 を書き出すことにします。

import unreal
import csv

unreal.log("save")

actor = unreal.EditorAssetLibrary.load_blueprint_class('/Game/Sample/BP_Cube') # Content/Sample/BP_Cube.uasset
sub = unreal.UnrealEditorSubsystem()

# レベル上のすべての対象 Actor を集める
actors_in_level = unreal.GameplayStatics.get_all_actors_of_class(sub.get_editor_world(), actor)

header = ['', 'X', 'Y', 'Z']

with open(unreal.Paths.combine([unreal.Paths.project_content_dir(), 'Data/CubeLocation.csv']), 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(header)

    for a in actors_in_level:    
        asset_name = a.get_name()
        location = a.get_actor_location()
        writer.writerow([asset_name, location.x, location.y, location.z])

unreal.log("finished saving to csv")

Save をクリックすると以下のような CSV が出力されれば成功です。

,X,Y,Z
BP_Cube_C_UAID_A8A1596048F915FE00_1762746744,270.0,1010.0,128.00009999999747
BP_Cube_C_UAID_A8A1596048F915FE00_2071157745,-130.0,1010.0,128.0001
BP_Cube_C_UAID_A8A1596048F915FE00_2073499746,-530.0,1010.0,128.0001

CSV から呼び出しレベルに配置するスクリプト

実行前にレベルから先程の Actor を削除しておきます。

import unreal
import csv

unreal.log("load")

actor = unreal.EditorAssetLibrary.load_blueprint_class('/Game/Sample/BP_Cube')
sub = unreal.UnrealEditorSubsystem()

with open(unreal.Paths.combine([unreal.Paths.project_content_dir(), 'Data/CubeLocation.csv']), newline='') as csvfile:
    reader = csv.DictReader(csvfile)

    for row in reader:
        unreal.EditorLevelLibrary.spawn_actor_from_class(actor, 
            (unreal.StringLibrary.conv_string_to_float(row['X']), 
            unreal.StringLibrary.conv_string_to_float(row['Y']), 
            unreal.StringLibrary.conv_string_to_float(row['Z'])))

unreal.log("finished loading from csv")

Load をクリックすると、先程と同じ場所に Cube が出現すれば成功です。

Python API について

Unreal Python API Documentation — Unreal Python 5.0 (Experimental) documentation

上記から Blueprint の関数名(もちろん C++ API に精通していればそっちも)を参考に探していくことになります。
今回書いたスクリプトも、もっとスッキリさせられそうな気がする( StringLibrary の使い方あってるのかどうかなど)のでいろいろ試してみたいです。

参考にさせていただいたページ 🙏

コード

GitHub - dany1468/UE_EUW_CSV

余談

Unreal Editor で Git の設定をさせると lfs の設定で Content/** filter=lfs diff=lfs merge=lfs -text.gitattributes に入るので、Python ファイルまで対象になってしまうの気づいてませんでした。次からはちゃんとカスタマイズしないといけないですね。