読者です 読者をやめる 読者になる 読者になる

【Unity】ZenjectのAsSingle(),AsCached(),AsTransient()の違い

 

 

調べてみた。

 

超適当にテストコード書いて挙動を確かめた

メソッドの名前が意味不明なのは許してほしい

 

gist.github.com

 

挙動を理解しないとヤバい事故生みそうだなこれという結果になった

結果的にはAsSingle()でInjectされてる参照がUnbindで破棄されてなくて、Inject済みの参照をもう一回Bindし直してた

あとGCで回収されてなかった

 

そもそも

As〜ってなんぞやというと

BindするクラスやインスタンスのBind方法の定義である

 

https://github.com/modesttree/Zenject#binding

 

AsSingle(),AsCached()はキャッシュするのね〜

AsTransientはキャッシュ作らないのね〜くらいの認識だと

しぬ(しんだ)

 

AsTransient()

クラスをBindする際に複数インスタンスの生成を許容する

クラスがContainerにBindされている際、参照情報が一つであればResolveで解決出来る

 

しかし、Resolveは単一のインスタンスに対してのみ参照を解決するため

AsTransientでBindを複数回行っていた場合、multiple instanceエラーを吐く

 

gist.github.com

 

AsTransientでのBindには必ず.WithId()を使用してIdentiferを定義するか

ResolveAllを使用した参照解決を図るようにした方が良い

 

また、As〜などの定義をしなかった場合はデフォルトでAsTransientになる

Factoryなどを使用したインスタンス生成もAsTransientになるため、Instantiateで複数生成したクラスに対してResolveするとエラー吐く、気をつけよう

(まあInstantiateした時点でResolveすることなんてほとんどないんだけど)

 

AsCached()

Bindされているクラスがすでにインスタンス化されている場合、同じインスタンスを使い回すようにする

また、UnbindするとCacheされていたインスタンスは全て参照を破棄される

 

同じインスタンスは使いまわしたいが、Unbindで破棄したい時はこれを使用する

クセがなく、取り回しがしやすいので動的Bindをする際は非常におすすめしたい

 

AsSingle()

クラスをBindする際にSingletonとして定義する

クラスがContainerにBindされている際は

「そのContainer内で唯一存在するクラス」となることが保障される

 

Bindされている際は、Cachedと同じ挙動をする

 

しかし、Unbindを呼んだ際に

Bindした自身の参照を他のクラスにInjectしなくなるが、生成したインスタンス自体は破棄されずキャッシュされたまま

のため、Unbindを呼び、その後もう一度同じクラスをBindしても

new()などが走らず、キャッシュされていた参照をもう一度Bindして使い回す

 

gist.github.com

(HashCodeでやっているが、他にもResolveで返ってきた値の中身を書き換えたところどちらも書き換わった)

 

AsSingle()は必ず一つ、かつ一度しか生成されないオブジェクトに対してのみ使用しよう

 

 

まとめ

readmeには、「AsSingle()をほとんどの場面で使いたいと思うでしょうが〜」とか書いてあるけど

基本AsCached()でいいわ。

 

AsSingle()で動的Bindすると初期化走らせられなくてビビった結果、動作を知れたのでまあよかったと思う。

 

あと、ZenjectのUnitTestマジで神以外の何物でもないのでバンバン使っていきたい

【Unity】Zenjectで禅の心を手に入れる

 

Zenjectについて

github.com

 

導入を躊躇っていたものの、あるところから脅しがかかったので本格的に使い始めた

 

基本的な使い方などはこちらをご参考いただければわかるのではないかと思います

Unity3DのDIフレームワーク、Zenjectの紹介 | Aiming 開発者ブログ

 

今回はちょっと具体的な使い方を模索してみました。

 

何ができるのか

リポジトリのreadmeを読めばわかる

DI(Dependancy Injection)を行うためのライブラリで、すっごい簡単に言うと

 

newの引数とか、シングルトンで引っ張ってきてたオブジェクトを、登録しておいたオブジェクトの中から勝手に引っ張ってきて自動で参照を入れてくれるやつ

 

って認識でいいと思う、もちろんこれは機能のごく一部に過ぎない

他にもいろいろできる、公式読んで 

 

中身の話

Zenjectの基本は

 

  1. Containerにオブジェクトを登録
  2. Containerに登録したオブジェクトをインスタンス化(この時に引数などの参照が自動で持ってこられる)
  3. オブジェクトが作られたらごにょごにょする

 

