徳島ゲーム開発ごっこ 技術ブログ

ゲームを作るために役に立ったり立たなかったりする技術を学んでいきます!

【Unity】プロジェクト内のファイルを検索したい

今回は UnityEditorAssetDatabase.FindAssets() に関するお話。
エディタを拡張する際に、プロジェクト内のファイルを検索したいときに使う関数ですね。
この関数の挙動や使い方について確認していこうと思います。
f:id:urahimono:20211011122030p:plain


この記事にはUnity2020.3.20f1を使用しています。
.Netのバージョン設定には.Net4.x を使用しています。

ファイルを検索したいなー

来月のデジゲー博に向けて順調にゲームを開発しているわけですが、開発が進めば進むほどプロジェクト内のファイルが増えてきて、管理が難しくなってきますよね。
開発も進めていかなくてはならないですが、良い環境でゲームは作って生きたのです。
Unity のエディタ拡張などを用いて、ゲーム開発のしやすい環境を整えていきましょう。

ではエディタ拡張をするにあたって、プロジェクト内のファイルってどうやって検索するんだろう?
リファレンスを漁ったところ、AssetDatabase.FindAssets()という関数が出てきましたよ。
docs.unity3d.com

この関数の引数に検索したいファイルの文字列を指定すればいいのですね。
なんと簡単!
気をつけねばならないのは、大文字小文字は区別しないってところですね。
使ってみましょう。

// FindTest.cs
using UnityEngine;
using UnityEditor;
public class FindTest
{
    [MenuItem("MyGame/Find")]
    private static void FindTestMenu()
    {
        // setting というファイルを探してみるよ.
        string[] assets = AssetDatabase.FindAssets("setting");
        Debug.Log($"検索結果:\n{string.Join("\n", assets)}");
    }
} // class FindTest

f:id:urahimono:20211011120648j:plain

思ってたのと違う!
変な英数字が返ってきた!
スクリプトリファレンスを読み直してみましょう。

Returns
string[] Array of matching asset. Note that GUIDs will be returned.

返ってくるの GUID だった。
じゃあ、ちゃんと検索されているんですね。
で、このGUIDでどうしよう……。

GUID をパスに変換するよ

GUID があるってことは、GUIDを何かしらに変換する関数があるはずだ!
見つけたぜ、 AssetDatabase.GUIDToAssetPath() を。
docs.unity3d.com

これを使えばGUIDからファイルパスに変換出来るわけですね。
早速試しますよー!

// FindTest.cs
using UnityEngine;
using UnityEditor;
using System.Linq;
public class FindTest
{
    [MenuItem("MyGame/Find")]
    private static void FindTestMenu()
    {
        // setting というファイルを探してみるよ.
        string[] guids = AssetDatabase.FindAssets("setting");
        string[] paths = guids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToArray();
        Debug.Log($"検索結果:\n{string.Join("\n", paths)}");
    }
} // class FindTest

f:id:urahimono:20211011120702j:plain

こんどは分かりやすいファイルパスが検索出来てますね。
それにしても Assets 以下だけでなく、Packages 以下のファイルも検索できちゃうんですね。

フィルターについてもう少し調べよう

簡単な検索はできましたが、もう少し調べておきましょう。
AssetDatabase.FindAssets() って、Unityエディタ上の Projectビュー の検索ウィンドウと同じなわけでしょ?

f:id:urahimono:20211011120714j:plain

それなら "t:" や "l:" を使ってタイプやラベル毎の検索だってできちゃうはず!

f:id:urahimono:20211011120726g:plain

スクリプト上でも試してみましょう。

// FindTest.cs
using UnityEngine;
using UnityEditor;
using System.Linq;
public class FindTest
{
    [MenuItem("MyGame/Find")]
    private static void FindTestMenu()
    {
        // Sprite ファイルを探してみるよ.
        string[] guids = AssetDatabase.FindAssets("t:Sprite");
        string[] paths = guids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToArray();
        Debug.Log($"検索結果:\n{string.Join("\n", paths)}");
    }
} // class FindTest
f:id:urahimono:20211011120738j:plainf:id:urahimono:20211011120740j:plain

Projectビュー と同じ結果になりましたー。

searchInFolders の引数について調べてみる

AssetDatabase.FindAssets() にはフィルターとは別にもう一つ引数が用意されていまよね。
searchInFolders です。
名前からしてフォルダを指定できるみたいですね。

Specifying one or more folders using the searchInFolders argument will limit the searching to these folders and their child folders. This is faster than searching all assets in all folders.

フォルダは複数指定できるようですし、当然全検索より早そうですね。
さっきから Packages の中身まで検索していたのが気になっていたんですよ。
スクリプトを少し修正してみましょう。

// FindTest.cs
using UnityEngine;
using UnityEditor;
using System.Linq;
public class FindTest
{
    [MenuItem("MyGame/Find")]
    private static void FindTestMenu()
    {
        // Photon フォルダ以下の Sprite ファイルを探してみるよ.
        string[] guids = AssetDatabase.FindAssets("t:Sprite", new string[] { "Photon" });
        string[] paths = guids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToArray();
        Debug.Log($"検索結果:\n{string.Join("\n", paths)}");
    }
} // class FindTest

f:id:urahimono:20211011120756j:plain

あれっ、無いってさ! しかも警告文まで出ちゃってるし。

そういえば、さっきパスの一覧が出たとき、 Assets からの文字列になっていましたね。
ちゃんとルートから指定する必要があるわけですね。

// FindTest.cs
using UnityEngine;
using UnityEditor;
using System.Linq;
public class FindTest
{
    [MenuItem("MyGame/Find")]
    private static void FindTestMenu()
    {
        // Photon フォルダ以下の Sprite ファイルを探してみるよ.
        string[] guids = AssetDatabase.FindAssets("t:Sprite", new string[] { "Assets/Photon" });
        string[] paths = guids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToArray();
        Debug.Log($"検索結果:\n{string.Join("\n", paths)}");
    }
} // class FindTest

f:id:urahimono:20211011120807j:plain

今度こそOKですね。

せっかくなので Object 化も試してみよう。

これで今回の目標である AssetDatabase.FindAssets() の使い方は大体分かりました。
最後にこのパスからファイルをロードする処理までみておきましょうか。

AssetDatabase.LoadAssetAtPath()AssetDatabase.LoadAllAssetsAtPath() がそれっぽいかな。

docs.unity3d.com
docs.unity3d.com

型がわかっているなら、 LoadAssetAtPath() を使って、
型がわからないのなら、 LoadAllAssetsAtPath() で全部持ってくるしかないってところですか。

// FindTest.cs
using UnityEngine;
using UnityEditor;
using System.Linq;
public class FindTest
{
    [MenuItem("MyGame/Find")]
    private static void FindTestMenu()
    {
        // Photon フォルダ以下の Sprite ファイルを探してみるよ.
        string[] guids = AssetDatabase.FindAssets("t:Sprite", new string[] { "Assets/Photon" });
        string[] paths = guids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToArray();

        // とりあえず先頭のパスのファイルを選択状態にする。
        if (paths.Length > 0)
        {
            Object[] objects  = AssetDatabase.LoadAllAssetsAtPath(paths[0]);
            Selection.objects = objects;
        }
    }
} // class FindTest

f:id:urahimono:20211011120818j:plain

簡単ではありますが、ロードする処理が確認できたのでOKかと。

これらの関数を使っていけば、素敵なエディタ拡張が出来そうですね!