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
1.2k
Timeline エディター拡張入門
2024/05/10 に開催された「Unity Timeline/Playable API 完全に理解した 勉強会」の登壇資料です。
Yuichiro MUKAI
May 10, 2024
Tweet
Share
More Decks by Yuichiro MUKAI
See All by Yuichiro MUKAI
MasterMemory v3 最速確認会
yucchiy
0
1.1k
Other Decks in Programming
See All in Programming
はじめてのWeb API体験 ー 飲食店検索アプリを作ろうー
akinko_0915
0
140
フロントエンドのパフォーマンスチューニング
koukimiura
5
2k
PHPでWebSocketサーバーを実装しよう2025
kubotak
0
320
可変変数との向き合い方 $$変数名が踊り出す$$ / php conference Variable variables
gunji
0
180
Startups on Rails in Past, Present and Future–Irina Nazarova, RailsConf 2025
irinanazarova
0
240
iOS 26にアップデートすると実機でのHot Reloadができない?
umigishiaoi
0
140
商品比較サービス「マイベスト」における パーソナライズレコメンドの第一歩
ucchiii43
0
180
Model Pollution
hschwentner
1
160
Vibe Codingの幻想を超えて-生成AIを現場で使えるようにするまでの泥臭い話.ai
fumiyakume
9
3.8k
20250708_JAWS_opscdk
takuyay0ne
2
130
Modern Angular with Signals and Signal Store:New Rules for Your Architecture @enterJS Advanced Angular Day 2025
manfredsteyer
PRO
0
270
マッチングアプリにおけるフリックUIで苦労したこと
yuheiito
0
190
Featured
See All Featured
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
For a Future-Friendly Web
brad_frost
179
9.8k
How STYLIGHT went responsive
nonsquared
100
5.6k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
44
2.4k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
60k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
5.9k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
667
120k
Balancing Empowerment & Direction
lara
1
450
GitHub's CSS Performance
jonrohan
1031
460k
A designer walks into a library…
pauljervisheath
207
24k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
181
54k
Measuring & Analyzing Core Web Vitals
bluesmoon
7
520
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を用いたイベントのフック まとめ