になる

Containerは、登録されたオブジェクトが入ってるDictionaryみたいなもので

ここの中にオブジェクトが登録されていないと、参照を解決できない

また、自動で参照してくれるようにするためには、参照先のオブジェクトも登録してあげないといけない

 

つまり、こう

 

gist.github.com

 

これは全てにおいて適用されるので、まずBindを忘れないようにしよう。

 

使い方

まだ全てを把握できているわけではないので、状況別で

 

 

SingletonにしていたManagerを、Singletonにしない

(MonoBehaviour継承クラスに、参照を渡す方法)

 

Zenjectには、ZenjectBindingというコンポーネントが存在する

こいつは、登録したコンポーネントを勝手にContainerに登録してくれるすごいやつ

 

実際に使ってみるとこうなる

f:id:yutakaseda3216:20170417121301p:plain

 

Managerの中身は、普通のMonoBehaviour継承クラスになっている

使用する場合は、こう書く

 

gist.github.com

 

MonoBehaviour継承クラスの場合は、Injectアトリビュートを使用することで自動で参照が設定される

非継承の場合は、newで渡してあげる

 

これで、Managerは自動で使いたいところで使えるようになるので、ただのstatic参照のためだけにSingletonである必要がなくなる

もちろん、同一インスタンスが一つしかないことを保証するために使うのはあり

同一クラスは一つのみ生成し、あとはキャッシュを使う.AsSingle()というBind方法が存在する

  

同じことをしてる人がいたので、こちらも

qiita.com

余談ですが、torisoupさんはいつも勉強になる記事をあげてくださっているので定期購読をお勧めします

 

また、Manager以外にもZenjectBindingに登録さえすればなんでも使えます

Playerとかカメラとかを登録すると捗るかもね

 

 Pureクラスを好きなタイミングでBindしながら生成する

 

MonoBehaviourの参照を渡したりするPureクラスとか・・・

クラスがごちゃごちゃしがちな原因の一つだが、Zenjectを使用することで解決出来る

 

 

まずMonoBehaviour継承クラスで渡したいクラスをBindする

  • シーンに最初から直置きしてあればZenjectBindingを使用する
  • 動的に生成される場合は、Container.Bindを使用してContainerに登録する

 

動的生成する際に勘違いされがちだが

別にInstallerを使って記述しなくてもいい

 

DIContainerへのBindさえできればよく、さらに

DIContainerはBindされているので好きなクラスにInjectで引っ張ってこれる

好きなタイミングでBindすればいいと思う

 

 試しに、ボタンを押したらHogeをBindして生成するにはこうする

 

gist.github.com

 

GameManagerはBindInstallerか何かでBindしてあげる必要があるが

BindさえすればDIContainerを引っ張ってこれるのでこういう書き方ができる

 

Container.Resolve<Hoge>()はBindされているオブジェクトをインスタンス化してreturnするメソッド

他にも.TryResolve<T>()などのメソッドが用意されているので、動的バインドする基底クラスかヘルパーを作ることをお勧めする

 

Resolveについては公式を

https://github.com/modesttree/Zenject#dicontainer-methods-resolve

 

ちなみに、BindされたPureクラスは明示的にUnBindしない限り参照を握られ続けるため破棄されない

Disposeメソッドなどを使い、破棄するタイミングでContainer.UnBind<Hoge>()を呼んであげよう

 

(これって非推奨だったりするのかな、知っている人がいたら教えてください)

その他

.FromNew

.FromMethodsとかは紹介したかったのですが、長くなりそうなのでまとめたときにでも

 

DIContainer.Inject

DiContainer.Instantiate

は今試してます。追記予定。

 

処理速度とかの測定はしてないんだけど、Bindは結構重いらしい

まあそうだろうねって感じだけど、これも測らないといけないね

 

終わり

禅の心強いわー

【Unity】StateMachineBehaviourについてと注意まとめ

StateMachineBehaviourとは

 

過去記事

 

yutakaseda3216.hatenablog.com

 

 

yutakaseda3216.hatenablog.com

 

 

AnimatorのStateにAddComponentできるスクリプト
(MonoBehaviourみたいな感じ)

クラスに継承させることで使用できる

