※この記事で使用しているUnrealのVersionは04.26.0です。

※この記事のサンプルプロジェクトは以下URLにアップされています。

サンプルプロジェクト

みなさん朗報です!

ついに「Python Editor Script Plugin」がデフォルトでPython 3(3.7.7)対応になりましたよ!!

今まで

あのModule使いたいな~ → Python 2では使えません(´·ω·`)ショボーン

と言うようなことが多々あり、色々妥協していた部分がありましたがPython2が非推奨のバージョンとなり、
バージョン決定のネックになっていたVFX リファレンス プラットフォームのガイドラインに従うため、
Unreal Engine側でも Python 3 のサポートするようになったようです。

テンション爆上がりですね!

この機会に「Python Editor Script Plugin」について色々まとめてみました!

始めに…

記事を書いているのはしがないゲームプランナーなため、
PythonやC++等の記述の仕方などが変な場合がございますが、
予めご了承しあたたかい目で見ていただけると幸いです…(*´ω`*)。

また、「Python Editor Script Plugin」まだBeta版で実験的段階のバージョンです(いつまでBetaなんだろう…)
なので、今後Engineのバージョンアップに伴い動作や仕様が大きく変わることがございますので、ご了承ください。

初級編 レベル【★★】

~導入方法~

基本的には、

Python を使用したエディタのスクリプティング

に記載されている通りです。

まず、UnrealEditor上の左上にある「Edit」から「Plugins」を選択します。

そして、「Python Editor Script Plugin」の「Enable」というチェックボックスにチェックを入れて
(クリックして)、その後に一度Editorを再起動します。

これで、Unreal内でPythonを使うことができます!

~実行方法~

Unreal上でPythonを実行する場合は、いくつかの方法があります。

①「OutputLog」ウィンドウから実行

Pythonのプラグインを有効にすると、「OutputLog」ウィンドウ左下のアイコンをクリックすることで、
Pythonコマンド実行用入力ボックスに切り替えることができます。

選択肢として、「Python」と「Python(REPL)」がありますが、
基本的には「Python」の方を使います。

②Fileを指定して実行

Editor左上にある「File」から「ExecutePythonScript…」を選択することで、
外部にある「.py」拡張子のPythonファイルを実行することができます。

Explorerのファイル選択画面が出るため、任意のPythonファイルを選択して実行できます。


※特にフォルダの指定はなく、ProjectやEngineフォルダ外のファイルでも実行できます。

py_test.pyの中身

print("pytest")

結果

また、一度実行したことのあるPythonファイルは、
Editor左上の「File」から「Recent Python Script」にカーソルを合わせることでリストアップされ、
選択することで再び実行できます。

③ConsoleCommandで実行

OutputLogの「cmd」モード時、プレイイン時に「@マーク」キーを押す、
Blueprintの「Execute Console Command」関数などから実行できるUnreal上で機能するコンソールコマンドから「py [Pythonファイルパス]」もしくは「py [Pythonファイル名]」の形式の文字を入力して実行することで、Pythonを実行できます。
※File名の場合はあらかじめファイルのパスが「sys.path」に登録されている必要があります。

④Blueprint(C++)から実行

おそらくこれが一番使い勝手が良いかと思います。

Pythonプラグインを有効にすると、以下の3つのPython実行用Blueprintノードが使えるようになります。
これらの関数はEditor Utility系のBlueprintのみで使用可能です。

1、ExecutePythonCommand

一番シンプルにPythonコマンドを実行できる関数。
エラーなどで実行が失敗したかどうかを「Return Value」に返してくれます。

bool UPythonScriptLibrary::ExecutePythonCommand(const FString& PythonCommand)
2、ExecutePythonCommand(Advance)

コードをPythonファイルとして実行するか、単一のコードとして実行するかなどの細かな設定ができるとのと、実行結果としてOutputLogに出力されるデータを受け取ったりすることができます。

bool UPythonScriptLibrary::ExecutePythonCommandEx(const FString& PythonCommand, FString& CommandResult, TArray<FPythonLogOutputEntry>& LogOutput, const EPythonCommandExecutionMode ExecutionMode, const EPythonFileExecutionScope FileExecutionScope)
3、ExecutePythonScript

