UNITY Editor拡張で、タイムラインをスクリプトから生成する
お題
タイムラインを自動生成しよう
トラックやクリップも外から作成してみます
それに伴い、エディタ拡張の勉強
メニューよりEditorWindowを表示
とりあえず最も汎用的な?メニュの拡張から
メニューの拡張
Editor拡張で最も使われると思うので、よーく覚えよう
UnityEditor.MenuItem - Unity スクリプトリファレンス
MenuItemを使う
ドキュメントの通り、非常に機能が豊富なので細かい事は説明しません
まず、Editor専用コードなので、Editorディレクトリを作りその下にコードを置く事
そして、Editor拡張は静的関数しか呼べないので、そういう設計にする事
主な機能をざっと
メニューアイテムの追加
[MenuItem("hoge/fuga")]
のように属性をつけると、メニューに hoge->fuga と追加される
メニュー名に日本語も使えます
当然そのメニューを選んだ時に、その関数が呼ばれます
ホットキーも簡単に付ける事ができます
また、Validateもする事が出来ますし、メニューの表示順番も変更できます
Validate関数を設定すれば、関数がFalseを返すと無効、Trueを返すと有効になります
ヒエラルキー
MenuItemのパスの頭をGameObjectにすると、ヒエラルキーで右クリックした際のメニューに追加できます
[MenuItem("GameObject/CustomEditor")]
private static void Create()
{
Debug.Log("Menu");
}

ヒエラルキー右クリックでちゃんと表示されました

もちろん、メニューの のGameObjectにも表示される
Assets
MenuItemのパスの先頭をAssetsにすると、Assetsビューのコンテキストメニューに表示される
[MenuItem("Assets/CustomEditor", false, 1)]
private static void Create()
{
Debug.Log("Menu");
}

もちろん、メニューのAssetsにも表示される

CONTEXT
コンポーネントの 右上の3点アイコンを押したときのメニューを作れます!
もちろん、自作コンポーネントだけじゃなく、UNITYのデフォルトコンポーネントも可能です
[MenuItem("CONTEXT/PlayableDirector/CustomEditor", false, 1)]
private static void Create()
{
Debug.Log("Menu");
}
今回はPlayableDirectorコンポーネントにメニューを追加しました

今回使うもの
今回は、選択したGameObjectにPlayableDirectorコンポーネントを追加したいので
ヒエラルキー上のGameObjectのコンテキストメニューに追加する
コンテキストメニュー
public class CustomEditorWindow : EditorWindow
{
[MenuItem("GameObject/CustomEditor", false, 1)]
private static void Create()
{
// 生成
GetWindow<CustomEditorWindow>("CustomEditor");
}
ヒエラルキーのGameObjectメニューにCustomEditorを追加します
そして、CustomEditorWindow(自分のクラス)を呼び、GUIを表示させます
今回は EditorWindowを継承し、ウインドウを表示させます
EditorWindow
private void OnGUI()
{
using (new GUILayout.HorizontalScope())
{
targetGo_ = Selection.activeGameObject;
if (GUILayout.Button("CreateTimeline"))
{
// ここにボタン押したときの処理が入る
}
}
}
とりあえずボタンが1個あるだけの単純なウインドウ

以上でEditor拡張のおさらい終了
Timeline等をコードから生成
本題という事になる
GameObjectにPlayableDirectorのアタッチ
if(targetGo is null)
{
return;
}
// 選択中のGameObjectにPlayableDirectorコンポーネントをつける
var director = targetGo.GetComponent<PlayableDirector>();
if(director == null)
{
director = targetGo.AddComponent<PlayableDirector>();
}
AddComponentを使って普通にPlayableDirectorを作成する
TimelineAssetを作成しAssetデータベースへ保存する
TimelineAssetを作成します
つまり、タイムラインの情報すべてを保存するものです
ScriptableObjectで作成します
ただし、このままでは、シーン上に作られます
実際の運用では、タイムラインはファイル(Asset)にして読み込んで使うと思うので
AssetDatabaseに登録します
拡張子は playableです
// TimelineAssetの作成
var timelineAsset = playableDirector.playableAsset as TimelineAsset;
if (timelineAsset == null)
{
timelineAsset = ScriptableObject.CreateInstance<TimelineAsset>();
director .playableAsset = timelineAsset;
}
var path = "Assets/Resources/sample.playable";
AssetDatabase.DeleteAsset(path);
AssetDatabase.CreateAsset(timelineAsset, path);
AssetDatabase.SaveAssets();
AssetDatabaseの仕様で、ディレクトリが無い場合はエラーになるので、ディレクトリは事前に作っておく事
そして、既にAssetが存在する場合は上書きするとエラーになるので、事前にDeleteしておきます
SaveAssetは、即座にデータベースに反映するおまじないです
トラックの作成&バインディングを設定
今回はGroupトラックを作成し、そこにトラックを作成します
型は前回作った NewPlayableTrackです
Bindingにはゲームオブジェクトをつけます。今回はCubeという名前のゲームオブジェクトをつけます
var groupTrack = timelineAsset.CreateTrack<GroupTrack>(null, "Group");
var track = timelineAsset.CreateTrack<NewPlayableTrack>(groupTrack, "NewPlayable");
var b = GameObject.Find("Cube");
director.SetGenericBinding(track, b);
クリップ作成とKeyFrame作成
クリップを作成します。もちろん NewPlayableAssetで
そしてそのクリップにKeyFrame(AnimationCurve)を設定します
今回は最初が1fで、デフォルトのカーブでエンドが0fになる形で
もちろん、カーブのEaseを変えたり、KeyFrameを増やす事は容易
var clip = track.CreateClip<NewPlayableAsset>();
clip.CreateCurves(null);
var curve = new AnimationCurve();
curve.AddKey(new Keyframe(0f, 1f));
curve.AddKey(new Keyframe((float)clip.duration, 0f));
clip.curves.SetCurve("", typeof(NewPlayableAsset), "hoge", curve);
リフレッシュ
最後にリフレッシュ
EditorUtility.SetDirty(timelineAsset);
TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved);
成果物

とりあえず、目的の事は出来た