【Unity(C#)】ProjectWindowを更新する

　　
　　
　　
この記事は

**『プログラミング完全未経験からUnityでの開発現場に迎え入れてもらえた世界一の幸せ者』**

の記事です。そのつもりでお読みください。


##ProjectWindowを更新
Inspectorの更新をまとめた記事はたくさんあったんですが、
ProjectWindowを更新する方法があまり見受けられなかったので詳しく調べました。


####何のためにProjectWindowを更新したいの？
以前、編集ソフトとか使わずにUnityEditor上でGIF画像をサクッと作りたくて
プレイ中にキー入力で録画できるようなものを作りました。(人が作ったものを改造した)
記事→[こちら](https://qiita.com/OKsaiyowa/items/f96c94173230712840ee)
ダウンロード→[こちら](https://github.com/ForJobOk/GIF_Create)

その際に、
**作成されたGIF画像がProjectWindowに反映されない**
というめんどくさい現象が起きてました。(一度EditorWindowをたたまないと反映されない)

しかも、保存処理の終了するタイミングはPlayMode中とは限らないため、
**メインスレッド(Updateとか)で終了するまで監視するということはできません。**

その結果、
**ProjectWindow上に何かが保存されたタイミングでエディタ拡張を利用してProjectWindowを更新**
↑これをすれば解決するのでは？という仮説にたどり着きました。

結論から言うとできました。(もっと楽勝な方法があれば教えてください喜びます)

##コード

```C#
public class Refresh : Editor
{
    //コンストラクタ
    static Refresh()
    {
        EditorApplication.update += ProjectWindowUpdate;
    }

    [RuntimeInitializeOnLoadMethod]
    static void ProjectWindowUpdate()
    {
        if (/*処理終了のフラグ*/)
        {
            AssetDatabase.Refresh();
        }

        if (EditorApplication.isPlaying ==false && /*処理終了のフラグ*/)
        {
            EditorApplication.update -= ProjectWindowUpdate;
        }
    }
}
```
---

###AssetDatabase.Refresh

`AssetDatabase.Refresh`は　かなりぼんやり説明するとProjectWindowを更新してくれます。
~~正直なところこれを更新したいタイミングで呼び出せばいいだけなんですが、~~
~~コードが上級者向け過ぎて私には理解できず、求めている処理の箇所にたどり着きませんでした。~~
~~なのでこんなめんどくさいこと調べて気付いたら朝の五時です。~~

~~誰か見つけたら教えてください。~~

~~本来は`AssetDatabase.Refresh();`この一行と名前空間書いて終わりです。~~
~~ただのあほやん~~

追記(翌日)
よくよく考えたら、今回の場合、**PlayMode中に保存処理が終了するとは限らない**ので
更新したいタイミングで`AssetDatabase.Refresh`を呼び出すには
UnityEditorを更新する方法で合ってました。


---

`AssetDatabase.Refresh`のUnityのすくりぷとりふぁれんす↓

>何かしら変更があったアセットをすべてインポートします。
コンテンツ内容が何かしら変更されたアセットや、
>プロジェクトフォルダーに追加/削除されたアセットをインポートします。



---

###[RuntimeInitializeOnLoadMethod]
`[RuntimeInitializeOnLoadMethod]`をメソッドの上に書くと
**PlayModeになった瞬間**に一回呼び出されます。

ちなみにコンストラクタはRefreshクラスの初期化処理を行っており、
最初にやっておいてほしい処理(初期設定みたいなもの)を書いてます。
上記コードの場合のコンストラクタは**PlayModeになった瞬間**に一度だけ呼び出されます。

---
###[InitializeOnLoadMethod]
`[InitializeOnLoadMethod]`というのも存在しており、
`[InitializeOnLoadMethod]`をメソッドの上に書くと、
**Editor画面を開いた瞬間(起動時)**と**PlayModeになった瞬間**に一回呼び出されます。

---
###[InitializeOnLoad]
`[InitializeOnLoad]`はクラスの上に書きます。
こんな感じです。

```C#
[InitializeOnLoad]
public class Refresh : Editor
{
    //コンストラクタ
    static Refresh()
    {
        EditorApplication.update += ProjectWindowUpdate;
    }

    static void ProjectWindowUpdate()
    {
        if (/*処理終了のフラグ*/)
        {
            AssetDatabase.Refresh();
        }

        if (EditorApplication.isPlaying ==false && /*処理終了のフラグ*/)
        {
            EditorApplication.update -= ProjectWindowUpdate;
        }
    }
}
```
`[InitializeOnLoad]`を上に書いたクラスは
**Editor画面を開いた瞬間(起動時)**と**PlayModeになった瞬間**に一回呼び出されます。

クラスごと呼び出されるのでコンストラクタも一緒に行われます。
つまり、上記コードの場合、コンストラクタは
**Editor画面を開いた瞬間(起動時)**と**PlayModeになった瞬間**に一度だけ呼び出されます。

---


2019/04/22 追記
##静的コンストラクタ
どうやら、 この記事でまとめた[InitializeOnLoad 属性](https://docs.unity3d.com/ja/current/Manual/RunningEditorCodeOnLaunch.html)は
任意のstaticデータの初期化ついでに、1度だけ実行する必要がある処理に対して使うのが目的のようです。

ですので、文字通り、静的なコンストラクタを作らないとうまく動いてくれませんでした。
要するに**InitializeOnLoad 属性を利用する際はコンストラクタにはstaticをつける必要がある**ということです。

unityのスクリプトリファレンスには
>スタティックコンストラクターは、どのようなスタティック関数やクラスのインスタンスが使用されるよりも先に呼び出されることが保証されていますが、InitializeOnLoad 属性によりエディター起動時に呼び出されることを保証します。

とあります。

---
###EditorApplication.update
`EditorApplication.update`はUnityEditorで更新処理を行うためのデリゲートです。
登録したメソッドがUnityEditor上で**秒間100回呼び出される**ようになります。

ただ、それだと重くなったりするのかな～と思って
更新後、メソッドの登録を解除してます。

あまり重くなったように感じませんがどうなんでしょうか。
呼び出し回数も調整できたりするのかな？
誰か教えてください～




##ProjectWindow変更時のデリゲート
実はProjectWindowの変更時のデリゲートは用意されています。
`EditorApplication.projectChanged`です。
なので、下記でいけるかと思いましたが無理でした。

```C#
[InitializeOnLoad]
public class Refresh : EditorWindow
{
    static Refresh()
    {
        EditorApplication.projectChanged += OnProjectChange;
    }

    static void OnProjectChange()
    {
        AssetDatabase.Refresh();
    }
}
```
`EditorApplication.projectChanged`に関する
Unityのすくりぷとりふぁれんすに`creating`とあったのでこれでいける！と確信しましたが、
これはどうやらEditor上でポチポチ操作したときの話のようです。

実際に`EditorApplication.projectChanged`を利用した上記のコードに変更し、
ProjectWindow上でCreateとかRenameとかしてみたところEditorが更新されました。

##結論

`EditorApplication.projectChanged`に関するUnityのすくりぷとりふぁれんす↓
>Actions that trigger this event include creating, renaming, or reparenting assets, as well as moving or renaming folders in the project. Note that the event is not raised immediately in response to these actions, but rather during the next update of the editor application.
>


**EditorApplication.projectChangedがイベントを渡すのは次にEditorが更新されている最中だよ**
って書いてます。

推測ですが、`Editorが更新されるタイミング`は
**ProjectWindow上のフォルダがProjectWindow上で何かしら変更されたタイミング**です。

なので、**UnityEditor上でスクリプトによって変更が加えられたProjectWindow**に対して
**スクリプトでProjectWindowを更新したい**なら、
`EditorApplication.projectChanged`は使わずに
Editorの更新処理のデリゲートである
**EditorApplication.updateにメソッドを渡す**
しかないってことなんじゃないでしょーか。