役に立たない公式リファレンスはこれ
https://docs.unity3d.com/jp/540/ScriptReference/StateMachineBehaviour.html

動作

基本的には用意されたコールバックをoverrideすることで使用する

 

  • OnStateEnter
  • ステートマシンがこのステートを評価している場合、最初の Update のフレームで呼び出されます。

MonoBehaviourで言うところのStart()

 

  • OnStateUpdate
  • 最初と最後のフレームを除いて Update フレームごとに呼び出されます。 

MonoBehaviourで言うところのUpdate()

 

  • OnStateExit
  • ステートマシンがこのステートを評価している場合、最後の Update のフレームで呼び出されます。 

MonoBehaviourで言うところのOnDestroy()


Update()が完全に流れなくなるフレームで呼ばれる

 

 

----------この辺からよく知らずに使うと闇---------------------------------

 

 

  • OnStateIK
  • MonoBehaviour.OnAnimatorIK の直後に呼び出されます。

IKを使用した処理を行う


overrideすると、IKの処理を自前で実装することになり(Animator.SetIK〜を使う)

 

Stateに対してIKが自動で適用されなくなる

base.OnStateIKを呼んでもダメ

 

基本overrideする必要なし

 

  • OnStateMove
  • MonoBehaviour.OnAnimatorMove の直後に呼び出されます。 

Animationに設定されたRootAnimationの処理を行う

 

Animation内のPosition、Rotation、Scaleに関わる処理が行われるため

 

overrideすると、Stateに対してRootAnimationが適用されなくなる

base.OnStateMoveを呼んでもダメ 

 

基本overrideする必要なし

 

 

AnimationCrossFade時のStateの取り扱いについて

CrossFadeのリファレンス

https://docs.unity3d.com/jp/540/ScriptReference/Experimental.Director.IAnimatorControllerPlayable.CrossFade.html


AnimationがCrossFadeしている時(durationが0以上の時)

 

CrossFadeが終了するまで、CurrentStateはCrossFade元のまま
さらに、normalizedTimeもそのまま加算され続ける

 

normalizedTimeを使用した処理をする時は、CrossFadeを使用しないようにするか
自前でnormalizedTimeを使用しよう

 

 

最後に

 

用法用量を守って使う
基底クラスを作ろうとしてOnStateMoveとOnStateIKをoverrideすると痛い目にあう(あった)

【Unity】OnStateMoveをoverrideするとRootMotionが適用されずに詰む

StateMachineBehaviourについてのまとめを読んでください

 

yutakaseda3216.hatenablog.com

 

概要

Unity.ver 5.4.4

 

UnityにはStateMachineBehaviourというものがある。

Unity - スクリプトリファレンス: StateMachineBehaviour

 

AnimatorのStateに紐づけられるスクリプトで、そこそこ便利

その中にOnStateMoveというoverrideできるメソッドがある

 

日本語のリファレンスはこんな感じ

Unity - スクリプトリファレンス: StateMachineBehaviour.OnStateMove(Animator animator, AnimatorStateInfo animatorStateInfo, int layerIndex)

何も書いてないに等しい

 

それで今回は表題の通り地雷を踏んだ

 

RootMotion

RootMotionはAnimationに適用されているポジションや回転をオブジェクトに作用させてくれる機能で、それがなぜか効かなくなって半ギレしていた

(Apply Root Motionはチェックを入れていて、そのほかもろもろもすべてokだった)

 

StateMachineBehaviour用の基底クラスを作成して、処理を共通化した時に効かなくなったので何かあるなと思い調査

 

結果、StateMachineBehaviourのOnSTateMoveがRootMotionのPosition更新制御を行っていて、それを直接overrideして書き換えている様子

なので、overrideしているとそのStateにセットされているアニメーションは適用されなくなるらしい

 

base.OnStateMove()を呼んでみたものの、適用されなかったので

 

OnStateMove()は自前で移動処理を書かないなら

絶対にoverrideしてはいけない

 

でFA、OnStateUpdateで間に合わせよう

 

 

この辺の仕様はマジでリファレンスに書いとけよUnity

 

UnityのコーディングにVisual Studio for Macを導入して一週間が過ぎた

 

入れました、導入した後にやった設定と感想をつらつらと書きます

とりあえずPreview版とはいえ、まあなかなかでした

 

導入・インストール

New Release Preview: Visual Studio for Mac | Visual Studio

