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 の作成

今回は WBP_AssetLocationStore としました。

UI

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

ひとまず実行・配置

ここまでで、Editor Utility Widget を実行し、レベルエディタに配置しておくことにします。

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

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

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

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

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

Python を呼び出す

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

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

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

import unreal

unreal.log("save")

Python Script を Blueprint から呼出す

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

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

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

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

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

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

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: Editor Utility Widget と Python で CSV をやりとり

余談

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