この記事は,Unity Advent Calendar 2018の20日目の記事です.
https://qiita.com/advent-calendar/2018/unity
##はじめに
こんにちは、突然ですが非同期処理、どういう風に実装していますか?
unirx? coroutine? delegate?
うーん、確かにどれもこれも実績があって安定してるんですが本当にそれでいいんでしょうか?
皆さんメリットを感じて使ってると思うんですが
僕が開発中にぶち当たった悩みを聞いてください
unirxやdelegate系
####メモリリークの危険性
http://baba-s.hatenablog.com/entry/2017/06/29/100000
####eventは解除しにくい(unirxならdisposeできるけど
https://ufcpp.net/study/csharp/MiscEventSubscribe.html
####前シーンのコールバックが呼ばれる危険性
globalなassetbundle managerにdelegate渡して、別シーンに遷移したら処理が完了して呼ばれるけど、前シーンのリソースは死んでるのdnull error
unirxならdisposeで消えてくれるのでちゃんと実装すれば安全
####リアクティブスパゲッティ化
ボタン押されたら通信して~みたいのをイベント駆動で実装しちゃうのを多く見てきました
そしてボタン同時押し問題や通信中にボタン押せちゃう問題をguardパターンや透明な板などで無理くり実装してコードが複雑化して保守困難になったケースを多く見てきました
(運営始まったら既存コードをフリーズしてglobal変数追加しまくって機能実装とか不具合隠蔽とかやっちゃだめですよ、絶対ですよ
##coroutine
####yield return 1フレーム待ちによるパフォーマンス問題
foreach( )
yield return op
単体で呼び出したら1msにも満たないのにyield return しばっかりに1フレーム(60fpsなら16.66ms)も時間かかっちゃった
これをforloopで回すととんでもない時間待つことになります
(メインスレッド止めてるわけではないし、負荷を複数フレームにわたって分散したいときは逆に便利
####ゾンビコルーチン問題
global monobehaviourでStartCoroutineして何か分からないけど処理が終わらない場合、そのコルーチン生きてますよ?
ある時突然処理が進みメンバ変数にアクセスして死亡
裏でいくつコルーチンが動いているのか可視化できないため予期せぬ不具合の原因になります
####止めるのが難しい
MonoBehaviourや自前のupdateであれば、enable=Falseや呼ばない事でupdateさせずにすみます
一方コルーチンを止めるにはgameobject.SetActive(false)やStopCoroutineしなくてはいけません、enable=falseでは止まりません
前者はSetActive(true)しても再開しないというややこしい問題があります
####実行するのが難しい
そもそもなんでMonoBehaviour.StartCoroutine呼ばなくちゃ
####デバッグしにくい
ブレークポイントがはりにくいです、初期化をコルーチンでやるとかアリガチなんですが
なんか分からないけど初期化終わらない時にどうデバッグしますか?
ブレークポイントで止めたらコールスタック呼び出し元がコルーチンだった時は本当につらいです
普通のstatemachineならルートupdateにブレーク貼って問題を切り分けるのが簡単なのですがコルーチンはそうはいきません
他にも引数返せないことによるコードの複雑化がありますね
だんだん怖くなってきましたね…
##そこでfutureパターン
future(promise)パターン使いましょう
https://www.hyuki.com/dp/dpinfo.html#Future
レガシーになっちゃいましたが、unity社のAssetBundleManager参考になります
https://bitbucket.org/Unity-Technologies/assetbundledemo
callbackと違い、globalなAssetBundleManagerは自分が作ったfuture objectしか知りません
そのため余計な参照がなくメモリリークしません、当然前シーンのコールバックが呼ばれるなんてこともありません
coroutineの1フレーム待ちもないのでパフォーマンスは最高です(コールバックが最速はいったん置いといて
pure C#クラスなのでブレークポイントはれば処理を止めることができます、いいことしかありません
そのそもこのパターン、unityのwebrequestやassetbundle.loadAsyncをはじめ、ほとんどの関数で使われてるんですよね(むしろコールバックの方が少ない
unity社の信頼と実績のfutureパターン
ぜひ自分のプロジェクトに導入して堅牢なクラス設計をして、デバッグ期間中であっても心穏やかに過ごしましょう
##明日は
Unity Advent Calendar 2018,明日の記事は @Takaaki_Ichijo さんの記事です.
##雑談
#####async await
期待してます
運営中プロジェクトなので導入できなくて悲しいです
#####unity2018.3がきたあ
クラス設計しまくって飽きてきたのでECS jobSystem burstとかで高速化とか、compute shader + PositionBasedDynamicsで軟体シミュとか流体シミュとかやる仕事ないですか?