【Unity(C#),Python】IronPython使ってUnityでPythonコード動かしてみる

## アドカレ遅刻しました　すみません

この記事は[Python Advent Calendar 2020](https://qiita.com/advent-calendar/2020/python)の20日目の記事です。

大変遅くなりました。申し訳ございません。

## やりたかったこと

過去に書いたPythonコードをUnityで動かしたかったです。

【参考リンク】：[【Python】Qiita投稿記事の総閲覧数をAPI使って自動集計](https://qiita.com/OKsaiyowa/items/8df4b7595f0041dfc695)

アプリにして、個人ID、トークンの入力箇所をUnityのGUI経由で渡してあげれば便利かなーて思ってました。

**結論、Pythonのコードから文字列を受け取ってUnityのコンソールに表示するまでできました。**

過去のIronPythonでは`requestsモジュール`を使うのは
一筋縄ではいかなかったみたいです。

【参考リンク】：[How can I use requests with Ironpython?](https://stackoverflow.com/questions/16171415/how-can-i-use-requests-with-ironpython)


あと、IronPythonに関する過去に投稿された記事が結構出てきたんですが、
いろいろと変更されている箇所が多く、苦しみました。

同じ苦しみを味わう人が減ることを願ってメモします。

## バージョン情報
Unity 2020.2.0a13
IronPython 2.7.11

## 下準備

下記リンク先にPackageにまとめてくれている神がいました。
落としてきてインポートします。
【参考リンク】：[Use Python with Unity 3D – the definitive guide](https://mikalikes.men/use-python-with-unity-3d-the-definitive-guide/)

嫌だ　という人はGithubから取ってきて手動でドラッグアンドドロップです。
(`.msi`がインストーラーなのでそれを落としてくると楽です。)

【リンク】：[IronLanguages/ironpython2](https://github.com/IronLanguages/ironpython2/releases/tag/ipy-2.7.11)

exe以外全部入れとけば動くと思います。
Unityに`Plugins`というフォルダを作って入れときましょう。
![IronPythonSet.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/1c084b72-e9ad-06a9-9e55-cfbd15544092.png)

あとは.NETを4.xに変更すれば完了です。

![IronPythonSettingUnity.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/9ac17c0e-74c2-d03d-ac28-06608f060a17.png)

##Python側のコード

今回は下記のPythonコードを利用しました。
文字リストを返す関数を用意しました。この関数をC#側で呼び出します。

```Python:qiita.py
# coding: UTF-8
import requests
import json
import math
from msvcrt import getch

class ViewCount():

    USER_ID = 'OKsaiyowa'
    PER_PAGE = 20
    allViews = 0

    headers = {"content-type": "application/json",
            'Authorization': 'Bearer APIキー'}

    # リクエスト送って総投稿数が含まれたjsonをぶち込む
    url = 'https://qiita.com/api/v2/users/' + USER_ID
    res = requests.get(url, headers=headers)
    json_qiita_info = res.json()

    # 投稿数をjsonから引っ張り出す
    items_count = json_qiita_info['items_count']

    # ページ数を計算
    page = items_count / PER_PAGE

    def count(self):
        
        info_list = list()
        info_list.append('|記事タイトル|View数|いいねカウント|')

        # 投稿記事を全て取得
        for i in range(int(self.page) + 1):

            # リクエスト送ってそれぞれの記事の情報が含まれたjsonをぶち込む
            url = 'https://qiita.com/api/v2/authenticated_user/items' + \
                '?page=' + str(i + 1)
            res = requests.get(url, headers=self.headers)
            json_qiita_info = res.json()

            for j in range(self.PER_PAGE):
                try:
                    # IDをjsonから引っ張り出す
                    item_id = json_qiita_info[j]['id']

                    # リクエスト送って指定したIDの記事のView数が含まれたjsonをぶち込む
                    url = 'https://qiita.com/api/v2/items/' + str(item_id)
                    res = requests.get(url, headers=self.headers)
                    json_view = res.json()

                    # View数をjsonから引っ張り出す
                    page_view = json_view['page_views_count']

                    # 加算代入して総View数とする
                    self.allViews += page_view

                    # タイトル、いいね数、View数の順に表示
                    info_list.append('| ' + json_qiita_info[j]['title'] + ' | ' +
                        str(json_qiita_info[j]['likes_count']) + ' |' +
                        str(page_view) + ' |')

                except IndexError:
                    info_list.append('View総計:' + str(self.allViews))
                    # 改行
                    info_list = "\n".join(info_list)
                    return info_list
                
        # 改行して出力
        info_list = "\n".join(info_list)
        return info_list

# テスト用
v = ViewCount()
print(v.count())
```
ちょっと雑ですが、このPythonファイルをUnityのAsset直下に配置します。


## C#側のコード

先ほどのPython側のコードから関数を呼び出します。

```C#
using System.Collections.Generic;
using IronPython.Hosting;
using UnityEngine;

/// <summary>
/// シーン上の適当なオブジェクトにアタッチ
/// </summary>
public class QiitaViewCountFromPython : MonoBehaviour {
    
    void Start()
    {
        var engine = Python.CreateEngine();

        ICollection<string> searchPaths = engine.GetSearchPaths();
        
        //使用するファイルのパスを追加していく
        searchPaths.Add(Application.dataPath);
        searchPaths.Add(Application.dataPath + @"\Plugins\Lib\");
        searchPaths.Add(Application.dataPath + @"\Plugins\Lib\site-packages\");
        engine.SetSearchPaths(searchPaths);

        //実行
        dynamic py = engine.ExecuteFile(Application.dataPath + @"\qiita.py");
        
        //クラスを取得
        dynamic viewCount = py.ViewCount();
        //関数が返す文字列をコンソールに表示
        Debug.Log(viewCount.count());
    }
}
```

## 結果
無事出力されました。

![PythonUnityQiitaAllView.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/34072505-aa2d-ecc4-bc93-4e9a73679a21.gif)


## 課題
間に合いませんでしたが、個人ID、トークンの入力箇所を
UnityのGUI経由でPythonに渡すってのをなんとかしたいです。

検証して、でき次第続き書きます。

あともう1つ解決したいこととして、
実行から結果出力まで2分近くかかってしまうところをなんとかしたいです。

1行1行リクエスト送って返ってくるの待っているので仕方ないのかもしれません。
良い方法知ってる方いたら教えてください。

## 参考リンク
[Pythonでリスト（配列）に要素を追加するappend, extend, insert](https://note.nkmk.me/python-list-append-extend-insert/)
[Python クラスについて](https://qiita.com/motoki1990/items/376fc1d1f3d59c960f5c)
[Pythonの文字列の改行方法と便利な操作まとめ](https://www.headboost.jp/python-n-strings/#:~:text=%E6%96%87%E5%AD%97%E5%88%97%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%20join,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82)