Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9543

【Unity】タイムライン風のAnimationEditorを作成する - ロジック編

$
0
0

本記事は QualiArts Advent Calendar 2020の24日目の記事です。

はじめに

この記事ではUnityのEditorを実装した際に躓いた点や新しく発見した点などを、個人的に後日振り替えれるようにまとめました。

想定される読者

  • Unityエディタ拡張の初心者
  • Animationに興味のある方

別記事

各内容が大きくなってしまったので記事を分けました。
興味のある記事から読んでいただければ幸いです。

クラス構成

今回次のクラスを作成しEditorを実装しました。
クラスの役割について説明します。

TweenAnimation

GameObjectへアタッチするコンポーネント。
タイムラインはこのTween単位で作成される。
具体的なTween情報を保持する。
Tween情報(TweenInfo)は複数設定が可能。

TweenInfo

Tweenの情報を持つクラス
- Tweenのスタート時間
- 具体的なTweenの中身

Tween

実際のTweenアニメーションの情報を司るクラス。
- 長さ
- 経過時間
- スタート値
- 終了値
などを共通に持ち、継承したくらすで振る舞い(移動、拡縮、回転、透明...)などを制御する。
経過時間を使って「スタート値」「終了値」間の値を取得します。

Tweenを追加する

TweenAnimation直下のTransformを取得しMenuが表示できるようにします。
その際にGenericMenuクラスを利用します。

