Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Timeline エディター拡張入門
Search
Yuichiro MUKAI
May 10, 2024
Programming
0
460
Timeline エディター拡張入門
2024/05/10 に開催された「Unity Timeline/Playable API 完全に理解した 勉強会」の登壇資料です。
Yuichiro MUKAI
May 10, 2024
Tweet
Share
Other Decks in Programming
See All in Programming
ペパボOpenTelemetry革命
pyama86
2
1.2k
The Final Frontier of Web Development: React Server Components vs Jakarta EE
ivargrimstad
0
210
スタックトレース始めてみた
kuro_kurorrr
5
1.2k
Namespace, What and Why
tagomoris
4
1.5k
The grand strategy of Ruby Parser
yui_knk
5
430
GitHub Actionsの痒いところを埋めるサードパーティーランナー
dora1998
2
280
TypeScriptの型とパフォーマンス (TSKaigi 2024)
ypresto
14
5.2k
Revisiting the Hotwire Landscape after Turbo 8 @ RailsConf 2024, Detroit
marcoroth
3
610
[RailsConf Detroit 2024] Ruby on Fails - effective error handling with Rails conventions
talyssonoc
0
300
[RubyKaigi 2024] Ruby Mixology 101: adding shots of PHP, Elixir, and more
palkan
0
140
Using "modern" Ruby to build a better, faster Homebrew
mikemcquaid
2
300
Next.js App Router
quramy
14
2.3k
Featured
See All Featured
YesSQL, Process and Tooling at Scale
rocio
165
13k
Automating Front-end Workflow
addyosmani
1357
200k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
660
120k
Building Better People: How to give real-time feedback that sticks.
wjessup
356
18k
RailsConf 2023
tenderlove
9
590
A Philosophy of Restraint
colly
197
16k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
501
140k
The Mythical Team-Month
searls
217
42k
The Pragmatic Product Professional
lauravandoore
26
5.9k
Become a Pro
speakerdeck
PRO
13
4.6k
BBQ
matthewcrist
80
8.8k
How GitHub (no longer) Works
holman
305
140k
Transcript
Unity Timeline/Playable API શʹཧղͨ͠ ษڧձ 2024/05/10 Ҫ ༞Ұ Timeline エ
デ ィター拡張入門
自己紹介 向井 祐一郎 • 株式会社 サイバーエージェント / 株式会社アプリ ボ ット
• Lead Developer Experience • システム系の基盤開発・プロジェクトの開発支援 SNS・個人活動など • @yucchiy_(X) • Unity Weekly • https://blog.yucchiy.com/tags/unity-weekly/ • UniTips(社内有志で技術書典に執筆)
• タイムラインのエ デ ィター拡張について概要の紹介 • TrackEditorとClipEditor • TrackEditorとClipEditorを用いた拡張の実例の紹介 • 見た目の調整やエラーメッセージの表示
• トラックのバインドオブジェクトの制御 • 生成・ 編 集時コールバックの取得 このLTで話したいこと
TrackEditorとClipEditorについて
TrackEditorとClipEditor • Timeline 1.1.0(2019/02/14)で追加された、トラックやクリップの エ デ ィター拡張 • UnityEditor.Editorのタイムライン版、といった立ち位置 •
トラックやクリップのエ デ ィターの挙動を制御できる • 見た目の調整やエラー表示 • トラックやクリップの追加・ 編 集時への処理の差し込み • トラックのバインドオブジェクトの制御 • etc…
標準トラックにおける拡張 事例 AudioClipの音声を波形情報として クリップの背景に表示したり バインドされたコン ポ ーネントがdisableなので エラーメッセージを表示したり
SampleTrackに対するエ デ ィターを作る TrackEditorの作り方 [CustomTimelineEditor(typeof(SampleTrack))] public class SampleTrackEditor : TrackEditor
{ public override void OnCreate(TrackAsset track, TrackAsset copiedFrom) { Debug.Log($"SampleTrack͕࡞͞Ε·ͨ͠ʂ"); } } CustomTimelineEditor属性をつけ、 対象のトラックの型を指定 用途に応じてメソッドをオーバーライドして処理を書く TrackEditorを継承
SampleTrackEditorの動作確認
SampleClipに対するエ デ ィターを作る ClipEditorの作り方 [CustomTimelineEditor(typeof(SampleClip))] public class SampleClipEditor : ClipEditor
{ public override void OnClipChanged(TimelineClip clip) { Debug.Log($"ΫϦοϓ͕ฤू͞Ε·ͨ͠ʂ"); } } CustomTimelineEditor属性をつけ、 対象のクリップの型を指定 ClipEditorを継承 用途に応じてメソッドをオーバーライドして処理を書く
SampleClipEditorの動作確認
TrackEditorでオーバーライドできるメソッド一覧 https://docs.unity3d.com/Packages/
[email protected]
/api/UnityEditor.Timeline.TrackEditor.html 見た目調整 & エラー処理 トラック操作時のコールバック トラックへのバインド オブジェクトの制御
ClipEditorでオーバーライドできるメソッド一覧 https://docs.unity3d.com/Packages/
[email protected]
/api/UnityEditor.Timeline.ClipEditor.html クリップ操作時のコールバック 見た目調整 & エラー処理 クリップ下へのタイムラインネスト制御
トラック/クリップの見た目を調整する
GetTrackOptionsをオーバーライドし、独自のTrackDrawOptionsを返却する GetTrackOptionsによる見た目の調整 [CustomTimelineEditor(typeof(SampleTrack))] public class SampleTrackEditor : TrackEditor { public
override TrackDrawOptions GetTrackOptions( TrackAsset track, Object binding) { var options = base.GetTrackOptions(track, binding); options.icon = AssetPreview.GetMiniTypeThumbnail(typeof(Rigidbody)); options.trackColor = Color.yellow; return options; } }
[CustomTimelineEditor(typeof(SampleTrack))] public class SampleTrackEditor : TrackEditor { public override TrackDrawOptions
GetTrackOptions( TrackAsset track, Object binding) { var options = base.GetTrackOptions(track, binding); options.errorText = track.GetClips().Count() switch { 0 => "ΫϦοϓΛઃఆ͍ͯͩ͘͠͞ʂ", 1 => string.Empty, _ => "ΫϦοϓ1͔ͭ͠ઃఆͰ͖·ͤΜʂ" }; return options; } errorTextにテキストを埋めることで、エラーメッセージを表示できる GetTrackOptionsでのエラーテキスト表示 クリップ一覧はTrackAsset.GetClipsで取得できる bindingには、トラックにバインドされた オブジェクトが渡ってくる
GetClipOptionsをオーバーライドし、独自のClipDrawOptionsを返却する GetClipOptionsによる見た目の調整 [CustomTimelineEditor(typeof(SampleClip))] public class SampleClipEditor : ClipEditor { public
override ClipDrawOptions GetClipOptions(TimelineClip clip) { var options = base.GetClipOptions(clip); options.highlightColor = Color.yellow; options.errorText = clip.duration < 1.0 ? "ΫϦοϓͷ͞1ඵҎ্ʹ͍ͯͩ͘͠͞ʂ" : string.Empty; return options; } } 各クリップごとにGetClipOptionsが呼び出され、 クリップ情報が引数に渡される
引数にクリップとその背景の領域が渡されるので、その領域にテクスチャを描画する DrawBackgroundによるクリップの背景の調整 [CustomTimelineEditor(typeof(SampleClip))] public class SampleClipEditor : ClipEditor { public
override void DrawBackground( TimelineClip clip, ClipBackgroundRegion region) { var tex = new Texture2D(128, 1); for (var x = 0; x < tex.width; x++) { tex.SetPixel(x, 0, Color.Lerp(Color.blue, Color.green, (float)x / tex.width)); } tex.wrapMode = TextureWrapMode.Clamp; tex.Apply(); GUI.DrawTexture(region.position, tex); } } 背景の矩形情報がRectで渡ってくるので、 その位置にGUI.DrawTextureなどで描画する エディターの定期更新で呼び出されるので、 テクスチャのキャッシュ等でパフォーマンスの工夫が必要
トラックへのオ ブ ジェクトの バイン デ ィン グ 挙動を調整する
IsBindingAssignableFromによって、オ ブ ジェクトのバインド判定を独自に定義できる IsBindingAssignableFromの活用 [CustomTimelineEditor(typeof(SampleTrack))] public class SampleTrackEditor : TrackEditor
{ public override bool IsBindingAssignableFrom( Object candidate, TrackAsset track) { var go = candidate as GameObject; if (go == null) return false; return go.TryGetComponent(out ISample _); } public override Object GetBindingFrom( Object candidate, TrackAsset track) { if (candidate is GameObject go) return go.GetComponent<ISample>() as Object; return null; } } IsBindingAssignableFromでfalseを返せば、 トラックへのオブジェクトバインディングを 拒否できる 実際にどのオブジェクトを トラックへバインドするかを指定できる (通常やれない)特定のインターフェイス を実装したコン ポ ーネントのバインド、ができる
トラック/クリップの追加・ 編 集を 検知する
それ ぞ れ OnCreate・On(Track|Clip)Changedというコールバックから取得可能 トラック/クリップの生成・ 編 集タイミン グ の取得 [CustomTimelineEditor(typeof(SampleTrack))]
public class SampleTrackEditor : TrackEditor { public override void OnCreate( TrackAsset track, TrackAsset copiedFrom) => Debug.Log($"SampleTrack͕࡞͞Ε·ͨ͠ʂ"); public override void OnTrackChanged( TrackAsset track) => Debug.Log($"SampleTrack͕ߋ৽͞Ε·ͨ͠ʂ"); }
クリップの振る舞いを制御できる(サ ポ ートする機能を報告する) 余談: ITimelineClipAsset.clipCaps [Flags] public enum ClipCaps {
None = 0, Looping = 1 << 0, Extrapolation = 1 << 1, ClipIn = 1 << 2, SpeedMultiplier = 1 << 3, Blending = 1 << 4, AutoScale = 1 << 5 | SpeedMultiplier, All = ~None } クリップをブレンドやループが可能か、 スケールした際の挙動、クリップ外の挙動など をフラグとして複数指定できる。 public interface ITimelineClipAsset { ClipCaps clipCaps { get; } }
クリップを重ねた際に、クリップを ブ レンドできることを示す機能 ClipCaps.Blending public class SampleClip : PlayableAsset, ITimelineClipAsset
{ public ClipCaps clipCaps => ClipCaps.Blending; }
クリップの範囲外の挙動を設定する ClipCaps.Extrapolation public class SampleClip : PlayableAsset, ITimelineClipAsset { public
ClipCaps clipCaps => ClipCaps.Extrapolation; } public class SampleBehaviour : PlayableBehaviour { public override void ProcessFrame( Playable playable, FrameData info, object playerData) { // ͨͩ͠Playable.GetDuration()ͷऔΓѻ͍ҙ Debug.Log($"{playable.GetTime()} / {playable.GetDuration()}"); } }
Extrapolationを設定すると、クリップ範囲外でもProcessFrameが呼 び 出される ClipCaps.Extrapolation の挙動
TimelineClipをTrackAssetからClipに渡して、Behaviourから参照する ClipCaps.Extrapolationにおけるdurationの取得 - 1 public class SampleTrack : TrackAsset {
public override Playable CreateTrackMixer( PlayableGraph graph, GameObject go, int inputCount) { var playable = ScriptPlayable<SampleMixer> .Create(graph, inputCount); foreach (var timelineClip in GetClips()) { var clip = timelineClip.asset as SampleClip; clip.TimelineClip = timelineClip; } return playable; } } public class SampleClip : PlayableAsset, ITimelineClipAsset { public TimelineClip TimelineClip; public override Playable CreatePlayable( PlayableGraph graph, GameObject owner) { var playable = ScriptPlayable<SampleBehaviour> .Create(graph); var behaviour = playable.GetBehaviour(); behaviour.Clip = this; return playable; } } TimelineClip.durationには正しい(?) Durationが入っているので、それをClipに渡し… https://blog.yucchiy.com/2019/11/how-to-access-clip-timing-in-playablebehaviour-with-unitytimeline/
public class SampleBehaviour : PlayableBehaviour { public SampleClip Clip {
get; set; } public override void ProcessFrame( Playable playable, FrameData info, object playerData) { var normalizedTime = playable.GetTime() / Clip.TimelineClip.duration; Debug.Log($"t = {normalizedTime}"); } } TimelineClipをTrackAssetからClipに渡して、Behaviourから参照する ClipCaps.Extrapolationにおけるdurationの取得 - 2 public class SampleClip : PlayableAsset, ITimelineClipAsset { public TimelineClip TimelineClip; public override Playable CreatePlayable( PlayableGraph graph, GameObject owner) { var playable = ScriptPlayable<SampleBehaviour> .Create(graph); var behaviour = playable.GetBehaviour(); behaviour.Clip = this; return playable; } } さらにClipをBehaviourに引き回して、 BehaviourからTimelineClipを参照する https://blog.yucchiy.com/2019/11/how-to-access-clip-timing-in-playablebehaviour-with-unitytimeline/
正しく(?)正規化時間が算出できることが確認できた ClipCaps.Extrapolationにおけるdurationの取得 -3
まとめ
• TrackEditorやClipEditorの基本的な使い方について紹介しました • TrackEditorやClipEditorを継承したうえで、 差し込みたい処理をメソッドのオーバーライドで実装する • TrackEditorやClipEditorの活用方法について説明しました • Get(Track|Clip)Optionsによる見た目の調整やエラーメッセージ表示 •
IsBindingAssignableToによるオブジェクトバインドの挙動制御 • OnCreateやOn(Track|Clip)Changedを用いたイベントのフック まとめ