はじめに
UnityのTimelineを使用していてTimelineAssetが握っている参照アセットをすべて調べたくなりました。
やりたいことは以下です。
- Timelineが参照しているPrefabやAnimationClipを一覧表示
- さらにそのPrefabが参照してる各アセットを全表示
です。
先に見せてしまいますが、Timelineからこのようにアセット名+参照Listが出るようにしたいです。
ソースコード
今回のアセット構造
Timeline
タイムラインにはTrackGroup、さらにSubTrackを作り、それぞれにTrackを置きます
上4つのAnimationTrackはUnity標準のAnimationTrackでAnimationClipをアタッチします。
下の4つは自作のTrackとClipを配置します。PrefabTrackというものを作成しました。Prefabを3つ入れることができるだけです。
今回は参照関係を知りたいだけなので自作のTrackは特に機能はありません。
ディレクトリ
Asset>Timelineにタイムラインに関係するものを入れます。
アセットは001と002というIDがあり、Timelineからは001が参照されているのが正しいシチュエーションです。
001と似てるアセットが002にあります。
アニメーション、マテリアル、Prefabがここに入っています。
002は間違っている想定の場所です。
そして、TimelineTrackの4番目では002を参照させます
各Trackで002が参照されてしまっていることをツールで知れるようにすることが目的です。
やり方
AssetDatabase.GetDependenciesを使用してみる
AssetDatabase.GetDependencies
という依存関係を出してくれるメソッドがUnityであるので使用します。
使い方は以下です。
private string[] GetDependencies(TimelineAsset timelineAsset)
{
var assetPath = AssetDatabase.GetAssetPath(timelineAsset);
var dependencies = AssetDatabase.GetDependencies(assetPath, true);
return dependencies;
}
AssetDatabase.GetDependencies
は第2引数にTrue
を入れると再帰的に参照を調べてくれます。
戻ってくる配列に重複はありませんでした。
間違った場所にアクセスしているのが取得できました。
しかし、この方法だと冒頭で書いたやりたいことの
Timelineが参照しているPrefabやAnimationClipを一覧表示
が達成できません。
Timelineから参照関係は知れましたがこのままだとどのアセットに問題があるのかわからない状態です。
Clipから調べる
AssetDatabase.GetDependencies
で達成できなかった理由はアセット名を取得できていなかったこととアセットごとの参照を取得できていない点なのでそれを解決します。
Timelineが含んでいるアセットをすべて取得し、Dictionaryに格納すればうまくいくためそうします。
private void GetDependencies(TimelineAsset timelineAsset)
{
foreach (var track in timelineAsset.GetOutputTracks())
{
GetDependenciesFromTrack(track);
}
}
まずはTimelineからTrackの数だけループを行います。
/// <summary>
/// TrackのからClipを探し、参照を調べる
/// </summary>
private void GetDependenciesFromTrack(TrackAsset trackAsset)
{
// Clipの数だけループする
foreach (var clip in trackAsset.GetClips())
{
// シリアライズされたオブジェクト、プロパティを探し、その数だけループさせる
SerializedObject serializedObject = new SerializedObject(clip.asset);
SerializedProperty prop = serializedObject.GetIterator();
while (prop.NextVisible(true))
{
// プロパティの種類がUnityEngine.Objectのものだけ調べます。
if(prop.propertyType == SerializedPropertyType.ObjectReference)
{
// プロパティとしてついているスクリプトは参照を調べなくてよいので(今回は)分岐します
Object referencedObject = prop.objectReferenceValue;
if(referencedObject != null && referencedObject.GetType() != typeof(MonoScript))
{
// GetDependenciesを使用してプロパティに入れられたアセットの参照を取得します。
var assetPath = AssetDatabase.GetAssetPath(referencedObject);
// .csは参照から除きます。(今回は)
var dependencies = AssetDatabase.GetDependencies(assetPath, true).Where(dependency => !dependency.EndsWith(".cs")).ToList();
var assetName = referencedObject.name;
if(!_assetDependencies.ContainsKey(assetName))
{
_assetDependencies[assetName] = dependencies;
}
}
}
}
// SubTrackに関して再帰的に処理します。
foreach (var subTrack in trackAsset.GetChildTracks())
{
GetDependenciesFromTrack(subTrack);
}
}
}
foreach (var clip in trackAsset.GetClips())
Clipでループを回します。
そしてClipに入っているアセットをすべて取得します。
取得方法はここです。
SerializedObject serializedObject = new SerializedObject(clip.asset);
SerializedProperty prop = serializedObject.GetIterator();
while (prop.NextVisible(true))
{
// プロパティの種類がUnityEngine.Objectのものだけ調べます。
if(prop.propertyType == SerializedPropertyType.ObjectReference)
}
Clipにドラッグアンドドロップできる場所に入っているものがClipに入っているアセットなのでSerializedObject
,SerializedProperty
を使用してシリアライズされているプロパティを取得します。
while (prop.NextVisible(true))
でプロパティの数だけループします。
// プロパティの種類がUnityEngine.Objectのものだけ調べます。
if(prop.propertyType == SerializedPropertyType.ObjectReference)
ドラッグアンドドロップできるものがアセットと捉えているためこの処理をします。intや他のプロパティはアセット参照とは関係がないため扱いません。
// プロパティとしてついているスクリプトは参照を調べなくてよいので(今回は)分岐します
Object referencedObject = prop.objectReferenceValue;
if(referencedObject != null && referencedObject.GetType() != typeof(MonoScript))
{
// GetDependenciesを使用してプロパティに入れられたアセットの参照を取得します。
var assetPath = AssetDatabase.GetAssetPath(referencedObject);
// .csは参照から除きます。(今回は)
var dependencies = AssetDatabase.GetDependencies(assetPath, true).Where(dependency => !dependency.EndsWith(".cs")).ToList();
var assetName = referencedObject.name;
if(!_assetDependencies.ContainsKey(assetName))
{
_assetDependencies[assetName] = dependencies;
}
}
prop.objectReferenceValue
でObjectを取得できるため何をドラッグアンドドロップで入れたのか取得できています。Prefabであろうが、マテリアルであろうがあとはAssetDatabase.GetDependencies(assetPath, true)
を行えばUnityの機能でアセット参照を全部出してくれます。
今回はアセット参照が知りたいため、Where(dependency => !dependency.EndsWith(".cs"))
でC#スクリプトは除外します。
最終的にPrefab名+かかわるアセット参照の関係でList表示したいのでDictionary<string, List<string>>
を作成して入れます。Keyにはアセット名、Valueには参照関係のListを入れます。
このDictionaryを使用してEditor拡張で表示をすると
このように表示さて、SubTrackのClipに入っているアセットから参照情報を取得できました。
終わりに
このツールを作成していて最初はAssetDatabase.GetDependencies
を使用しただけで終わろうと思っていましたがどのアセットに問題があるのか全くわからなかったのでPrefabやアセットどこにわかる表示を行えるようにしました。
GUIDから関わるアセットを全表示する方法が多分あるのですが、これはおそらくアセット全検索になり速度が出るのか怪しかったためこの方法に行きつきました。