はじめに
Editor拡張でAnimationCurveのプリセット機能を作成しました。
その際、Animationウィンドウから情報を引っ張ってくるのに苦戦したので
方法をまとめておこうと思います。
コード全文
流れ
Animationウィンドウは外部から操作できない仕様ぽかったので、
ここを見ながらReflectionを利用してアクセスする形になりました。
AnimationWindowから3つの情報を取得できれば自由に操れるのでゴールです。
①選択中のKeyframeが所属するAnimationCurve
②選択中のKeyframe
③選択中のAnimationClip
選択中のAnimationWindowKeyframeを取得
AnimationWindow.animEditor.state.selectedKeys というところに
選択中のキーフレーム情報が保持されているのがわかりました。
まずはこれを取得します。
//Animationウィンドウを取得する。開いてなけれ開き、そのインスタンスを取得する。
var animationWindow = EditorWindow.GetWindow(animationWindowType,false,null,true);
//AnimationWindowstateを取得するため、AnimEditorを取得
var animEditorProperty = animationWindowType.GetProperty("animEditor", BindingFlags.Instance | BindingFlags.NonPublic);
var animEditor = animEditorProperty.GetValue(animationWindow);
//Animationウィンドウ内の情報取得のため、AnimationWindowstateを取得。
var stateProperty = animEditorType.GetProperty("state", BindingFlags.Instance | BindingFlags.Public);
var state = stateProperty.GetValue(animEditor);
//今開いてるのがCurveWindowの場合は選択中のキーフレームが上手く取得できないので、
//一度DopeSheetに戻してSelectionKeyの更新をする
var showCurveEditor = animationWindowStateType.GetField("showCurveEditor", BindingFlags.Instance | BindingFlags.Public);
if((bool)showCurveEditor.GetValue(state))
{
var onEnableWindow = animEditorType.GetMethod("SwitchBetweenCurvesAndDopesheet", BindingFlags . Instance | BindingFlags . NonPublic | BindingFlags . Public);
onEnableWindow.Invoke(animEditor,null); // DopeSheetに切り替えたことでSelectionKeyが更新される
onEnableWindow.Invoke(animEditor,null); // 更新が終わったのでCurveWindowに戻す
}
//選択中のキーフレーム取得のため、AnimationWindowState.selectedKeysを取得
//AnimationWindowKeyframe型のListで返ってくるが、そのままだと扱えないのでdynamic型で受け取り
var getSelectionProperty = animationWindowStateType.GetProperty("selectedKeys", BindingFlags.Instance | BindingFlags.Public);
var keys = getSelectionProperty.GetValue(state) as dynamic;
foreach (var key in keys)
{
//この中でキーを処理
}
ここまででツマッたポイントが2つあります。
①カーブウィンドウを開いている場合、selectedKeysが更新されないらしい。
animEditor.SwitchBetweenCurvesAndDopesheet()で一度ドープシートを開くことで更新し、
もう一度実行しカーブウィンドウに戻すことで解消しました。
②selectedKeysを取得したはいいが、なかなかListの要素にアクセスできなかった
NGパターン
keys = getSelectionProperty.GetValue(state);
//この場合Listとして認識されないので、foreachで回せず要素を取り出すことができない。
keys = getSelectionProperty.GetValue(state) as List<AnimationWindowKeyframe>;
//かといって正しい型で取得しようとした場合、AnimationWindowKeyframeへのアクセス権が無くエラーを吐く
dynamicで取得してあげたら上手く行きました。
AnimationCurveの取得・編集・保存
AnimationWindowKeyframeから
AnimationCurve、Keyframe、AnimationClipを取得出来るので
自由編集してAnimationClipを保存すれば完了です。
//選択しているキーフレームを含む、AnimationWindowCurveを取得
var curveProperty = animationWindowKeyframeType.GetProperty("curve", BindingFlags.Instance | BindingFlags.Public);
var selectWindowCurve = curveProperty.GetValue(key);
//AnimationWindowCurveのままだと編集しにくいので、UnityEngene.AnimationCurveに変換する
var toAnimationCurve = animationWindowCurveType.GetMethod("ToAnimationCurve" , BindingFlags.Instance | BindingFlags.Public);
var animationCurve = toAnimationCurve.Invoke(obj:selectWindowCurve , parameters: null) as AnimationCurve;
//選択したキーフレームがAnimationCurve内の何番目か取得
var getIndex = animationWindowKeyframeType.GetMethod("GetIndex" , BindingFlags.Instance | BindingFlags.Public);
var index = (int)getIndex.Invoke(key , null);
//選択キーフレームをKeyframeで取得
var keyframe = animationCurve[index];
///////
//ここでanimationCurveを編集する
///////
//保存先のAnimationClipを取得
var clipProperty = animationWindowCurveType.GetProperty("clip", BindingFlags.Instance | BindingFlags.Public);
var clip = clipProperty.GetValue(selectWindowCurve);
//AnimationCurve判別のためにCurveBindingを取得
var bindingProperty = animationWindowCurveType.GetProperty("binding", BindingFlags.Instance | BindingFlags.Public);
var binding = bindingProperty.GetValue(selectWindowCurve);
//AnimationClipにAnimationCurveの編集を反映
AnimationUtility.SetEditorCurve(clip, binding, animationCurve);
あとがき
カーブ調整もう少し楽出来ないかなぁと軽い気持ちではじめましたが、
公式のコードを読み解くだけで一苦労でした…
冒頭GifのEditor拡張はこちらです。