ここからインストールしてください

 

導入・Unity側の設定

f:id:yutakaseda3216:20170228112214p:plain

Preferences を開いて

f:id:yutakaseda3216:20170228112319p:plain

External Tools からExternal Script Editor でインストールした Visual Studio for Macを指定してあげる。

 

これでスクリプトファイルを開くと動くぞ!

 

とはいかない、このままだとインテリセンス(補完)が効かない

 

導入・VSforMac側の設定

f:id:yutakaseda3216:20170228112608p:plain

※機密プロジェクトがあるので名前がバレないように「あ」を入力してます

 

Open から、Unityのプロジェクトディレクトリの中にある.sinファイルを選択して、ソリューションを開こう

.sinファイルがない時は、Unityでスクリプトを選択して開けば多分自動生成されるはず

 

これでインテリセンスが効くようになる

ただこのままだと死ぬほど使いづらいので色々カスタムする

 

カスタマイズ・エディタカラー

まずはエディタのカラーを変える、白背景とか、謎の文字色とかゴメンだし

 

f:id:yutakaseda3216:20170228113052p:plain

Visual Studio から ユーザー設定を開く

余談だけど日本語のメニューなのいいよね。

 

f:id:yutakaseda3216:20170228114613p:plain

 

インターフェースを エンジニア大好き Dark にした

 

f:id:yutakaseda3216:20170228113156p:plain

テキストエディターからColor Themeを選択、選択すればエディタが変更されるので好きな色を選ぼう

 

現状Theme編集機能はないらしい

自分でカスタマイズしたテーマがあるときは、MonoDevelopなどからエクスポートして持ってこよう

自分はOblivion派なので、今回は割愛

 

カスタマイズ・ポリシー

ソリューションごと、あるいはグローバルでコードフォーマットなどを定義できる

f:id:yutakaseda3216:20170228113452p:plain

f:id:yutakaseda3216:20170228113520p:plain

ここから新しくポリシーを作る

基本的にはコードの書式設定から、C#の設定項目を変更していく

 

自分の好きなようにやろう

ソリューションごとにポリシーを適用できるので、プロジェクトを複数抱えてても安心

 

ポリシーにAttribute改行の設定項目がない件について

 

KUSO

Unity使ってると頻繁に出てくるSerializeField変数を一行で宣言したい時、アトリビュートで勝手に改行されるのでその度にCmd+Zを押すことになる

その上アトリビュートで囲むと補完されないので一回一回手打ちになる

 

カスタマイズ・コードテンプレート

とりあえず上記の問題を解決するためによく使うアトリビュートをテンプレート化した

f:id:yutakaseda3216:20170228114225p:plain

追加する時はユーザー設定からCode Snippetsを選択して、追加をする

また、$class$などの記述を行うことでフリー入力を作ることもできるので活用する

 

今回、アトリビュートでよく使うSerializeFieldを登録した

ただ、テンプレートに

 

[SerializeField] $class$ $field$

 

みたいな感じで記述すると勝手に改行されたのでやらないことにした

わけわからんので製品版で早く設定追加してほしい

ただ海外フォーラム見てると超昔から言われてるのに開発者がResharperを使ってるからいつまでも追加されないとかなんとか

 

カスタマイズ・パフォーマンス

とりあえずで上記の設定を終え、使ってみると

インテリセンス遅すぎワロタ状態

だった

 

とにかくクッソ重い、ちなみに使用してるmacは2015年モデルのフルカスタマイズなのでスペックが低いわけじゃないと思う

なので、パフォーマンス改善をする

 

f:id:yutakaseda3216:20170228114922p:plain

 

まずは何はともあれテキストエディタからアニメーションを無効に

強調表示とかも消す

他にも視覚効果をOFFっていく

 

f:id:yutakaseda3216:20170228115041p:plain

 

効果があるかはわからないものの、コードの動的コンパイルが走るので解析も毎回走るんじゃないかと思い解析も切った

 

だいぶ早くなった

 

カスタマイズ・キーバインド

人それぞれすぎるので割愛、設定しとくと楽やで(提案)

 

 

最後

頻繁に落ちる上に操作不能になるMonoDevelopよりは使いやすい

VisualStudioCode、AtomVimとかのエディタよりIDE派なので今後に期待したい・・・特にアトリビュートとパフォーマンス

