UnrealEnginePython プラグインを使ってみた (導入編)

# はじめに

UE4 の UnrealEnginePython プラグインは、文字通り UE4 で Python を使えるようにするというもの。
が、日本語で検証している記事があまりに少ない・・・なので自分で検証してみる。

UnrealEnginePython は github で管理されている。

https://github.com/20tab/UnrealEnginePython

- MIT ライセンス
- UE4 の Editor/Runtime 両方で Python を使えるようにする(Editorだけ使用と限定することも可能)
- エディタ拡張や自動化等の機能が Python で作れる
- ランタイムで使用する場合は、 PyActor, PyPawn 等の専用の継承クラスが用意されている
- Python は 3.x 系 および 2.7 対応
- UE4.12 - 4.17 まで対応
- プラグインを導入すると Python Console, Python Editor が UE4 上で使用可能になる

なにこれしゅごい。

そこに書かれている通り、現在の UE4.17にも対応していて、比較的頻繁に更新されている。
さらにはドキュメントもかなり充実している様子。
https://github.com/20tab/UnrealEnginePython/tree/master/tutorials
https://github.com/20tab/UnrealEnginePython/tree/master/docs
https://github.com/20tab/UnrealEnginePython/tree/master/examples
 
まさに至れり尽くせり。

今回はこれらのドキュメントを読みながら、自分の環境で導入テストしてみる。
なお、自分の環境は以下の通り。

- Win10
- UE4.17.1 (UE4.17.2 でも動作確認済み)
- Python3.6 (Anaconda, 64bit版) 
- VS2017

ちなみに、下記にリリース版が定期的にあげられているので、ビルド環境がない人はそちらを試してみるのがよい。

https://github.com/20tab/UnrealEnginePython/releases

# プラグインの導入
## 1. ソースファイルのダウンロード

https://github.com/20tab/UnrealEnginePython

から最新版をダウンロード。
C++ プロジェクトの Plugins フォルダ以下に放り込む。
ここは UE4 プラグイン共通。

![00_dl_plugin.png](https://qiita-image-store.s3.amazonaws.com/0/138515/1f86118d-1e08-d5fe-abac-cb4db53bd1f4.png)


## 2. Python のパスを指定

プラグイン側に Python のパスをあらかじめ指定しておく必要がある。
(そうしないと、ビルド時にエラーが出る)

UnrealEnginePython/Source/UnrealEnginePython/UnrealEnginePython.Build.cs の下記を編集

```csharp:UnrealEnginePython.Build.cs
using UnrealBuildTool;
using System.IO;
using System.Collections.Generic;

public class UnrealEnginePython : ModuleRules
{

    // leave this string as empty for triggering auto-discovery of python installations...
    private string pythonHome = "D:/Anaconda3";

```
自分の場合は Anaconda なのでそのパスになっているが、要するに Python の dll がおいてある場所を指定すれば良い。

### UnrealEnginePython プラグインを Runtime に加えたくない場合

UnrealEnginePython/UnrealEnginePython.uplugin を開く。
UnrealEnginePython モジュールを Runtime から Editor に変更しておく。

```json:UnrealEnginePython.uplugin
  "Modules": [
    {
      "Name": "UnrealEnginePython",
      "Type": "Editor",
      "LoadingPhase": "Default"
    },
    {
      "Name": "PythonConsole",
      "Type": "Editor",
      "LoadingPhase": "PostDefault"
    },
    {
      "Name": "PythonEditor",
      "Type": "Editor",
      "LoadingPhase": "PostDefault"
    }
```

## 3. プロジェクトをビルドして実行

あとは、いつものように、Generate Visual Studio project files して Development Editor ビルド。

![01_genelate_vsp_file.png](https://qiita-image-store.s3.amazonaws.com/0/138515/f373c1f6-d472-a861-004a-09b35ddbb51d.png)

ビルドが通ったら、早速立ち上げて Plugins が追加されているか確認。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/138515/ec11839b-4248-1173-1438-d042450b89f3.png)

# 簡単な使い方と動作テスト

プラグインが入ったので早速いじってみる。

## Python Console

「ウィンドウ -> ディベロッパーツール -> Python Console」で Python を実行できるコンソールが開く。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/138515/1f0db7f1-c24f-bef1-9e13-f9e5f4bf72dd.png)

こんな感じ。見た目は標準のアウトプットログとほとんど変わらない。
試しにテキトーに打ってみたけどちゃんと動く。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/138515/0018cbbd-5222-eec5-1bec-cf164dae2991.png)

## Python Editor

