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
どこのご家庭にもあるシーンマネージャーの話
Search
いも
March 24, 2021
Programming
8.7k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
どこのご家庭にもあるシーンマネージャーの話
いも
March 24, 2021
More Decks by いも
See All by いも
UnityプログラミングバイブルR6号宣伝&Unity Logging小話
adarapata
0
630
Unityテスト活動のふりかえり
adarapata
1
660
Gather.townはいいぞ その後
adarapata
1
1.7k
Unityでの開発事例
adarapata
3
23k
Gather.townはいいぞ
adarapata
2
2.4k
宴はいいぞ
adarapata
0
2.2k
わかった気になるモブプログラミング
adarapata
1
170
モブワークっぽいのをやっている話/Trying mobwork
adarapata
2
1.3k
Zenjectを導入する前に
adarapata
0
4k
Other Decks in Programming
See All in Programming
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
13
3.6k
Javaの型とAI時代に型が大事な理由 / java types and type in AI era
kishida
2
120
肥大化するレガシーコードに立ち向かうためのインターフェース分離と依存の逆転 / JJUG CCC 2026 Spring
hirokunimaeta
0
530
Oxlintのカスタムルールの現況
syumai
6
1.1k
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
4.9k
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
210
セキュリティの専門家じゃなくてもできる。「セキュリティ意識」をアップデートして サプライチェーン攻撃への耐性を高めよう。
tk3fftk
5
710
フロントエンドとバックエンドで「1文字」を揃えよう
youkidearitai
PRO
0
260
その問い、本当に正しいですか?AI時代のエンジニアに必要な哲学と認知科学 / ai-philosophy-cognitive-science
minodriven
6
4k
正しくソフトウェアを作る、前提を疑うための認知の視点 / doubt-premise
minodriven
20
6.5k
気づいたらRubyで100作品 ー クリエイティブコーディングが生活の一部になるまで / 100 Ruby Sketches Later: How Creative Coding Became Part of My Life
chobishiba
3
560
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
0
210
Featured
See All Featured
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
420
What does AI have to do with Human Rights?
axbom
PRO
1
2.2k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.2k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
390
My Coaching Mixtape
mlcsv
0
140
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
65
56k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
570
Visualization
eitanlees
152
17k
30 Presentation Tips
portentint
PRO
1
320
Transcript
━━━ どのご家庭にもある シーンマネージャーの話 いも ━━━ 2021/03/24 Gotanda.unity #17 1
いもです twitter @adarapata Unity触ったりしています Godot Engineも好きです 2021/03/24 Gotanda.unity #17 2
モチベーション 画⾯遷移の管理の仕組みを毎回作っている気がする みんな各⾃で似たようなもの作ってる気がする しかしあまり公開されてる気がしない 話すからみんなも教えて下さい 2021/03/24 Gotanda.unity #17 3
シーンマネージャー is Unityでの「画⾯をいい感じに管理してくれる」君 画⾯の単位を定義して管理する 1Sceneとか1Prefabとか 画⾯をスタックできる 前の画⾯に戻ったりとか 画⾯遷移にデータを渡せる 前の画⾯で選択した設定でクエストに挑むとか 2021/03/24
Gotanda.unity #17 4
Unityデフォルトでの画⾯管理 Sceneファイルの切り替え・追加は可能 Prefabはデフォルトではない 画⾯の名称の管理が必要 遷移先の⽂字列 or Indexをどう持つか。増えたらどうするか パラメータを渡す⼿段がデフォルトではない スタックする機能はない ⼀個前の画⾯の状態を保持させるなど
この辺を解決するために画⾯管理クラスを作ったりする 2021/03/24 Gotanda.unity #17 5
解決したいこと 画⾯をスタックできること パラメータを渡せること 名称の管理が楽なこと 2021/03/24 Gotanda.unity #17 6
⾃作の⼀例 : PageManager 画⾯のことを「Page」と呼ぶ SceneはUnityの呼称とかぶるのでPageにした 1Page = 1Prefab Page Prefabを動的にロードして追加する
Pageをスタックしており、前のPageに戻れる 遷移先Pageを型で呼ぶ パラメータを型で渡せる 依存関係の解決にVContainerを使⽤ 2021/03/24 Gotanda.unity #17 7
呼び⽅ var parameter = new FooPage.CreateParameter { Bar = "bar"
}; _pageManager.Push(new FooPage.Transition { Parameter = parameter }); // 追加 _pageManager.Pop(); // ⼀つ前に戻る _pageManager.Replace(new FooPage.Transition { Parameter = parameter }); // 現在のページを破棄して追加する // スタックしたページをすべて破棄して追加する _pageManager.ReplaceAll(new FooPage.Transition { Parameter = parameter }); await _pageManager.PushAsync(new FooPage.Transition { Parameter = parameter }); // ⾮同期で追加 2021/03/24 Gotanda.unity #17 8
Page定義側 [PageAsset("BarPage.prefab")] public class BarPage : BasePage { public class
Transition : BasePageTransition<BarPage>{} } 2021/03/24 Gotanda.unity #17 9
登場⼈物 PageManager : ページのスタック・ライフサイクル管理者 IPageTransition : ページ遷移者 BasePage : ページ本体
2021/03/24 Gotanda.unity #17 10
クラス図 2021/03/24 Gotanda.unity #17 11
PageManager Pageのスタックを管理する リストで保持してる 読み込まれたPageに対して初期化・停⽌というライフサイクルイベ ントを呼んであげるのが役割 具体的にどうやってPageが読み込まれるのかは知らない public async UniTask PushAsync(IPageTransition
transition) { var page = await transition.LoadPage(); await page.Initialize(); _pages.Add(page); // ページリストの管理 } 2021/03/24 Gotanda.unity #17 12
処理の流れ 2021/03/24 Gotanda.unity #17 13
ライフサイクル Unityのライフサイクルに合わせるとハンドリングしにくい StartはPageManager側から⾒て初期化済みか保証できない Destroyで破棄はできるが⼀時停⽌とか再開がない なのでざっくりと4つのライフサイクルイベントを⽤意 Initialize Suspend Resume Discard 2021/03/24
Gotanda.unity #17 14
Pushすると、現在のPageのSupend、新しいPageのInitializeが呼ばれ る Popすると、現在のPageのSuspend->Discard、⼀つ前のPageの Resumeが呼ばれる 2021/03/24 Gotanda.unity #17 15
BasePage 画⾯⾃体を司るクラス ライフサイクルを実装しており、初期化とか停⽌とかできる PageManagerとか外の世界とのやり取りを⾏う際の受け⼝ なので内部はMVCとかMVPとか好きに分割してる 2021/03/24 Gotanda.unity #17 16
public abstract class BasePage { public virtual UniTask Initialize(); public
virtual UniTask Suspend(); public virtual UniTask Resume(); public virtual UniTask Discard(); } public abstract class BasePage<TParam> : BasePage { protected TParam Parameter { get; } } 2021/03/24 Gotanda.unity #17 17
IPageTransition Pageの呼び出し⽅を知っているやつ PageManagerが直接Pageの呼び出しを⾏うのではなく IPageTransitionに具体的な呼び出しをお任せする public class PageTransitionBase<TPage> : IPageTransition where
TPage : BasePage { public virtual async UniTask<PageBase> LoadPage() { var pageInstance = await DoPrefabLoad(pageAssetName); var pageLifetimeScope = pageInstance.GetComponent<LifetimeScope>(); InitializePageParameter(pageLifetimeScope); pageLifetimeScope.Build(); // Page側の依存関係解決 var page = pageLifetimeScope.Container.Resolve<TPage>(); return page; } } 2021/03/24 Gotanda.unity #17 18
リソースとクラスのマッピング どのアセットを引っ張ってくるのかパスを渡したい 毎回⼿打ちで渡したくはない クラス定義とPageアセットは原則1:1と考えて良い // pageAssetNameをどう渡すか var pageInstance = await
DoPrefabLoad(pageAssetName); カスタムアトリビュートで直接紐付けてしまう 2021/03/24 Gotanda.unity #17 19
[AttributeUsage(AttributeTargets.Class, Inherited = false)] public class PageAssetAttribute : Attribute {
public string PrefabName { get; } public PageAssetAttribute(string prefabName) { this.PrefabName = prefabName; } } [PageAsset("BarPage.prefab")] public class BarPage : BasePage { public class Transition : BasePageTransition<BarPage>{} } // PageAssetAttributeを取り出す var pageNameAttr = Attribute.GetCustomAttribute(typeof(TPage), typeof(PageAssetAttribute)) as PageAssetAttribute; var pageInstance = await DoPrefabLoad(pageNameAttr.PrefabName); 2021/03/24 Gotanda.unity #17 20
パラメータ渡し [PageAsset("FooPage.prefab")] public class FooPage : BasePage<FooPage.CreateParameter> { public class
CreateParameter { public string Bar; } public class Transition : BasePageTransition<FooPage, CreateParameter> { } } ジェネリクスで定義できるようにする 2021/03/24 Gotanda.unity #17 21
そもそも外部パラメータをどう扱っているのか public class LifetimeScopeWithParameter<TParam> : LifetimeScope { public TParam Param;
protected override void Configure(IContainerBuilder builder) { builder.RegisterInstance(Param); } } 外からパラメータを設定できるようなLifetimeScopeを⽤意 2021/03/24 Gotanda.unity #17 22
パラメータを設定した上でBuildする public class BasePageTransition<TPage> : IPageTransition where TPage : BasePage
{ public virtual async UniTask<PageBase> LoadPage() { var pageInstance = await DoPrefabLoad(pageAssetName); var pageLifetimeScope = pageInstance.GetComponent<LifetimeScope>(); InitializePageParameter(pageLifetimeScope); pageLifetimeScope.Build(); // Page側の依存関係解決 var page = pageLifetimeScope.Container.Resolve<TPage>(); return page; } protected virtual void InitializePageParameter(LifetimeScope container) { } } public class BasePageTransition<TPage, TParam> : PageTransitionBase<TPage> where TPage : BasePage<TParam> { public TParam Parameter { get; set; } protected override void InitializePageParameter(LifetimeScope scope) { if (scope is LifetimeScopeWithParameter<TParam> s) { s.Parameter = Parameter; } } } 2021/03/24 Gotanda.unity #17 23
PageのアセットにLifetimeScopeをアタッチする public class FooLifetimeScope : LifetimeScopeWithParameter<FooPage.CreateParameter> { [SerializeField] private FooView
_fooView; protected override void Configure(IContainerBuilder builder) { base.Configure(builder); builder.Register<FooPage>(Lifetime.Scoped); builder.Register<FooPresenter>(Lifetime.Scoped); builder.Register<FooModel>(Lifetime.Scoped); builder.RegisterComponent(_fooView); } } これでPageのオブジェクトに外部データをInjectionできる 2021/03/24 Gotanda.unity #17 24
好きなところ 型ベースで画⾯遷移を⾏える 定数を管理しなくていい 渡すべきパラメータも型で管理できるので楽 コード⾒ればリソースがわかる 1:1の関係なら密接でもいいんじゃない派 2021/03/24 Gotanda.unity #17 25
PageManagerまとめ 遷移先をクラス名で指定できる パラメータも型で定義して渡せる Managerから詳細な遷移処理を切り出している Pageは独⾃にライフサイクルを持たせている クラスとアセットをカスタムアトリビュートで紐付けしている 2021/03/24 Gotanda.unity #17 26
みなさんの 画⾯遷移マネージャーの話を お待ちしています 2021/03/24 Gotanda.unity #17 27