メモリ2GB食われるのでメモリの残量には気をつけよう

 

 

もうしばらく使ってみる

 

昨今のUnity開発におけるMVP設計思想についてと、それの適用可否の話

はじめに

こちらの記事を読んで、最近Unity開発でよく言われるようになった

MVPについてだいぶ浸透(あるいは導入)してきたのではないかと思った。

developers.cyberagent.co.jp

 

ただ、すべてのゲームがこの思想でカバーできるかというとそうでもないと思っている。

 

「そりゃ仕様によって最適解は変わるだろw」と思うかもしれないが

言いたいことはそういうことではなく

MVP設計というのは元々Web業界で浸透した設計であり

WebのフロントはユーザーがUIを操作するものであるということだ。

 

つまりこの設計思想をそのまま、あるいは少しのアレンジを加え適用できるのはユーザーがUIを操作することが基本となる

モバイル向けソーシャルゲームアプリということである。

 

それを意識せず、ユーザーがコントローラを持ち、キャラクターを操作するタイプのゲームなど(あくまで、など)で

 

「MVPっていうのがいいらしい」

「とりあえず導入しよう」

 

でどこまでがModelでどこまでがViewでPresenterをどこまで定義すれば良いのかが曖昧のまま設計が迷走する例をよく見た。

 

MVPの具体的な活用法に対しては先ほどの記事を読んでもらえばわかると思うので 

今回はModel-View-Presenterに対して、各レイヤーの解釈について特にUnityゲーム開発における持論を書こうと思う。

 

ある人にとっては間違っているかもしれないが、ある人にとっては正解への道標になるはずだ。

 

先に結論を書くと、Viewは積極的に拡大解釈しろという話だ。

 

あとUniRxとMVPの理解は前提で。

  

レイヤーの明確な責任

まずModel-View-Presenterにおける役割を明確にするのと同時に

ゲームを作るために必要なレイヤーを全て洗い出す。

 

MasterData

一応書く。

ゲーム中の経験値テーブルや、アイテムのId、価格などのデータ。

 

Model

ゲームを構成するマスターデータをもとに、ユーザーの現在所持しているアイテム数や、経験値などのいわゆるセーブ対象になるデータを管理しているもの。

ゲームを進行するごとに、書き換えられていく。

 

役割として

 

  1. データを保持する
  2. データの書き換えロジックを、メソッドとして外部に公開する
  3. データが書き換えられた時、通知を発行する

 

この役割さえ守っていれば、中身でどれだけインスタンスを生成しようと、どんなロジックを持とうと構わない。

逆に言うと、これ以外で役割を持ってしまってはいけない。

そしてModelは依存先を持たず、どんな状態でも自身をインスタンス化できるようにしなければならない。

 

View

プレイヤーが実際に見る画面と勘違いされやすい(名前的に)。

本質的な役割は

 

  1. ユーザーが実際に見る画面を構成し、表示する
  2. 画面を更新するメソッドを外部に公開する
  3. ユーザーの操作を通知する

 

最初に書いた通り、UIを操作するだけであるならこのUIが操作されたという通知を飛ばすだけで済む。

その通知の結果、Viewの更新を受け取るだけで済むからだ。

 

ただ実際にはそうはいかない。

なぜなら、キャラクターを操作できるゲームであるなら、キャラクターの位置が更新され、その結果アイテムを拾えたりする領域に入ったりするなど、複雑であるからだ。

 

そして愚直にMVPに当てはめるのであれば、コントローラ操作というViewが通知を発行し、キャラクターのモデルが位置の変更を通知で管理しなければいけなくなる。

 

はっきり言ってそんな設計は冗長すぎてクソだ。

なので後ほどViewの拡大解釈と自由設計について記述する。

 

ただ、上記3つの役割自体は変わらないので厳守しなければならない。

そして、Modelと同じく、Viewはどんな時でもインスタンス化可能でなければならない。

 

Presenter

具体的な機能の提供者である。役割として

 

  1. Viewからの通知を受け取り、Modelが外部公開しているメソッドを呼ぶ
  2. Modelのメソッドを呼んだ結果、Modelからの通知を受け取り、Viewが外部公開しているメソッドを呼ぶ

 

単体では機能しないViewとModelをつなげ、ゲームとして成り立つような機能をロジックとして提供する

