Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unity Searchを拡張して高度な検索型UIを実現する

trapezoid
December 01, 2023

Unity Searchを拡張して高度な検索型UIを実現する

Unity SearchはUnity Editor上で標準で提供されるようになった、高度なアセット検索機能です。 しかし、実はUnity Searchは独自に拡張することが可能な構造になっており、単なるアセット検索機能に留まらず、高度な検索型のUIを提供するための基盤としての可能性も秘めています。 本講演では、マスタデータに対してUnity Searchによる検索を可能にする拡張を実装した事例や、バージョン間のアセット差分をUnity Searchで表示できるようにした事例を紹介し、 Unity Searchを基盤とすることでどのような可能性が開けるのか、そしてそれはどのように実現できるのかを解説します。

trapezoid

December 01, 2023
Tweet

More Decks by trapezoid

Other Decks in Programming

Transcript

  1. © DeNA Co., Ltd. 1 Unity Searchを拡張して 高度な検索型UIを実現する 大竹 悠人

    (Haruto Otake) ゲームサービス事業本部開発事業部第一技術部テクノロジー推進第二G 株式会社ディー・エヌ・エー
  2. © DeNA Co., Ltd. 2 登壇者プロフィール • 大竹 悠人(Haruto Otake)

    ◦ Unity向けの様々な内製ライブラリの実装・保守 ◦ Unity製タイトルへの様々な技術サポート • 株式会社ディー・エヌ・エー ◦ ゲームサービス事業本部 開発事業部 第一技術部 テクノロジー推進第二G ▪ ゲーム開発系横断部門
  3. © DeNA Co., Ltd. 4 Unity Searchとは? • Unity Editorが標準で提供している高度なアセット検索システム

    ◦ Unity 2021.2 未満ではcom.unity.quicksearchパッケージの導入が必要 • Window > Search > New Windowや、Ctrl + Kで検索ウィンドウを起動 • 条件をクエリ式として入力すると、それを満たすアセットなどを一覧表示する
  4. © DeNA Co., Ltd. 5 Unity Searchの検索対象と構文 • 検索機能はSearchProviderによって提供され、多様な対象の検索をサポート ◦

    Project(プロジェクト内のアセット) p:Query ◦ Hierarchy(シーン中のオブジェクト) h:Query ◦ Settings(Preferences/ProjectSettingsの項目) set:Query ◦ Menu (MenuItemの項目) m:Query ◦ Static API(C#コードのStatic関数を実行) api:Query ◦ Calculator(クエリ式を計算式として評価) =Query ◦ Asset Store(アセットストアのアセット) store:Query • 一部のProviderではFilterIdの記述を省略可能(横断検索が可能) • QueryはProvider毎にそれぞれ異なる解釈がされ、検索結果の絞り込みがされる FilterIdQuery Provider別検索の構文 Query 横断検索の構文
  5. © DeNA Co., Ltd. 6 検索式(Search Expression) • 通常のクエリの上位の構文として、検索式という構文が存在する ◦

    通常のクエリに対してLINQのような演算を、S式のような関数志向で行える ◦ 複数のクエリ間の集合演算、絞り込み、グループ化等の複雑な検索が可能 • 演算子に対して引数として任意のクエリか任意の検索式を与える ◦ 組み合わせることで高度なクエリを実現できる ◦ Providerの種別毎のクエリと独立した構文で複雑な条件を記述できる • 即値を扱うこともできる operator{arg1, arg2, …} 演算子の構文 [123, 123.45, string, …] 即値の例
  6. © DeNA Co., Ltd. 7 検索式による集合演算 • union{クエリ1, クエリ2, クエリ3,

    …} ◦ 引数のクエリの結果すべてを結合した和集合を算出する • except{クエリ1, クエリ2, クエリ3, …} ◦ 第1引数のクエリの結果からそれ以降のクエリの結果を除去した差集合を算出する • intercept{クエリ1, クエリ2, クエリ3, …} ◦ 引数のクエリすべての結果で共通する積集合を算出する
  7. © DeNA Co., Ltd. 8 セレクタを伴う検索式 • 結果の特定のプロパティを扱う演算子では、セレクタを用いる ◦ @セレクタ名

    の形で指定。@id, @label, @desc, @valueは常に利用可能 ◦ アセットに対しては@name, @size, @path, @type, @extension等も利用可能 • where{クエリ, 論理式} ◦ クエリの結果を論理式の評価値がtrueになるものだけに絞り込む ◦ where{t: Texture2D, @size > 4000} で 4KB以上のテクスチャのみを表示 • …groupBy{クエリ, キーの抽出式} ◦ クエリの結果をキーの抽出式の評価値毎にグループ化(UI上にタブが作成される) ◦ …groupBy{p: *, @type} で全アセットをアセット種別ごとにグループ化
  8. © DeNA Co., Ltd. 9 クエリを展開する(サブクエリ) • クエリの中で{ }, []で囲んでサブクエリを記述すると、複数のクエリに展開される

    ◦ { }の箇所に値を埋め込んだクエリが複数発行され、その全ての結果の和集合になる • 100件の結果を持つクエリをサブクエリにすると、展開後のクエリは100回発行される union{ t:Prefab @type=”Prefab”, t:Prefab @type=”Material” } 等価な表現 t:Prefab @type=[Prefab,Material] クエリ
  9. © DeNA Co., Ltd. 11 SearchAction • 検索結果に対して、様々なアクションを実行することができる機能 ◦ アセットを選択

    / 開く / Reimport などなど.... ◦ 検索結果をダブルクリックしたときのアクションは、設定でカスタマイズが可能
  10. © DeNA Co., Ltd. 12 Unity Searchを利用して独自の検索システムを構築する • SearchProviderを独自に定義すると、検索できる対象を任意に拡張することが可能 ◦

    アクション/カラムなども同様に独自に定義し、拡張することが可能 • 検索式は多くの部分がSearchProviderの外側で処理される為、必要な対応はわずか • 任意の対象に対する検索システムを • (テーブルも含む)高度なUIやクエリ式を持ち、結果に対するアクションも可能な形で • 簡単に提供できる
  11. © DeNA Co., Ltd. 14 Unity Searchを拡張する為の主なオブジェクト • SearchProvider ◦

    下記の様々なオブジェクトを生成する責務を負う、エントリポイント • SearchItem ◦ 1件の検索結果を表すオブジェクト。何らかのデータを紐づけられる • SearchColumn ◦ 1列の検索カラムを表すオブジェクト。カラムに応じた値の抽出/描画を行う • SearchAction ◦ SearchItemに対して実行できる処理を定義するオブジェクト • SearchSelector ◦ クエリ式の演算子で扱うプロパティの値の解決方法を記述する関数
  12. © DeNA Co., Ltd. 15 (ほぼ)なにもしないSearchProviderの最小限の実装 • エントリポイントとなるSearchProviderを用意 ◦ SearchItemProvider属性付きの静的メソッドで、

    SearchProviderのインスタンスを返すように実装 ◦ Unity Searchに自動的に呼び出され、 返り値のSearchProviderが登録される • SearchProviderに必要な設定を記述していく ◦ 振る舞いもdelegateとして代入する ◦ 様々な振る舞いが指定できるが、主要なのは 検索結果を返す fetchItems プロパティ
  13. © DeNA Co., Ltd. 16 SearchProviderの最小限の実装 : 検索結果の生成 • SearchContextからクエリ文字列を参照可能

    ◦ これを元に必要な結果を生成する • SearchItemとして検索結果を生成し、リストに追加 ◦ SearchItemには任意のデータを紐づけられる
  14. © DeNA Co., Ltd. 17 SearchColumnによるカラム定義 • TableViewでのカラムを独自に定義する • SearchProviderのfetchColumnsに、

    SearchColumnを列挙するdelegateを指定 ◦ 第3引数にカラム種別を指定 ◦ カラムの振る舞いはここでは持たない • 対象のSearchProviderによる検索時、 TableViewでカラムの追加が可能になる
  15. © DeNA Co., Ltd. 19 SearchActionによるアクションの定義 • SearchActionsProvider属性を設定した 関数でSearchActionを列挙 •

    SearchProviderのProviderIDを指定すると、 その結果に対するSearchActionとして認識 ◦ ハンドラ側に渡ってくる引数から、 実行元のSearchItemを取得できる
  16. © DeNA Co., Ltd. 20 SearchSelectorによるセレクタの定義 • 検索式のセレクタも独自に定義することが可能 • セレクタの評価結果を返す関数に、

    SearchSelector属性でその対象を指定 • 対象のセレクタ名は正規表現で指定できる ◦ マッチ結果を取ることもできるが、 internalプロパティのため工夫が必要(後述)
  17. © DeNA Co., Ltd. 22 バージョン間のアセット差分をリスト表示する • 背景 ◦ モバイルゲームのアセット開発では、データ解析への対策の為、

    意図しないアセットがリリース対象にならないことを保証する必要がある • 要求 ◦ 前回のアセット更新から、どんなアセットが新たに追加されたかを確認したい ◦ 含まれる全アセットや、含まれないアセットの確認も合わせて行いたい ◦ サムネイルなどを伴う、ビジュアライズされた形で確認したい ◦ 結果をファイルパスや種別などの任意のフィルタで絞りこみたい ◦ テキストファイルとして結果を出力したい
  18. © DeNA Co., Ltd. 23 Unity Seachを介せば、概ねすべての要求を満たせる • 前回のアセット更新から、どんなアセットが新たに追加されたかを確認したい ◦

    exceptを用いたクエリで実現可能 • 含まれる全アセットや、含まれないアセットの確認も行いたい ◦ exceptを用いたクエリで実現可能 • サムネイルなどを伴う、ビジュアライズされた形で確認したい ◦ Unity EditorでPreviewされるものはすべてサムネイル表示可能 • 結果をファイルパスや種別などの任意のフィルタで絞りこみたい ◦ クエリによるフィルタ/where演算子で実現可能 • テキストファイルとして結果を出力したい ◦ 検索結果はCSV/JSONとしてエクスポート可能
  19. © DeNA Co., Ltd. 24 Unity Searchの結果に出力する為の実装はこれだけ • 改行区切りでアセットのパスがリスト化されたテキストファイルを読み込んで、 されたアセットを検索結果として出力するだけのSearchProviderを実装

    ◦ AssetInList(ail:) SearchProviderとして実装 • アセットビルド対象になったアセットのパスを、上記の改行区切り形式でファイル出力 ◦ このファイルもビルド成果物としてビルドサーバに保存しておく Assets/Karting/Art/Models/Pipe.fbx Assets/Karting/Art/Models/Player.FBX Assets/Karting/Art/Models/ramp.fbx Assets/Karting/Art/Models/TrackCamber.fbx Assets/Karting/Art/Models/TrackCamberCurve.fbx ver1.txt
  20. © DeNA Co., Ltd. 25 バージョン間のアセットの差分をクエリで表現する • アセットリストファイルを準備する ◦ 旧バージョンがver1.txt

    , 新バージョンがver2.txt として保存する ◦ 現在のすべてのアセットリストを、all.txtとして保存する • 新しく含まれるアセットは、新バージョンから旧バージョンのものを除外して表示 ◦ except{ail:ver2.txt, ail:ver1.txt} • 削除された既存アセットは、旧バージョンから新バージョンのものを除外して表示 ◦ except{ail:ver1.txt, ail:ver2.txt} • 対象外の全アセットは、すべてのアセットから新バージョンのものを除外して表示 ◦ except{ail:all.txt, ail:ver2.txt}
  21. © DeNA Co., Ltd. 26 より複雑な条件をクエリで表現する • 新しく含まれるアセットのうち、特定のパス以下のアセットのみを表示する ◦ where{except{ail:ver2.txt,

    ail:ver1.txt}, @path:Assets/Parent} • 新しく含まれるアセットのうち、特定のパス以下のPrefabのみを表示する ◦ where{except{ail:ver2.txt, ail:ver1.txt}, @path:Assets/Parent and @type=Prefab} • 検索式を前提にすることで、最小の実装で複雑な要求に答えることができている
  22. © DeNA Co., Ltd. 27 AssetInListFileSearchProviderの実装 • typeに親のSearchProviderのIDを指定すると、 親の挙動をほぼ全て継承できる ◦

    カラム/アクション/セレクタ等 • 標準アセット検索を提供するAssetProviderを親に指定 • fetchItemsのみを独自に定義 ◦ クエリを元にリストファイルを読み込む ◦ AssetProviderと同じSearchItemを生成する
  23. © DeNA Co., Ltd. 28 Unity Searchのinternalなメソッドへのアクセス • AssetProviderのSearchItemの生成メソッドはinternal ◦

    何らかの形でinternalの突破が必要 • InternalVisibleToが公式の拡張パッケージである Unity Search Extensionsに対して指定されている • Assembly Reference(asmref)を利用して Unity Search ExtensionsのasmdefにWrapperを追加 • Unity Searchは他にもinternalなAPIを数多く持つ ◦ 必要に応じて同じ手段を取ると、 高度な拡張が簡単に可能になることも • できれば公式にpublicにしてほしい...
  24. © DeNA Co., Ltd. 29 Unity Search上でマスタデータに対する検索を可能にする • DeNAの社内システムであるOyakataで管理されているマスタデータを Unity

    Search上から直接検索して、内容を閲覧できるようにする ◦ 列と行からなる、表で表現できるマスタデータを対象とする • SpreadsheetやExcelに置き換えて考えることは十分可能
  25. © DeNA Co., Ltd. 30 Unity Search上でマスタデータに対する検索を可能にする • クエリで表示したいテーブル名を受け取り、表の内容をAPI経由で取得して表示 ◦

    ワイルドカード(*)による複数テーブルの取得にも対応させる • 対象のテーブルの各行のデータをSearchItemに紐づけて結果として表示 • 対象のテーブルの各列をSearchColumn及びSearchSelectorとして扱えるように生成 • 該当行を編集するページのURLをブラウザで開くSearchActionを生成 oyakata:TableName クエリの構文
  26. © DeNA Co., Ltd. 31 非アセットに対してのインスペクタの描画 • toObjectに設定したdelegateからUnityEngine.Objectを 返すと、選択時に右ペインにそのEditorが表示される •

    ScriptableObjectを介してとAsset以外も扱う手法 ◦ toObject時にSOを生成し、値を移し替える ◦ 各行のインスペクタ風表示をこの手法で実現
  27. © DeNA Co., Ltd. 33 まとめ • Unity Searchは検索システムの構築基盤であり、マルチカラムの情報を扱う高度なUIと、 複雑な検索を柔軟に構成できるクエリシステムを、様々なデータソースに提供する

    • SearchProviderを独自に実装することで、Unity Searchによる検索システム基盤上に、 任意のデータソースを接続することができる • Unity Searchを通して課題解決を行うことで、 大きな課題を小さな実装で解決することが可能になる