varmenu=newGenericMenu();vartargetGameObjectTransforms=_tweenAnimation.GetComponentsInChildren<Transform>(false);foreach(vartransformintargetGameObjectTransforms){vartargetTransform=transform;menu.AddItem(newGUIContent(targetTransform.name),false,_=>{// こちらの追加処理}}

メニューを選択したときに対象のTransformにTweenが追加されるようにします。
エディタ上のTweenを追加するときには「Undo.AddComponent」を利用します。
https://baba-s.hatenablog.com/entry/2019/09/30/170000

// 選択したTweenBehaviourを対象のTweenAnimationへ追加するvartween=Undo.AddComponent<Tween>(targetTransform.gameObject);tween.hideFlags=HideFlags.HideInInspector;// SerializeObjectを更新_animationSerializeObject.Update();vartweenAnimationInfos=_animationSerializeObject.FindProperty(TweenAnimationInfosString);tweenAnimationInfos.arraySize++;// 追加するvartweenInfo=tweenAnimationInfos.GetArrayElementAtIndex(tweenAnimationInfos.arraySize-1);tweenInfo.FindPropertyRelative("TweenBase").objectReferenceValue=tween;tweenInfo.FindPropertyRelative("StartTweenTime").floatValue=0.0f;_animationSerializeObject.ApplyModifiedProperties();

削除する

逆に削除する場合にはこのような形で実装ができます。

_animationSerializeObject.Update();vartweenInfos=_animationSerializeObject.FindProperty(TweenAnimationInfosString);for(inti=0;i<tweenInfos.arraySize;i++){varinfo=tweenInfos.GetArrayElementAtIndex(i);if(info.FindPropertyRelative("TweenBase").objectReferenceValue!=targetTween){continue;}// 削除するtweenInfos.DeleteArrayElementAtIndex(i);i--;}_animationSerializeObject.ApplyModifiedProperties();Undo.DestroyObjectImmediate(targetTween);

タイムラインの経過時間に合わせてTweenを動かす

各Tweenに関してはスタート時間がバラバラになる可能性があるので、単純にタイムライン上の時間を使うことができません。
そのため各Tweenごと経過時間を計算します。

タイムライン上での経過時間を取得する

まずはタイムライン上の経過時間を取得します。

privateTweenAnimation_tweenAnimation;privatefloat_prevTime;voidUpdate(){if(isPlay){varcurrentTime=Time.realtimeSinceStartup;vardeltaTime=(currentTime-_prevTime)*1.0f;// Durationはアニメーションの長さvardeltaRate=deltaTime/_tweenAnimation.Duration;// CurrentTimeRateは経過時間の割合varrate=_tweenAnimation.CurrentTimeRate+deltaRate;if(rate>1.0f){rate=1.0f;isPlay=false;}_tweenAnimation.SetCurrentTimeRate(rate);_prevTime=currentTime;}

各Tweenに経過時間を設定する

タイムライン経過時間を取得できたので、これを使って各Tweenの経過時間設定します。
先程のコードでは SetCurrentTimeRate でTweenAnimationのRateを設定しているので、これを使います

// TweenAnimationの経過時間割合を設定publicvoidSetCurrentTimeRate(floatrate){_currentTimeRate=rate;}

エディタ上の再生位置を更新

TweenAnimationに設定されているTweenの情報を元に現在時間とスタート時間・Tweenの長さを加味して再生中なのかどうかを判定しています。
再生中の場合にTweenに経過時間をセットしています。

// TweenAnimationの経過時間割合を設定publicvoidSetCurrentTimeRate(floatrate){_currentTimeRate=rate;for(inti=0;i<_tweenAnmationInfos.Length;i++){vartargetInfo=_tweenAnmationInfos[i];if(targetInfo.TweenBase==null){continue;}vartweenDuration=targetInfo.TweenBase.Duration;// 現在時間がスタートタイムより前の場合if(currentTime<targetInfo.StartTweenTime){return;}// 現在時間がTweenの時間を超えている場合if(currentTime>=targetInfo.StartTweenTime+tweenDuration){return;}// Tweenへfloattime=tweenDuration>float.Epsilon?(currentTime-targetInfo.StartTweenTime):0.0f;targetInfo.TweenBase.SetCurrentTime(time);}}

再生時間を元に任意のAnimationを実行する

Tweenクラスを継承して、移動や透過などのそれぞれAnimationを担当するクラスを作り、経過時間に応じて値を変更します。

publicoverridevoidSetCurrentTime(floatcurrentTime){// キャッシュ_currentTime=currentTime;// 経過時間割合算出varrate=currentTime/_duration;rate=Mathf.Min(rate,1.0f);// 内部処理SetCurrentTimeRate(rate);}

経過時間に応じた値を取得する

// timeRateが経過時間varvalue=Mathf.Lerp(StartValue,EndValue,timeRate);

例えば、CanvasGroupを使った透過処理の場合には次のようなコードを書くことができます。

// 個別の内部処理protectedvoidSetCurrentTimeRateInternal(floatrate){varvalue=Mathf.Lerp(0.0f,1.0f,rate);if(_canvasGroup!=null){_canvasGroup.alpha=value;}}

Tweenを選択する

特定のTweenを選択する場合には次のように実装ができます。

選択するKeyを設定

まずはGUI.SetNextControlName()を使って選択に必要なkey(String型)を設定します。

// 今回はTweenのIndexGUI.SetNextControlName(tweenIndex.ToString());

選択する

設定したKeyを選択する場合にはGUI.FocusControl()を使います。
例えば対象のTweenをマウスでクリックした時など何かしらのイベントに仕込みます。

if(Event.current.type==EventType.MouseUp){if(Event.current.button==0){// 左判定vartweenRect=GUILayoutUtility.GetLastRect();varmousePosition=Event.current.mousePosition;if(tweenRect.Contains(mousePosition)){// IndexをフォーカスするGUI.FocusControl(tweenIndex.ToString());Event.current.Use();}}}

選択したTweenを取り出す

選択されたkeyを取り出すときにはGUI.GetNameOfFocusedControl()を使います。
今回int型のString型にキャストして使っているので、少しめんどくさいやり方にはなっています。

varfocusIndexString=GUI.GetNameOfFocusedControl();if(!string.IsNullOrEmpty(focusIndexString)){intfocusIndex=int.Parse(focusIndexString);if(_tweenAnimation.TweenAnmationInfos.Length>focusIndex){_focusedTweenAnimationInfo=_tweenAnimation.TweenAnmationInfos[focusIndex];}}

取り出したTween情報を元に、色を変えたり、Inspectorを表示したりします。

最後に

Editorに関しては、この記事執筆時(2020/12/24)ある程度動くところまで実装はできたのですが、まだゲーム開発で使えるまでには作り込めていないため、サンプルなどは用意していません。
今後継続して開発は続けるつもりですので、何かしらアウトプットできるものがあれば何かしらの形で公開したいと思っています。

関連記事


Viewing all articles
Browse latest Browse all 9543

Trending Articles