機能的には「ExecutePythonCommand」と変わらない(こちらはPythonファイルも実行できる)のですが、こちらはなんと引数や返り値を可変で追加できるノードになっております。

bool UPythonScriptLibrary::ExecutePythonScript(UPARAM(meta=(MultiLine=True)) const FString& PythonScript, const TArray<FString>& PythonInputs, const TArray<FString>& PythonOutputs)

どういうことかと言いますと、Python内に引数を渡したり、Pythonから戻り値を受け取ったりする場合に使います。

配列の要素を追加すると、Blueprint側にはWildcardピンとして、どんな要素でもつなげられるピンが登場します。

追加した要素に名前を付け、任意の要素をピンにつなげることで、追加した名前が変数として定義されます。Inputにつなげた場合は、中身がつなげたピンの値になります。

例:

実行結果

これを応用すればPython ←→Blueprintのやり取りがかなりスムーズになりますよね!便利!!

⑤Editor起動時に実行

Editor左上の「Edit」→「Project Settings」を選択してProject Settingsを開き
Pluginsの「Python」の項目にある「Startup Scriptss」という配列にPythonファイルのパスまたはファイル名を登録することで、Editor起動時にそのPythonを実行してくれます。
※ファイル名での実行の際は、sys.pathに登録されているパス内にあるもののみになります。

また、Pythonパスとして登録されているパス内にある「init_unreal.py」というPythonファイルは、Project Settingsで指定することなく自動的に実行されます。

※注意

PythonコードをConsoleCommandやBlueprintで実行する際に、独自のPythonファイルをimportする場合、import後にPythonファイルの編集を行っても反映されません!

もし作業中などでPythonファイルを編集しながら実行を行う場合は、「importlib」Moduleで「reload([Module名])」もセットで行うように心がけてください。でないと変更が適応されなくて「あれ?なんかおかしいぞ?」という術中にはまってしまいます。

例:

import py_test
py_test.test_func()

py_test.pyの中身

def test_func():
	print("test")

上記を実行します。

その後にpy_test.pyファイルを編集し

sdef test_func():
	print("PYTEST")

と変更し、もう一度Blueprintを実行します。