となっているが、例として

 

  1. アイテムを拾える領域に入った通知がViewから飛んでくるので
  2. その通知が飛んできた後で他のViewからボタンが押された通知が飛んできたら
  3. Modelが公開しているアイテムを一個増やすメソッドを呼び
  4. アイテムが増えたという通知を受け取り
  5. Viewが公開している「アイテムを拾った演出」を出すメソッドを呼ぶ

 

という処理を行うといえば、わかりやすいだろうか。

Viewが作り込まれれば、あとはPresenterを量産するだけでゲームを拡張していくことが可能になる。

 

 

以上がレイヤーの責任の話。

 

 

Viewを積極的に拡大解釈していく

 

ここまでの話で、何が一体問題なのかというと

 

ゲームはユーザーの操作がバリエーション豊富すぎてViewの役割がブレやすい

 

というところだ。

 

例として、ゲームの操作キャラクターについての話をした。

ここで考えたい

 

キャラクターとはどこのレイヤーに属するのか?

 

ゲームの1機能としてキャラクターを見るのであれば

Model-View-Presenterにまたがって処理を書かなければいけない。

 

でも、ユーザーの操作だし、キャラだって要は画面の1要素なのだからView単体で記述しなければいけない。

 

どっちだろうとなる。

 

結論としては、どっちも正しい

なので

 

Viewとしての役割を厳守できれば、内部の処理はView内で完結されたMVPで記述されても良いのでは

と考えることも可能である。

 

そしてさらに拡大解釈するなら

 

Viewとしての役割さえ守れていれば、中身の設計はなんでもいい

 

だ。

 

例として、クラス図を書いてみた。

 

f:id:yutakaseda3216:20170222145935p:plain

 

ViewがCharacter全体の各コンポーネントで、ゲームに関連するHpなどをModelに移譲することで見通しが良くなる。

 

CharacterPresenterはCharacterの各コンポーネントの通知を使って、Modelを更新すれば良くなるが、既存のPresenterとは違う点がここで生まれる。

 

それは通知を発行したViewは自身で更新を行うということだ。

ダメージを受けた時に、ダメージモーションの遷移などをいちいちPresenterから発行されていては、Character自身が独立して動かなくなってしまう。

 

なのでPresenterの役割としては

 

Viewの通知を使い、Modelを更新した結果、必要であれば他のViewに対しての更新を行う

 

になる、ダメージの結果HPゲージを更新する処理を呼ぶとかね。

 

 

最後

 

蓋を開けてみればなんだそんなことかと思うかもしれないが

責任を明確にしないままMVPを導入するとカオスになるのを身を以て経験している。

 

うまくレイヤーを定義してやり、そのレイヤーに属するものは何なのかを考えよう。

そして、責任さえ守れていれば、レイヤーの中身は自由に解釈して構わない

 

そういう結論を今自分では出している。

 

 

推敲されておらず、勢いだけで書いた文なのでわかりづらいかもしれないが

 

 

許してね^^

 

 

何かあればTwitterまでどうぞ。

 

あとこんなこと書いてる暇あるならゲーム作れって言われそうなのでゲーム作ります・・・。

 

UniRxでObservable -> IEnumerator -> Observable変換した時のライフサイクル

コード

gist9ab1097554ad9f37a8931f82cfdcb467

 

破棄されない話

.SelectMany()にCoroutineを使用するとうまく破棄されない。

.ToYieldInstruction()を使ってObservable -> IEnumeratorに変換したCoroutineを実行中にコンポーネントを削除するとエラーを吐き出す。

 

.ToYieldInstruction()したCoroutineはどうやら源流のObservableに紐づけられないようなので

別途.AddTo()でライフサイクルを定義してあげる必要がある

 

CompositeDisposableなどを使用したライフサイクル管理をしている場合は

  1. 引数でライフサイクルの依存先を渡す
  2. Observable.FromCoroutine(_ => Coroutine()).AddTo(依存先)

でなんとかできるのではないでしょうか。

 

追記

 

作者様

neuecc (@neuecc) | Twitter

 

よりご回答いただきました、模範解答はこちらのようです。

gist.github.com

 

CancellationTokenと言う仕組みがあり、引数にそいつを渡してあげることでライフサイクルを定義できるようです。

シランカッタ・・・

 

使うときはちゃんとオペレータの実装も見ないといかんですね。

neuecc様有難うございました。

 

 

終わり