0.9K Views
August 06, 24
スライド概要
Unity SearchはUnity Editor上で標準で提供されるようになった、高度なアセット検索機能です。
しかし、実はUnity Searchは独自に拡張することが可能な構造になっており、単なるアセット検索機能に留まらず、高度な検索型のUIを提供するための基盤としての可能性も秘めています。
本講演では、マスタデータに対してUnity Searchによる検索を可能にする拡張を実装した事例や、バージョン間のアセット差分をUnity Searchで表示できるようにした事例を紹介し、 Unity Searchを基盤とすることでどのような可能性が開けるのか、そしてそれはどのように実現できるのかを解説します。
登壇者 : 大竹 悠人 / 株式会社ディー・エヌ・エー
---------
Unity UI 完全に理解した勉強会
https://meetup.unity3d.jp/jp/events/1411
動画アーカイブ : https://www.youtube.com/watch?v=QF2iNTscjYA
DeNA が社会の技術向上に貢献するため、業務で得た知見を積極的に外部に発信する、DeNA 公式のアカウントです。DeNA エンジニアの登壇資料をお届けします。
Unity Searchを拡張して 高度な検索型UIを実現する 大竹 悠人 (Haruto Otake) ゲームサービス事業本部開発事業部第一技術部テクノロジー推進第二G 株式会社ディー・エヌ・エー © DeNA Co., Ltd. 1
登壇者プロフィール ● ● 大竹 悠人(Haruto Otake) ○ Unity向けの様々な内製ライブラリの実装・保守 ○ Unity製タイトルへの様々な技術サポート 株式会社ディー・エヌ・エー ○ ゲームサービス事業本部 開発事業部 第一技術部 テクノロジー推進第二G ■ © DeNA Co., Ltd. ゲーム開発系横断部門 2
目次 1 Unity Searchの機能と構造 2 小さなSearchProviderを実装する 3 実践的なSearchProviderの例と実装ノウハウ © DeNA Co., Ltd. 3
Unity Searchとは? ● Unity Editorが標準で提供している高度なアセット検索システム ○ Unity 2021.2 未満ではcom.unity.quicksearchパッケージの導入が必要 ● Window > Search > New Windowや、Ctrl + Kで検索ウィンドウを起動 ● 条件をクエリ式として入力すると、それを満たすアセットなどを一覧表示する © DeNA Co., Ltd. 4
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別検索の構文 FilterIdQuery 横断検索の構文 Query ● 一部のProviderではFilterIdの記述を省略可能(横断検索が可能) ● QueryはProvider毎にそれぞれ異なる解釈がされ、検索結果の絞り込みがされる © DeNA Co., Ltd. 5
検索式(Search Expression) ● ● ● 通常のクエリの上位の構文として、検索式という構文が存在する ○ 通常のクエリに対してLINQのような演算を、S式のような関数志向で行える ○ 複数のクエリ間の集合演算、絞り込み、グループ化等の複雑な検索が可能 演算子に対して引数として任意のクエリか任意の検索式を与える ○ 組み合わせることで高度なクエリを実現できる ○ Providerの種別毎のクエリと独立した構文で複雑な条件を記述できる 即値を扱うこともできる © DeNA Co., Ltd. 演算子の構文 即値の例 operator{arg1, arg2, …} [123, 123.45, string, …] 6
検索式による集合演算 ● union{クエリ1, クエリ2, クエリ3, …} ○ ● except{クエリ1, クエリ2, クエリ3, …} ○ ● 引数のクエリの結果すべてを結合した和集合を算出する 第1引数のクエリの結果からそれ以降のクエリの結果を除去した差集合を算出する intercept{クエリ1, クエリ2, クエリ3, …} ○ © DeNA Co., Ltd. 引数のクエリすべての結果で共通する積集合を算出する 7
セレクタを伴う検索式 ● ● ● 結果の特定のプロパティを扱う演算子では、セレクタを用いる ○ @セレクタ名 の形で指定。@id, @label, @desc, @valueは常に利用可能 ○ アセットに対しては@name, @size, @path, @type, @extension等も利用可能 where{クエリ, 論理式} ○ クエリの結果を論理式の評価値がtrueになるものだけに絞り込む ○ where{t: Texture2D, @size > 4000} で 4KB以上のテクスチャのみを表示 …groupBy{クエリ, キーの抽出式} © DeNA Co., Ltd. ○ クエリの結果をキーの抽出式の評価値毎にグループ化(UI上にタブが作成される) ○ …groupBy{p: *, @type} で全アセットをアセット種別ごとにグループ化 8
クエリを展開する(サブクエリ) ● クエリの中で{ }, []で囲んでサブクエリを記述すると、複数のクエリに展開される ○ ● { }の箇所に値を埋め込んだクエリが複数発行され、その全ての結果の和集合になる 100件の結果を持つクエリをサブクエリにすると、展開後のクエリは100回発行される クエリ t:Prefab @type=[Prefab,Material] 等価な表現 union{ t:Prefab @type=”Prefab”, t:Prefab @type=”Material” } © DeNA Co., Ltd. 9
TableView ● マルチカラムのテーブル形式で検索結果を表示する機能 ○ 検索結果の様々な属性値をカラム(SearchColumn)として追加できる ○ カラムを独自に定義する事もできる カラムを追加 TableViewを 有効化 © DeNA Co., Ltd. 10
SearchAction ● 検索結果に対して、様々なアクションを実行することができる機能 © DeNA Co., Ltd. ○ アセットを選択 / 開く / Reimport などなど.... ○ 検索結果をダブルクリックしたときのアクションは、設定でカスタマイズが可能 11
Unity Searchを利用して独自の検索システムを構築する ● SearchProviderを独自に定義すると、検索できる対象を任意に拡張することが可能 ○ アクション/カラムなども同様に独自に定義し、拡張することが可能 ● 検索式は多くの部分がSearchProviderの外側で処理される為、必要な対応はわずか ● 任意の対象に対する検索システムを ● (テーブルも含む)高度なUIやクエリ式を持ち、結果に対するアクションも可能な形で ● 簡単に提供できる © DeNA Co., Ltd. 12
小さなSearchProviderを実装する © DeNA Co., Ltd. 13
Unity Searchを拡張する為の主なオブジェクト ● SearchProvider ○ ● SearchItem ○ ● 1列の検索カラムを表すオブジェクト。カラムに応じた値の抽出/描画を行う SearchAction ○ ● 1件の検索結果を表すオブジェクト。何らかのデータを紐づけられる SearchColumn ○ ● 下記の様々なオブジェクトを生成する責務を負う、エントリポイント SearchItemに対して実行できる処理を定義するオブジェクト SearchSelector ○ © DeNA Co., Ltd. クエリ式の演算子で扱うプロパティの値の解決方法を記述する関数 14
(ほぼ)なにもしないSearchProviderの最小限の実装 ● エントリポイントとなるSearchProviderを用意 ○ SearchItemProvider属性付きの静的メソッドで、 SearchProviderのインスタンスを返すように実装 ○ Unity Searchに自動的に呼び出され、 返り値のSearchProviderが登録される ● SearchProviderに必要な設定を記述していく ○ 振る舞いもdelegateとして代入する ○ 様々な振る舞いが指定できるが、主要なのは 検索結果を返す fetchItems プロパティ © DeNA Co., Ltd. 15
SearchProviderの最小限の実装 : 検索結果の生成 ● SearchContextからクエリ文字列を参照可能 ○ ● これを元に必要な結果を生成する SearchItemとして検索結果を生成し、リストに追加 ○ © DeNA Co., Ltd. SearchItemには任意のデータを紐づけられる 16
SearchColumnによるカラム定義 ● TableViewでのカラムを独自に定義する ● SearchProviderのfetchColumnsに、 SearchColumnを列挙するdelegateを指定 ● ○ 第3引数にカラム種別を指定 ○ カラムの振る舞いはここでは持たない 対象のSearchProviderによる検索時、 TableViewでカラムの追加が可能になる © DeNA Co., Ltd. 17
SearchColumnProviderによるカラムの振る舞いの定義 ● SearchColumnProvider属性を設定した 関数でカラム種別毎の振る舞いを指定 ○ カラム種別を属性の引数にする ○ 値の取得処理や、 カラムの描画処理を定義 © DeNA Co., Ltd. 18
SearchActionによるアクションの定義 ● SearchActionsProvider属性を設定した 関数でSearchActionを列挙 ● SearchProviderのProviderIDを指定すると、 その結果に対するSearchActionとして認識 ○ ハンドラ側に渡ってくる引数から、 実行元のSearchItemを取得できる © DeNA Co., Ltd. 19
SearchSelectorによるセレクタの定義 ● 検索式のセレクタも独自に定義することが可能 ● セレクタの評価結果を返す関数に、 SearchSelector属性でその対象を指定 ● 対象のセレクタ名は正規表現で指定できる ○ マッチ結果を取ることもできるが、 internalプロパティのため工夫が必要(後述) © DeNA Co., Ltd. 20
実践的なSearchProviderの例とノウハウ © DeNA Co., Ltd. 21
バージョン間のアセット差分をリスト表示する ● 背景 ○ モバイルゲームのアセット開発では、データ解析への対策の為、 意図しないアセットがリリース対象にならないことを保証する必要がある ● 要求 © DeNA Co., Ltd. ○ 前回のアセット更新から、どんなアセットが新たに追加されたかを確認したい ○ 含まれる全アセットや、含まれないアセットの確認も合わせて行いたい ○ サムネイルなどを伴う、ビジュアライズされた形で確認したい ○ 結果をファイルパスや種別などの任意のフィルタで絞りこみたい ○ テキストファイルとして結果を出力したい 22
Unity Seachを介せば、概ねすべての要求を満たせる ● 前回のアセット更新から、どんなアセットが新たに追加されたかを確認したい ○ ● 含まれる全アセットや、含まれないアセットの確認も行いたい ○ ● Unity EditorでPreviewされるものはすべてサムネイル表示可能 結果をファイルパスや種別などの任意のフィルタで絞りこみたい ○ ● exceptを用いたクエリで実現可能 サムネイルなどを伴う、ビジュアライズされた形で確認したい ○ ● exceptを用いたクエリで実現可能 クエリによるフィルタ/where演算子で実現可能 テキストファイルとして結果を出力したい ○ © DeNA Co., Ltd. 検索結果はCSV/JSONとしてエクスポート可能 23
Unity Searchの結果に出力する為の実装はこれだけ ● 改行区切りでアセットのパスがリスト化されたテキストファイルを読み込んで、 されたアセットを検索結果として出力するだけのSearchProviderを実装 ○ ● AssetInList(ail:) SearchProviderとして実装 アセットビルド対象になったアセットのパスを、上記の改行区切り形式でファイル出力 ○ このファイルもビルド成果物としてビルドサーバに保存しておく ver1.txt 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 © DeNA Co., Ltd. 24
バージョン間のアセットの差分をクエリで表現する ● ● アセットリストファイルを準備する ○ 旧バージョンがver1.txt , 新バージョンがver2.txt として保存する ○ 現在のすべてのアセットリストを、all.txtとして保存する 新しく含まれるアセットは、新バージョンから旧バージョンのものを除外して表示 ○ ● 削除された既存アセットは、旧バージョンから新バージョンのものを除外して表示 ○ ● except{ail:ver2.txt, ail:ver1.txt} except{ail:ver1.txt, ail:ver2.txt} 対象外の全アセットは、すべてのアセットから新バージョンのものを除外して表示 ○ © DeNA Co., Ltd. except{ail:all.txt, ail:ver2.txt} 25
より複雑な条件をクエリで表現する ● 新しく含まれるアセットのうち、特定のパス以下のアセットのみを表示する ○ ● 新しく含まれるアセットのうち、特定のパス以下のPrefabのみを表示する ○ ● where{except{ail:ver2.txt, ail:ver1.txt}, @path:Assets/Parent} where{except{ail:ver2.txt, ail:ver1.txt}, @path:Assets/Parent and @type=Prefab} 検索式を前提にすることで、最小の実装で複雑な要求に答えることができている © DeNA Co., Ltd. 26
AssetInListFileSearchProviderの実装 ● typeに親のSearchProviderのIDを指定すると、 親の挙動をほぼ全て継承できる ○ カラム/アクション/セレクタ等 ● 標準アセット検索を提供するAssetProviderを親に指定 ● fetchItemsのみを独自に定義 © DeNA Co., Ltd. ○ クエリを元にリストファイルを読み込む ○ AssetProviderと同じSearchItemを生成する 27
Unity Searchのinternalなメソッドへのアクセス ● AssetProviderのSearchItemの生成メソッドはinternal ○ ● 何らかの形でinternalの突破が必要 InternalVisibleToが公式の拡張パッケージである Unity Search Extensionsに対して指定されている ● Assembly Reference(asmref)を利用して Unity Search ExtensionsのasmdefにWrapperを追加 ● Unity Searchは他にもinternalなAPIを数多く持つ ○ 必要に応じて同じ手段を取ると、 高度な拡張が簡単に可能になることも ● できれば公式にpublicにしてほしい... © DeNA Co., Ltd. 28
Unity Search上でマスタデータに対する検索を可能にする ● DeNAの社内システムであるOyakataで管理されているマスタデータを Unity Search上から直接検索して、内容を閲覧できるようにする ○ ● 列と行からなる、表で表現できるマスタデータを対象とする SpreadsheetやExcelに置き換えて考えることは十分可能 © DeNA Co., Ltd. 29
Unity Search上でマスタデータに対する検索を可能にする ● クエリで表示したいテーブル名を受け取り、表の内容をAPI経由で取得して表示 ○ ワイルドカード(*)による複数テーブルの取得にも対応させる ● 対象のテーブルの各行のデータをSearchItemに紐づけて結果として表示 ● 対象のテーブルの各列をSearchColumn及びSearchSelectorとして扱えるように生成 ● 該当行を編集するページのURLをブラウザで開くSearchActionを生成 クエリの構文 oyakata:TableName © DeNA Co., Ltd. 30
非アセットに対してのインスペクタの描画 ● toObjectに設定したdelegateからUnityEngine.Objectを 返すと、選択時に右ペインにそのEditorが表示される ● ScriptableObjectを介してとAsset以外も扱う手法 © DeNA Co., Ltd. ○ toObject時にSOを生成し、値を移し替える ○ 各行のインスペクタ風表示をこの手法で実現 31
まとめ © DeNA Co., Ltd. 32
まとめ ● Unity Searchは検索システムの構築基盤であり、マルチカラムの情報を扱う高度なUIと、 複雑な検索を柔軟に構成できるクエリシステムを、様々なデータソースに提供する ● SearchProviderを独自に実装することで、Unity Searchによる検索システム基盤上に、 任意のデータソースを接続することができる ● Unity Searchを通して課題解決を行うことで、 大きな課題を小さな実装で解決することが可能になる © DeNA Co., Ltd. 33
© DeNA Co., Ltd. 34