【UE5】UE5.3でPyTorchを使う方法

# はじめに
UnrealEngine5上でPytorchを使う方法を紹介します．
様々な方法ありますが，今回はUE5上でPythonコードが書ける方法を紹介します．
PyTorchに限らずPythonのサードパーティーパッケージ何でも可能だと思います(試してはない)．
もちろんnumpyのような標準ライブラリもUE5上で利用可能です．

# 1.UnrealEnginePythonをダウンロードする
まずUE5上でPythonを実行するのに以下のGithubからUnrealEnginePythonをダウンロードします．
友人に頼んで5.3に対応してもらいました．

https://twitter.com/suramaru517/status/1702273323533774946

https://github.com/suramaru517/UnrealEnginePython

githubのリポジトリから緑のCodeボタンを押して
![github.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/ec938877-13d3-abc4-b390-cf82b9713530.png)
Download ZIPでダウンロードできます．
![github2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/d8e787fa-cb10-f3d4-7c55-ad1ed1a3d52a.png)
ダウンロード出来たら解凍しておいてください．

# 2.Python環境を構築するプロジェクトを作成する

新しいプロジェクトを作成します．この際C++を選んでください．
※余談ですがPathに日本語含まれるとエラー吐きますので全てアルファベットにするのが推奨です．
![プロジェクトブラウザ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/c112d9b5-3293-60be-4b70-1c2670a97b47.png)
作成しましたらプロジェクトのフォルダに
![フォルダ1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/2778b23b-2400-9c06-b771-fa7480eb46f4.png)
Pluginsフォルダを作成し
![フォルダ2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/d274e1a4-3416-ead7-d13d-8d8cab9871b5.png)
先ほど解凍したUnrealEnginePythonをPluginsフォルダに移動します．
![フォルダ3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/3be3cd58-44b7-038d-9a1d-762fb6f51a33.png)
プロジェクトを起動するとリビルドを促してくるのでYesを押します．
![リビルド.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/d331ad38-2ffc-3cad-430e-e0811a7279aa.png)

# 3.PythonをUE5で実行する
![UE.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/637f9051-8feb-6464-3cd1-eb49f1a98814.png)
リビルドが終わったらブループリントクラスを作成し，すべてのクラスから「PyActor」を選んでください．
![UE2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/67973f23-1572-5c78-e5b2-fa61e5df9dbf.png)
今回は「BP_PyActor」と命名しました．
![UE3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/c92c8579-a072-a10f-e26f-0ede32f661d6.png)
では，Pythonのコードを書くためにウィンドウからPythonEditorを開きます．
![UE5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/9dccf1ad-5902-f0c5-bfd9-a9aa9b1b45fe.png)
PythonEditorを開いたらNewから新しいPythonファイルを作成します．
![UE6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/ff6fc459-c970-ff64-c60e-93e1eda22628.png)
今回は「PythonFunction」と命名しました．
右下に「再インポートを行いますか？」と表示されるので，インポートを押してください．
![UE10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/92be4e7d-1118-136b-581b-accdf3f9396f.png)
「PythonFunction」に今回のサンプルコードを書きます．
単にTickでZ軸移動しているだけのコードです．
![UE7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/f6c361f5-7e0e-20d9-e583-a9eaeed785db.png)
```Python:PythonFunction
import unreal_engine as ue
from unreal_engine.classes import KismetSystemLibrary

ue.log('Hello i am a Python module')

class PyActor:

    # this is called on game start
    def begin_play(self):
        KismetSystemLibrary.PrintString(InString = "PyActor BeginPlay", Duration = 5.0)
        
    # this is called at every 'tick'    
    def tick(self, delta_time):
        # get current location
        location = self.uobject.get_actor_location()
        # increase Z honouring delta_time
        location.z += 100 * delta_time
        # set new location
        self.uobject.set_actor_location(location)
```
では，「PythonFunction」で書いたコードを実行します．
先ほど作成した「BP_PyActor」を開きます．
![UE8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/2cee2740-e4d9-2562-7e80-224c1043d85c.png)
今回わかりやすいように，Sphereを追加しておきます．
![UE9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/a6df3ff7-a6a2-ba0d-45a1-8e9a7377c152.png)
それではクラスのデフォルトから詳細タブのPythonタブが追加されていますので，
![UE11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/affec72b-0d6d-e572-c540-4d8ff9c86925.png)
PythonModuleにPythonファイルの名前のPythonFunction，
PythonClassにPythonクラスの名前のPyActorを入力します．
![UE12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/4d59bb26-e885-3103-96b7-b0c208242b80.png)
では今作成した「BP_PyActor」をレベル上に置いてみましょう．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/c6e65d0b-dbc1-7695-43d9-452d2072e6c3.png)
それでは実行しますと，SphereがZ軸に移動しました．
またPrintStringも実行したので，左上に文字が出力していることもわかります．
```Python:PythonFunction
#PrintSting
KismetSystemLibrary.PrintString(InString = "PyActor BeginPlay", Duration = 5.0)
```
![UE14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/3324a7ee-4a45-0ca4-1f26-f52066d8329d.png)
PrintStringではなくue.logの出力は左下の「出力ログ」を押すと
![UE15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/dc42ca2d-7449-11ab-1993-5d3508120799.png)
ログを確認することができます．
![UE16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/49a12d35-8d87-8607-2059-87911fb67a58.png)
# 4.PyTorchをUE5で実行する
では最後にPyTorchを実行できるようにPyTorchをインポートします．
今回はこちらを参考にさせていただきました．同じようにするとnumpyをインポートできます．