「ウィンドウ -> Python Editor」で Python 用のエディタが開く。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/138515/c476b395-b8db-45e7-0151-4668e1f429f2.png)

こんな感じのウインドウ。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/138515/b695c69d-5670-83b5-aeed-17f903453407.png)

「New」 を押すと、新しく py ファイルを作って編集が可能になる。
python のスクリプトは Content/Scripts 以下に自動で保存される。
名前の変更は、左のファイル一覧からダブルクリックで変更することができた。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/138515/33384f9c-9532-ecb6-c2a5-25121fc17c62.png)

## スクリプトの実行テスト

例えば、以下のスクリプトファイルを作る。

```py3:TestScript.py
import unreal_engine as ue
from unreal_engine.classes import Actor, SceneComponent, StaticMeshComponent, StaticMesh
from unreal_engine import FVector

# EditorWrold 取得
editor_world = ue.get_editor_world()

# Actor を Spawn
new_actor = editor_world.actor_spawn(Actor);
# ラベル設定(アウトライナから見える名前)
new_actor.set_actor_label('TestActor');
# Rootコンポーネント追加
root_comp = new_actor.add_actor_root_component(SceneComponent, 'Root');
# StaticMeshComponent を追加
mesh_comp = new_actor.add_actor_component(StaticMeshComponent, 'StaticMesh', root_comp);
# メッシュをセット
mesh_comp.StaticMesh = ue.load_object(StaticMesh, '/Engine/VREditor/BasicMeshes/SM_Ball_01');
# 100.0, 100.0, 100.0 の位置に移動
new_actor.set_actor_location(FVector(100.0, 100.0, 100.0));

```
Python Editor の Execute ボタンを押して実行。
もしくは、Python Console で以下を記述して実行する。

```py3:Console
unreal_engine.exec('TestScript.py')
```

実行した結果。

![02_test_result.png](https://qiita-image-store.s3.amazonaws.com/0/138515/4f3e1177-5e2d-c92d-e64e-ab723874cc14.png)

# おまけ

## 機能を探すとき

まず、ドキュメントやexample, testsをあさる。
それ以外に、直にソースコード読んだほうがわかりやすいときもある。
特に、UnrealEnginePython/Private/UEPyModule.cpp は、
バインドされている関数やEnum定義が記述されているので、探すときの取っ掛かりになりやすい。

## Python Editor の使い勝手

正直に言って、使いにくい。
特に、エディタとして基本的なところで不具合があったり、使いにくいと感じることが多い。
例えば、

- Ctrl + S で保存とあるのに、ショートカットが効かない
- ディレクトリの Delete が実行されない
- ディレクトリが1つもない状態で New Directory すると、ディレクトリは作られるものの、Python Editor 側で反映されない
    - (1つでもディクレクトリが存在していれば正常に動作するようだが・・・)
- ファイルが未保存かどうかの印(いわゆるアスタリスクマーク)は特に付かない
- 外部から Content/Scripts 以下のフォルダ構成をいじった場合に即時反映されない(一度閉じる必要がある)

などなど・・・。
なので、本格的に使用する場合は、エディタは自分が使い慣れているものを使用したほうが良さそう。

## UPythonScript アセットに関して

スクリプトの実行は基本的に py ファイルさえあれば実行可能なのだが、
スクリプトはそのまま UE上にアセットとしてインポートすることも可能らしい。

インポートすると Python マークがついたファイルがコンテンツブラウザ上で見えるようになる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/138515/3cd5e42f-8e7d-1d43-e121-edda868d849c.png)

アセットを開くとこんな感じ。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/138515/ae26c162-8cca-9c71-3e96-f2de6115e40c.png)

UPythonScript のソースを読んでみる。

```cpp:PythonScript.h

#pragma once

#include "PythonScript.generated.h"

UCLASS(MinimalAPI)
class UPythonScript : public UObject
{
	GENERATED_BODY()

public:

	UPROPERTY(EditAnywhere, Category = "Python")
	FString ScriptPath;

	UPROPERTY(EditAnywhere, Category = "Python")
	FString FunctionToCall;

	UPROPERTY(EditAnywhere, Category = "Python")
	TArray<FString> FunctionArgs;

	UFUNCTION()
	void Run();

	UFUNCTION()
	void RunSandboxed();

	void CallSpecificFunctionWithArgs();
};
```

どうやら、スクリプトのパスと関数名、引数情報をセットでアセット化しているだけみたい。
(スクリプトの中身が保存されているわけではない)
BP公開されていないようだが、Run 関数を呼ぶことで指定の関数を実行できる様子。