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
940
Other Decks in Programming
See All in Programming
明示と暗黙 ー PHPとGoの インターフェイスの違いを知る
shimabox
2
470
Discover Metal 4
rei315
2
110
#kanrk08 / 公開版 PicoRubyとマイコンでの自作トレーニング計測装置を用いたワークアウトの理想と現実
bash0c7
1
670
イベントストーミング図からコードへの変換手順 / Procedure for Converting Event Storming Diagrams to Code
nrslib
2
590
PHP 8.4の新機能「プロパティフック」から学ぶオブジェクト指向設計とリスコフの置換原則
kentaroutakeda
2
720
A2A プロトコルを試してみる
azukiazusa1
2
1.3k
WindowInsetsだってテストしたい
ryunen344
1
230
データの民主化を支える、透明性のあるデータ利活用への挑戦 2025-06-25 Database Engineering Meetup#7
y_ken
0
340
ソフトウェア品質を数字で捉える技術。事業成長を支えるシステム品質の マネジメント
takuya542
1
3.6k
dbt民主化とLLMによる開発ブースト ~ AI Readyな分析サイクルを目指して ~
yoshyum
3
340
Azure AI Foundryではじめてのマルチエージェントワークフロー
seosoft
0
150
既存デザインを変更せずにタップ領域を広げる方法
tahia910
1
270
Featured
See All Featured
Six Lessons from altMBA
skipperchong
28
3.9k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Build The Right Thing And Hit Your Dates
maggiecrowley
36
2.8k
Intergalactic Javascript Robots from Outer Space
tanoku
271
27k
Building a Scalable Design System with Sketch
lauravandoore
462
33k
The Pragmatic Product Professional
lauravandoore
35
6.7k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
48
5.4k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
667
120k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
GraphQLの誤解/rethinking-graphql
sonatard
71
11k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
YesSQL, Process and Tooling at Scale
rocio
173
14k
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を用いたイベントのフック まとめ