https://dev.epicgames.com/community/learning/tutorials/lJly/python-install-modules-with-pip-unreal-engine-5-tutorial

まずUEのpython.exeのpathを確認します．
特に設定していなければ
C:\Program Files\Epic Games\UE_5.3\Engine\Binaries\ThirdParty\Python3\Win64
にあると思われます．
![UE17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/43e62d68-fd5b-b3e8-8e39-9368f8d21187.png)
ではcmdを立ち上げます．
Win+Rで検索画面がでるので「cmd」を入力しますと
![cmd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/d1c18a44-7a23-80ce-580d-87ef040c177b.png)
cmdが立ち上がります．
![cmd1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/a96350df-bbd9-682a-a92e-4803e881ea6e.png)
これから，PyTorchをインポートをします．
PyTorchを使ったことがある人は知っていると思いますが，通常は以下の公式からインポートするコマンドをコピーします．

https://pytorch.org/

![pytorch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/0c777f69-642b-b249-3019-8ef67ec4f60e.png)
![cmd2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/b61c8a7f-e225-42d2-c7ee-69ac9b281acb.png)
しかし，pip3はないとエラーを吐くので
![cmd3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/6dc1dbce-1510-0d27-184a-47bfae43b9bd.png)
今回のコマンドはpip3をpipに変えた以下のコマンドを使用します．
""内のpathは先ほど確認したpathです．
```
"C:\Program Files\Epic Games\UE_5.3\Engine\Binaries\ThirdParty\Python3\Win64\python.exe" -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
```
コマンドを入力すると，ダウンロードが始まります．
![cmd4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/f3b22a3d-243d-a7a0-09e8-ac86d86dc212.png)
![cmd5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/ed74de20-21c1-b696-3985-cbbdef34b692.png)
インポート終わったら「PythonFunction」を書き換えます．
ここでPyTorchが使えているかどうかを見るために
```Python
torch.device('cuda' if torch.cuda.is_available() else 'cpu')
```
を追加しPrintStringで出力しています．
```Python:PythonFunction
import torch
import unreal_engine as ue
from unreal_engine.classes import KismetSystemLibrary

DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
ue.log('Hello i am a Python module')

class PyActor:

    # this is called on game start
    def begin_play(self):
        KismetSystemLibrary.PrintString(InString = str(DEVICE), Duration = 5.0)
        
    # this is called at every 'tick'    
    def tick(self, delta_time):
        # get current location
        location = self.uobject.get_actor_location()
        # increase Z honouring delta_time
        location.z += 100 * delta_time
        # set new location
        self.uobject.set_actor_location(location)
```
それでは実行すると，左上に「cuda」と出力されればPyTorchが実行していることがわかります．
![UE18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2677939/fbff3979-2a3f-3abb-186f-489f921eaae2.png)
# さらに具体的なコードを書くには
Pythonの部分はPythonですが，UEに関する関数はUnrealC++と少し異なるので他に関数など使いたい場合は

https://github.com/crazytuzi/UnrealEnginePython

UE5.3改良前のGithubに具体的な手法が載っているのでこちらを参照してください．
Pythonで書いた関数を呼び出す方法など知ることが出来ると思います．