困った時の自分用メモ

読んだ本を考察してメモったり、自分でいじった物の感想をメモったりする場。週1更新を目指します。

Unityの話~エディター拡張を頑張る話4:CSファイルとSceneファイルのオープンと、履歴の保存と一覧表示とそこから起動(盛りだくさん)~

いくらEditor拡張が出来ると言っても、Unity上でのカーソル移動がVIMのようにHJKLマップになるわけでもないし、フォーカスを好きなボタンで移動させるなんて事もない。
やろうと思えばできるだろうけど、コストとリターンが割に合わないなんてことはよくある。
そう考えた時に、多少コストを払ってでも一番欲しい機能はなんだろうと思った時に、VIMで一番革命的だった

開いたファイルを履歴に残しておき、一覧化し、更にそこから選択して開くことが出来る

これのUnity版が絶対に必要だと思った。もう、とっ散らかっているCSとSCENEファイルを探すのはごめんだ。
今回はこれの実装を行うことにした。
ただ、もしかしたらEditor側のNULLバグが発生する可能性があるので、使用は自己責任で。
あ、あとWindows用なので、MAC用は筆者がMAC作業になった時にでも対応します。

〇開いたファイルの履歴新規追加
一番いいのは、Projectウィンドウで開いたファイル情報を取得し、それを保存しておくのが良かったのだが、そのイベントのフック方法が調べてもわからなかったので、やむを得ず開く時に自作のキーマップを叩くと履歴に登録されるようにした。

Open.cs
// 開く
// かつ、履歴に残す
[UnityEditor.MenuItem("ShortCutCommand/VimOpen &[")]
public static void VimOpen() {
    Debug.Log("VimOpen");
    if (PlayerPrefs.HasKey("vim_unite") == false) {
        PlayerPrefs.SetString("vim_unite", "");
    }

    List<string> list = VimModeInfoWindow.GetSaveFileHistory();
    foreach (var val in Selection.assetGUIDs) {
        var path = AssetDatabase.GUIDToAssetPath( val );
        if (path.Contains(".cs")) {
            path = path.Replace("/", "\\");
            System.Diagnostics.Process.Start("MonoDevelop.exe", path);
            //System.Diagnostics.Process.Start("C:\\yamashita\\github\\GVIM\\vim73\\win32\\gvim.exe", "--remote-tab-silent " + path);
            VimModeInfoWindow.AddSaveFileHistory(path);
        } else if (path.Contains(".unity")) {
            EditorSceneManager.SaveOpenScenes();
            EditorSceneManager.OpenScene(path, OpenSceneMode.Single);
            VimModeInfoWindow.AddSaveFileHistory(path);
        }
    }
}
VimModeInfoWindow.cs
public static List<string> GetSaveFileHistory() {
    string saveString = PlayerPrefs.GetString("vim_unite");
    string[] split = saveString.Split("\n"[0]);

    return new List<string>(split);
}

public static void AddSaveFileHistory(string path) {
    List<string> list = VimModeInfoWindow.GetSaveFileHistory();
    if (list.Contains(path)) {
        list.Remove(path);
        list.Insert(0, path);
    } else {
        if (list.Count >= 30) {
            list.RemoveAt(list.Count);
        }
        list.Insert(0, path);
    }

    string saveString = "";
    for (int i = 0; i < list.Count; i++) {
        if (i != 0) {
            saveString += "\n";
        }
        saveString += list[i];
    }
    PlayerPrefs.SetString("vim_unite", saveString);
}

結果だけ言うと、ALT+[で、Projectで選択されている物がCSファイルだったらMONOで開いてくれるし、sceneファイル(.unity)ファイルであれば、今のシーンを強制セーブしてそのシーンを開いてくれる。
以下、長くなるが細かい説明

・PlayerPrefs.*
何と、ゲームの中だけしか使えないと思っていたPlayerPrefsがEditorスクリプトでも使えるようだ。きちんとエディターを閉じても保存されている。EditorPrefsを使わなかった理由は、履歴はプロジェクトを跨ぐ必要が無いためである。

・Selection.assetGUIDs/var path = AssetDatabase.GUIDToAssetPath( val );
今選択しているファイルのGUIDの取得と、それを元にしたAssetsからのパスを返してくれる。複数選択されてれば、その分リストに格納されるけど、ここでは1ファイルずつの想定しかしていないので、複数選択すると多分予期しない挙動を起こすと思う。

・System.Diagnostics.Process.Start("MonoDevelop.exe", path);
外部実行ファイルの呼び出しのようだ。この書き方をする場合、予め環境変数のPATHに、MonoDevelop.exeまでのパスを入力しておかなければならない。もちろん、フルパスでも動く。

・EditorSceneManager.SaveOpenScenes();/EditorSceneManager.OpenScene(path, OpenSceneMode.Single);
シーンの保存と、シーンを開く。これの細かい挙動はリファレンスを参照してください。

とりあえず、設計としては、履歴は30件まで保存でき、開いた事が無い物は新規で登録し、開いたことがあるものはリストの先頭に移動させ、リストが30件を超えたら、その時点で一番古い物が消されるといった感じです。
ここでは、履歴の登録までしかしていないので、次回その情報を用いたウィンドウを作成します。