変更がありません…(´・ω・`)

では、Blueprint側を以下のように書き換えます。

import py_test
import importlib
importlib.reload(py_test)
py_test.test_func()

そして実行

しっかり編集内容が適応されました!

このように、importとReloadをセットで行うと、「あれ?」というようなことが起こらなくて済むかと思います(reloadは若干処理が重いように感じますが…)

尚、unrealモジュールに関してはreloadを行う必要がありません。

中級編 レベル【★★★】

~Unreal Editor の Python パスについて~

初級編でも何度か話題に出しましたが、Pythonを使う上でmoduleのimportを行う対象となるパスが「Python Editor Script Plugin」ではデフォルトでいくつかサポートされています。

自作の.pyファイルを使う際にパス指定でimport等をしなくても、unrealではデフォルトでプロジェクトの(または独自のプラグインの)「Content」以下に「Python」というフォルダを作ることで、自動的にPythonパスとして有効となります。

また、任意のフォルダーパスをincludeしたい場合は、
Project SettingsのPythonの項目にある「Additional Path」という配列に、パスを指定することで、”次回エディター起動後以降“に自動的にそのパスはPythonで有効となります。

ちなみに、パスを入力するボックスの右にある「…」のアイコンを押すことで、エクスプローラーのフォルダ選択画面になり、直接文字で入力しなくても入力可能です。
また、その際には表記が相対パスになります。

また、Pythonパスを即時有効にしたい場合や、特定のModuleを使うときにだけ有効にしたい場合などの時は、Pythonコードからでも追加できます。

import sys
sys.path.append("[追加したいPythonパス]")

上記コードを実行することで、Moduleの相対パスを追加できます。

また、現在どのPathが登録されているのかは、「sys.path」str配列にすべて記載されています。

↓sys.pathに含まれているパスをすべて出力

結果↓

LogBlueprintUserMessages: [None] C:\Program Files\Epic Games\UE_4.26\Engine\Binaries\ThirdParty\Python3\Win64\python37.zip
LogBlueprintUserMessages: [None] C:\Program Files\Epic Games\UE_4.26\Engine\Binaries\ThirdParty\Python3\Win64\DLLs
LogBlueprintUserMessages: [None] C:\Program Files\Epic Games\UE_4.26\Engine\Binaries\ThirdParty\Python3\Win64\lib
LogBlueprintUserMessages: [None] C:\Program Files\Epic Games\UE_4.26\Engine\Binaries\Win64
LogBlueprintUserMessages: [None] C:\Program Files\Epic Games\UE_4.26\Engine\Binaries\ThirdParty\Python3\Win64
LogBlueprintUserMessages: [None] C:\Program Files\Epic Games\UE_4.26\Engine\Binaries\ThirdParty\Python3\Win64\lib\site-packages
LogBlueprintUserMessages: [None] C:/Users/olgod/Documents/UnrealEngine/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Content/Python
LogBlueprintUserMessages: [None] C:/UE4/KA_Python3_7/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/2D/Paper2D/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Editor/GeometryMode/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Editor/SpeedTreeImporter/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Enterprise/DatasmithContent/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/FX/Niagara/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Lumin/MagicLeap/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Lumin/MagicLeapPassableWorld/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Media/MediaCompositing/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Runtime/AudioSynesthesia/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Runtime/PostSplashScreen/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Runtime/Synthesis/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Experimental/ChaosClothEditor/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Experimental/ChaosNiagara/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Experimental/ChaosSolverPlugin/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Experimental/GeometryCollectionPlugin/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Experimental/GeometryProcessing/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Experimental/MotoSynth/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Experimental/PythonScriptPlugin/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Developer/AnimationSharing/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Runtime/Oculus/OculusVR/Content/Python
LogBlueprintUserMessages: [None] C:/Program Files/Epic Games/UE_4.26/Engine/Plugins/Runtime/Steam/SteamVR/Content/Python
LogBlueprintUserMessages: [None] C:/UE4/KA_Python3_7

~「unreal」モジュールを使ってUnrealEngineの機能を使う~

「Python Editor Script Plugin」では、Pythonコード上で

import unreal

と宣言するだけで、様々なUnrealの機能を使うことができます。
API一覧は以下を参照してください。

Unreal Python API Documentation

基本的に、「Blueprint」で使える関数や参照できるプロパティはすべてunrealモジュールを通してPython内でも使用することができます!すごい!

例:EditorUtilityWidgetのアセットパスからEditorUtilityWidgetを起動させる

import unreal
EUS = unreal.EditorUtilitySubsystem()
EUWBP = unreal.load_object(None,euw_path) #euw_path = /Game/EUW_Test.EUW_Test
EUS.spawn_and_register_tab(EUWBP)

結果↓

ちなみに、プロジェクト内のC++のUCLASSにあるBlueprintで呼び出し可能な関数や、Blueprintで取得・編集可能な変数は自動的にunrealモジュールに登録され、使用することが可能です!

基本的なルールとしましては、C++で定義した関数名の大文字部分を小文字にして、その1文字前に文字が存在する場合は小文字にした文字の前に「_」をつけることで、関数名や変数名として機能します。

実際にやってみましょう。

MyObject.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyObject.generated.h"

/**
 * 
 */
UCLASS()
class KA_PYTHONCPP_API UMyObject : public UObject
{
	GENERATED_BODY()

public:

	UPROPERTY(BlueprintReadWrite)
		FString TestProperty = TEXT("PyTest");

	UFUNCTION(BlueprintCallable)
		void SetTestPropertyValue(FString InString);
	
};

MyObject.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyObject.h"

void UMyObject::SetTestPropertyValue(FString InString)
{
	TestProperty = InString;
}

これをビルドし、Pythonから以下のようなコードを実行します。

import unreal
MyObj = unreal.new_object(type(unreal.MyObject()))
print(MyObj.test_property)

結果↓

そしてもう一つ

import unreal
MyObj = unreal.new_object(type(unreal.MyObject()))
MyObj.set_test_property_value("NewStringValue")
print(MyObj.test_property)

結果↓

このように、特殊な手順などを踏まなくても、自作のクラスの関数や変数を扱うことができます。

上級編 レベル【★★★★】

~C++でのVersionによる分岐方法~

C++でのPythonのVersionでの分岐の際は

#if PY_MAJOR_VERSION < 3
	//Version2の処理
#else
	//Version3の処理
#endif

のような形で分岐ができます。

Versionに関わるDefineは

#define PY_MAJOR_VERSION        3
#define PY_MINOR_VERSION        7
#define PY_MICRO_VERSION        7
#define PY_VERSION              "3.7.7"

あたりが主に使いそうなものだと思います。詳しくは
/Engine/Source/ThirdParty/Python3/Win64/includeにある
「patchlevel.h」内に定義されているのでご覧ください。

~Versionを2に戻す方法(Engineビルドが必要)~

何らかの理由でVersionを2に戻す必要がある場合は、
Python を使用したエディタのスクリプティングに記載されているように
「UE_PYTHON_DIR」にPythonのexeがあるPathを指定しエンジンビルドをする方法があります(この方法なら任意のPythonバージョンに変更可能です)

が、どうせエンジンビルドするなら、それよりも簡単な方法があります。Engine側にある「Python Editor Script Plugin」の
「PythonScriptPlugin.Build.cs」
「PythonScriptPluginPreload.Build.cs」
という2つのファイルを編集します。

編集内容としては、Module名が「Python3」になっている部分を「Python」とするだけです。

PythonScriptPlugin.Build.cs

//途中まで省略
PrivateDependencyModuleNames.AddRange(
				new string[] {
					"Analytics",
					"AssetRegistry",
					"Projects",
					"Python",//デフォルトで"Python3"となっているので、"Python"とする
//以下省略

PythonScriptPluginPreload.Build.cs

//途中まで省略
PrivateIncludePathModuleNames.AddRange(
					new string[] {
						"Python",//デフォルトで"Python3"となっているので、"Python"とする
					}
//以下省略

ちなみにPythonのVersionを確認するには

import platform
print(platform.python_version())

とUnreal上で実行することで、OutputLogにVersionが出力されます。

4.26でのデフォルト状態での実行

Engineコード編集後にビルドしてから実行

4.26の現状では、Python2のデータも残してあるため、モジュールの指定を切り替えるだけでバージョン切り替えができるようです。

開発環境等の理由で戻さなければならない場面などがある場合は活用できるかと思います。

実用変 レベル【★★】

では、実際にどのようなことをPythonで行うのが良いのでしょうか?

「unreal」Moduleにあるほとんどの機能はBlueprintで実装可能です。
なので、unrealModuleを使うだけだとBlueprintで書いた方がイテレーションが速かったりします。

なので、Pythonだからこそできることを行うのがいいのかなと思います。

具体的に言えば、「豊富なLibraryの活用」や「外部ファイル等との連携」かなと思います。

例えば、

「特定のURLを外部のウェブブラウザを使って開く」

みたいなことは、超簡単にできちゃいます。

import webbrowser
browser = webbrowser.get(None)
browser.open(url)

結果↓

外部との連携で一番単純なのは、テキストファイルのExport、Importかなと思います。

例えば出力の場合

import os
try:
os.makedirs(path)
except:
pass
with open(path + "/" + name,mode = "w") as f:
f.write(text_string)

結果↓

以上のようなことが簡単にできてしまいます!

他にも考えようによって様々なことができてしまうので、皆さんも是非Pythonを使って楽しいUnrealライフを送ってみてはいかがでしょうか!?
(Python3ならではの事も書きたかったのですがボリュームたっぷりになってしまったので、また近いうちに!)

以上!

※この記事のサンプルプロジェクトは以下URLにアップされています。

サンプルプロジェクト

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です