メモ書きになります
数字を文字列変換
TextMeshProに表示するテキストは文字列でなければならないので、次のような記述をしていました。
text = x.ToString();
こちらの方法で変換はできるのですが、文字列に変換する家庭でメモリのアロケーションが起きるよと指摘をいただきました。
SetText
TextMeshProにはSetTextという関数があります。 これを使うとアロケーションなしに文字列を変換できます。
text.SetText("{0}",x};
エディタ拡張を行っている時にあるジェネリクス型のクラスのInspectorを変更したい時がありその時の知見です。
例えばベースクラスとして次のようなクラスを定義しました。
public class HogeBase<T> : MonoBehaviour { [SerializeField] private T _value; }
継承したクラスはこのような感じになります。
public class HogeChild : HogeBase<float> { }
こちらのInspectorを拡張したい場合。
[CustomEditor(typeof(HogeBase<>),true)] public class HogeInspector : Editor { private SerializedProperty _scriptProperty; private SerializedProperty _valueProperty; private void OnEnable() { _scriptProperty = serializedObject.FindProperty("m_Script"); _valueProperty = serializedObject.FindProperty("_value"); } public override void OnInspectorGUI() { using (new EditorGUI.DisabledScope(true)) { EditorGUILayout.PropertyField(_scriptProperty); } EditorGUILayout.PropertyField(_valueProperty, new GUIContent("値 ")); serializedObject.ApplyModifiedProperties(); } }
ジェネリクスを指定する場合には次のように指定します。 また継承先のクラスのCustomEditorの対象にするためには、第2引数をtrueにしてあげます。
[CustomEditor(typeof(HogeBase<>),true)]
次のように2つのジェネリクスの型があった場合
public class HogeBase<T,TValue> : MonoBehaviour { [SerializeField] private T _value; [SerializeField] private TValue _value2; }
カンマを使って指定ができます。
[CustomEditor(typeof(HogeBase<,>),true)]
TimelineのTrackの見た目を変える場合にはTrackEditorクラスを使います。
[CustomTimelineEditor(typeof(HogeTrack))] public class HogeTrackEditor : TrackEditor { }
CustomTimelineEditor Attributeに指定したTrackが拡張されます。
TrackEditorにはTrackDrawOptionsを指定できます。 デフォルトでは次のように設定されています。
public virtual TrackDrawOptions GetTrackOptions(TrackAsset track, UnityEngine.Object binding) { return new TrackDrawOptions() { errorText = GetErrorText(track, binding, TrackBindingErrors.All), minimumHeight = DefaultTrackHeight, trackColor = GetTrackColor(track), icon = null }; }
名前はTrack名を変更すると変わります。 次の例ではTrackEditorのOnCreateのタイミングで名前を変更しています。
public override void OnCreate(TrackAsset track, TrackAsset copiedFrom) { track.name = "ほげとらっく"; base.OnCreate(track, copiedFrom); }
なお、名前に関してはTrackが何かしらBindingをしていると表示から消えてしまいます。
色々調べたのですが、現状それ以外の項目はカスタマイズできなさそうです。 Animation TrackとAudio Trackには別途ボタンが設定されているので、もしかすると何かしらのボタンはつけられるのかもしれません。
Timelineで利用するClipの拡張をする場合、ClipEditorクラスを継承をして実装を行います。
[CustomTimelineEditor(typeof(HogeClip))] public class HogeClipEditor : ClipEditor { }
CustomTimelineEditor Attributeは拡張対象になるClipのクラスをしていします。
ClipEditorのGetClipOptionsを使えば簡単に色々な設定ができます。
デフォルトでは次のように設定されています。
public virtual ClipDrawOptions GetClipOptions(TimelineClip clip)
{
return new ClipDrawOptions()
{
errorText = GetErrorText(clip),
tooltip = string.Empty,
highlightColor = GetDefaultHighlightColor(clip),
icons = System.Linq.Enumerable.Empty<Texture2D>()
};
}
iconに関しては複数設定可能です。 見た目はこのような感じになります。
その他拡張ができそうな項目
ClipEditorにOnCreateメソッドがります。 これはClipを作成したときに呼び出されるのですが、次のようにdisplayNameを変更することができます。
public override void OnCreate(TimelineClip clip, TrackAsset track, TimelineClip clonedFrom) { var hogeClip = clip.asset as HogeClip; if (hogeClip == null) { return; } clip.displayName = "ほげくりっぷ"; }
DrawBackgroundメソッドをCLipが描画されるときに呼び出されます。 Clipの情報が引数で渡されるので、これを使って拡張ができます。
public override void DrawBackground(TimelineClip clip, ClipBackgroundRegion region)
{
var bgSize = new Rect(region.position.x, region.position.y, region.position.width, region.position.height);
EditorGUI.DrawRect(bgSize,Color.blue);
}
デバックログを仕込んだらよく分かるのですが、呼び出される頻度がとても高いメソッドですので、ロジックを書く場合にはなるべく重い処理は避ける必要がありそうです。
ClipBackgroundRegionに渡されるposition.heightに関してはハイライト色を除いた高さが渡されている。 ですので少し調整が必要。
Clipに関してはある程度決まった箇所の変更はできそうです。 この辺りを使って使いやすいようにカスタマイズを行っていきましょう。
TimelineのTrackを自作するときにTrackAssetクラスを使います。 このクラスでは何ができるのかを調べてみました。
自作のTrackを作る場合にはTrackAssetクラスを継承します。
public class TestTrack : TrackAsset { }
Trackで指定できるClipをTrackClipType Attributeを使って指定します。
[TrackClipType(typeof(TestClip))] public class TestTrack : TrackAsset { }
Timelineでアニメーションを行うオブジェクトを指定する場合には、TrackBindingType Attributeを使います。
[TrackClipType(typeof(TestClip))] [TrackBindingType(typeof(Image))] public class TestTrack : TrackAsset { }
Trackの判別をするために色を付けることができます。 こちらはTrackColor Attributeを使って指定します。
[TrackClipType(typeof(TestClip))] [TrackBindingType(typeof(Image))] [TrackColor(255,0,0)] public class TestTrack : TrackAsset { }
TrackAssetで定義されているメソッドを調べてみました。
Trackで設定されているClipを取得できます。
public IEnumerable<TimelineClip> GetClips()
このメソッドを継承することでClip同士のブレンド周りの処理を自作できます。
public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount) { var mixer = ScriptPlayable<TestMixer>.Create(graph, inputCount); // 各mixerの処理 return mixer; }
TimelineからTrackを削除したときに呼び出されます。 利用方法としては、Trackに設定されているobjectを元に戻すために利用することが多いみたいです。
public override void GatherProperties(PlayableDirector director, IPropertyCollector driver)
{
}
PlayableDirectorはTimelineを管理するコンポーネントです。
Trackに設定されているObjectを取得する場合GetGenericBindingを使います。 Imageの色を戻す場合は次のようにGatherPropertiesの記述ができそうです。
var bindingImage = director.GetGenericBinding(this) as Image; if(bindingImage != null) { // Bindingされたobjectがある場合、元に戻している driver.AddFromName<Text>(bindingImage.gameObject, "m_Color"); }
Unityで使うc#のクラスでは色々なAttributeを定義することができます。 その中で「ExecuteAlways」というAttributeがあります。
簡単な例は次のようになります。
[ExecuteAlways] public class ExecuteAlwaysTest : MonoBehaviour { // Start is called before the first frame update void Start() { Debug.Log("Start"); } }
通常、Unityを再生しないとStartは呼び出されませんが、ExecuteAlwaysの場合 Prafabモードに移行したときや、再生が終わった時などに呼び出されます。
MonoBehaviourには、Onvalidateというメソッドがあり、これを使うとInspectorの変更時の更新処理ができます。
#if UNITY_EDITOR private void OnValidate() { Debug.LogError("OnValidate:" + _value); } #endif
今まではこちらを使っていたのですが、ExecuteAlwaysに関しても何かしらできそうだなと感じました。
Playable API周りについての概要がなんとなくわかってきたので、次にScriptablePlayableを自作してみたいと思い調査をしてみました。 最終目標は独自のTrackをTimelineで再生をすることです。
ScriptPlayableを作るにはPlayableBehaviourを継承してクラスを作成します。 では適当にPlayableを作ってみます。
public class CustomPlayable : PlayableBehaviour { // PlayableGraph再生時 public override void OnGraphStart(Playable playable) { Debug.Log("Graph Start"); } // PlayableGraphストップ時 public override void OnGraphStop(Playable playable) { Debug.Log("Graph Stop"); } }
これはPlayableGraphで使って見ます。
_playableGraph = PlayableGraph.Create(); var customPlayable = ScriptPlayable<CustomPlayable>.Create(_playableGraph, 0); animationPlayableOutput.SetSourcePlayable(customPlayable); var scriptOutput = ScriptPlayableOutput.Create(_playableGraph, "custom"); scriptOutput.SetSourcePlayable(customPlayable); _playableGraph.Play();
Unityで再生するとPlayableGraph Visualizerではこのように表示されています。 (デバックログも吐き出されています)
PlayableBehaviourにProcessFrameというメソッドが定義されています。 PlayableGraph再生時の経過情報を受け取ることができます。
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
}
FrameDataにはdeltaTimeなどの情報がありますのでこれを使うことで、何かしらのアニメーションを設定できたりできそうです。
現状、Unity再生中にこのPlayableはずっと再生されています。 次に、Playableの再生時間を設定する方法を調べます。
public override void ProcessFrame(Playable playable, FrameData info, object playerData) { var time = playable.GetTime(); var duration = playable.GetDuration(); }
現在の経過時間はPlayableのGetTimeで取得でき、Playableの再生時間はGetDurationで取得ができます。 これを使い、必要な処理を行うことができます。
再生時間はPlayableのSetDurationで指定できます。
playable.SetDuration(1.0f);
ProcessPrameは経過時間を過ぎても呼び出し続けますので、もし止めたい場合には playable.Pause()を呼び出します。
playable.Pause();
Playableについて少しわかってきたので、当初の目的であるTimelineで使えるようする方法が調べました。
TImelineで用意するためには次の3つを表示できるクラスです。
TImelineが再生されたときのClipの挙動をPlayableで作成されています。 今回はUGUIのImageの色を変更するできるように実装をしました。
public class CustomTimelineClipPlayable : PlayableBehaviour { public Color StartColor = Color.white; public Color EndColor = Color.white; public override void ProcessFrame(Playable playable, FrameData info, object playerData) { var image = playerData as Image; if (image != null) { var duration = playable.GetDuration(); var time = playable.GetTime(); image.color = Color.Lerp(StartColor, EndColor, (float) (time / duration)); } } }
Color.Lerpを使って色変化を実装しているのですが、もしかしたらもっと良い方法あるかもしれません。
次にTimeline上にClip表示と編集ができるようにPlayableAssetを作成します。 今回はImageの始値と終値を設定できるようにしました。
[System.Serializable] public class CustomTimeLinePlayableAsset : PlayableAsset { public Color startColor = Color.white; public Color endColor = Color.white; // Factory method that generates a playable based on this asset public override Playable CreatePlayable(PlayableGraph graph, GameObject go) { var playable = ScriptPlayable<CustomTimelineClipPlayable>.Create(graph); var behaviour = playable.GetBehaviour(); // PlayableへInspectorでの設定値を渡しています。 behaviour.StartColor = startColor; behaviour.EndColor = endColor; return playable; } }
Timelineで使うTrackを作成します。
[TrackClipType(typeof(CustomTimeLinePlayableAsset))] [TrackBindingType(typeof(Image))] public class CustomTimelineTrack : TrackAsset { }
TrackBindingTypeで対象のImageを設定できるようにしています。
これで準備完了です。 TimelineでTrackを作成するときに、新しく「CustomTimelineTrack」が選択できるようになっているはずです。
とりあえず、自作のPlayableをTimeline上で動かせるようになりました。 ただ、簡単なPlayableでしたので、今後はもう少し複雑なPlayableが作れるようにしたいないと思いました。