Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

若輩エンジニアから見たUniRxを利用したゲーム開発

61,001 views

Published on

UniRx勉強会
「若輩エンジニアから見たUniRxを利用したゲーム開発」

UniRx勉強会 #UniRx
https://unirx.doorkeeper.jp/events/25218

Published in: Technology
  • Be the first to comment

若輩エンジニアから見たUniRxを利用したゲーム開発

  1. 1. 2015-06-19 若輩エンジニアから見た UniRxを利用したゲーム開発
  2. 2. ・森永 博仁(もりなが ひろひと) ・29歳 ・鹿児島県出身 ・クライアントエンジニア ・趣味は「お絵かき」 3度の飯より狐っ娘好き!! 自己紹介
  3. 3. ゲーム業界経歴「1年半」 駆け出しの「若輩エンジニア」です! よろしくお願いします! 自己紹介
  4. 4. ・UniRxをどのように活用したか ・開発している中で発生したアクシデント、苦労したこと アジェンダ
  5. 5. Photonのサービス(PhotonServer)を利用している関係上 ・Photon Unity Networking(PUN) という、アセットを使用しております ※以降では Photon Unity NetworkingをPUNと略させていただきます 説明の前に
  6. 6. UniRxに出会ったのは年始 携わっている開発においてUniRxを導入することになり 使い始めた
  7. 7. UniRxをどのように活用したか
  8. 8. UniRxをどのように活用したか UniRxにどのような便利な機能があるのかを調べました
  9. 9. UniRxをどのように活用したか まず目を付けたのが Buffer という機能
  10. 10. UniRxをどのように活用したか Bufferとは メッセージを蓄えて特定のタイミングで流すことができる 特定の条件: ・n個分のメッセージを蓄えたら流す ・別のストリームにメッセージが流れてきたら流す など
  11. 11. UniRxをどのように活用したか n個溜まったら流す ↓ ↓ n個づつに分けることができるということ?
  12. 12. UniRxをどのように活用したか ① ② ③ ④ ⑤ ⑥ Buffer ④ ⑤ ⑥ ① ② ③
  13. 13. UniRxをどのように活用したか 要素を分ける・・・ 要素を分ける必要のある処理ってあったかな?
  14. 14. UniRxをどのように活用したか 全プレイヤーの対戦情報が配列でまとめて送られ その情報を利用して表示するような処理
  15. 15. UniRxをどのように活用したか Game Server クライアント 端末 配列 イメージはこんな感じ! プレイヤーAの対戦情報 プレイヤーBの対戦情報 : : [0]:情報① [1]:情報② [2]:情報③ [3]:情報④ [4]:情報⑤ [5]:情報① [6]:情報② [7]:情報③ [8]:情報④ [9]:情報⑤ 情報をもとに 点数や撃破数など 表示する
  16. 16. UniRxをどのように活用したか この処理をUniRxで書いてみよう!
  17. 17. UniRxをどのように活用したか OnResponseBattlePointAsObservable() .Subscribe(playersData => { playersData.ToObservable() .Buffer(5) .Subscribe(data => { // 表示のための準備 }); }, () => { // 表示処理 }); 全プレイヤー情報がまとめて配列として送られてくる OnResponseBattlePoint()というイベントをTriggers化したもの (Triggersに関しては、後ほど説明します)
  18. 18. UniRxをどのように活用したか playersData.ToObservable() .Buffer(5) .Subscribe(data => { // 表示のための準備 }); playersDataをBufferで5個の要素に分割し、 分割されたデータは、Subscribのdataで受け取る 「playersData」は、全てのプレイヤーデータを受け取っている
  19. 19. UniRxをどのように活用したか OnResponseBattlePointAsObservable() .Subscribe(playersData => { playersData.ToObservable() .Buffer(5) .Subscribe(data => { // 表示のための準備 }); }, () => { // 表示処理 }); プレイヤーごとに 情報を分けて 表示のための 準備を行う 表示する
  20. 20. UniRxをどのように活用したか Bufferはメッセージを溜めて 特定のタイミングに分けるということができるのか!
  21. 21. UniRxをどのように活用したか OnResponseBattlePointAsObservable() .Where(data => mode == 1) .Subscribe(x => { x.ToObservable() .Buffer(5) .Subscribe(data => { // 表示のための準備 }); }, () => { // 表示処理 }); Whereで処理の動作条件をつけることで、 ここぞという時のみ処理を行うことができる
  22. 22. UniRxをどのように活用したか UniRxマジ便利!
  23. 23. UniRxをどのように活用したか 他にも便利な機能がありそうだ! もっと他にはないのか?
  24. 24. UniRxをどのように活用したか 次に目を付けたのが ReactiveProperty という機能
  25. 25. UniRxをどのように活用したか ReactivePropertyとは イベントと値がセットになったもの 値が変更になった時などに、イベントを取れたりできる
  26. 26. UniRxをどのように活用したか ふ〜ん 値の変化時などに通知ができるのか~
  27. 27. UniRxをどのように活用したか ReactiveProperty<型> 表示・状態変更 値の変化に応じて表示更新を行うような処理に 使えるんじゃないか!? 値の変更 通知
  28. 28. UniRxをどのように活用したか そうそう例えば「HPバー」とか! そうそう、これ!
  29. 29. UniRxをどのように活用したか ReactivePropertyを使って プレイヤーHPバーの処理を作ってみた
  30. 30. UniRxをどのように活用したか // 宣言 public ReactiveProperty<int> hp { get; private set; } // 初期化 hp = new ReactiveProperty<int>(初期HP); // 値の変更 hp.Value = 変更値; プレイヤー側(HP値の格納先)を ※HPはプレイヤーオブジェクトの外部から読み取れるようにpublicにしています
  31. 31. UniRxをどのように活用したか player.hp.AsObservable() .Subscribe(hp => { // HPバーの表示処理 }); HPバー側(変更通知を受け取る所)で ※HPバーのGameObjectにおいてStartまたはAwake時に設定しています
  32. 32. UniRxをどのように活用したか おお、HPを変更したときに 通知が来てHPバーの表示が変化するようになった これは他にも役立てられそうだ!
  33. 33. UniRxをどのように活用したか 例えば ・一定時間能力アップ時にエフェクトを出し続ける時の処理 ・プレイヤーの行動制限のオン/オフ 他にもフラグ管理されているような処理にもってこいだ!
  34. 34. UniRxをどのように活用したか さすがUniRxさん 便利さマジぱないっす!リスペクトっす!
  35. 35. UniRxをどのように活用したか あらかたUniRxについて学習した後 今回の開発においてPhotonを使うということもあり PUNとUniRxを組み合わせて便利に幸せになることを目指した!
  36. 36. UniRxをどのように活用したか UniRxを勉強している中で PUNとUniRxの組み合わせに関しての記事を 見つけていたからである
  37. 37. UniRxをどのように活用したか naichilab - Android iOSアプリ開発メモ 【Unity、UniRx、Photon】PUNをRx対応してみた http://naichilab.blogspot.jp/2014/11/unityunirxphotonpunrx.html naichilabさんがUniRxに対応させた PhotonRx.ObservableMonoBehaviourクラス を提供されていたのでそちらを参考にさせて頂きました
  38. 38. UniRxをどのように活用したか 序盤は・・・
  39. 39. UniRxをどのように活用したか UniRxがv4.8から MonoBehaviourのイベントハンドリング手法 ↓ ↓ ObservableTriggersという概念へ全面移行
  40. 40. UniRxをどのように活用したか ObservableTriggersへの移行理由として ・継承が必要(基底クラスが強制される) ・baseメソッドの呼び出しが必須 ・空イベントの呼び出しが必ず含まれるためパフォーマンス低下 など
  41. 41. UniRxをどのように活用したか 使いやすいようにしてくれたみたいだ
  42. 42. UniRxをどのように活用したか しかし ObservableMonoBehaviourを継承する方法から AddComponentをする形になったが 使用するたびAddComponentは面倒・・・
  43. 43. UniRxをどのように活用したか そこは抜かりがないUniRxさん! UniRx.Triggersをusingしている場合 ???AsObservableメソッドを直接拡張メソッドから呼べ Triggerが自動付与されるようにしているらしい
  44. 44. UniRxをどのように活用したか using UniRx; using UniRx.Triggers; public class Sample : MonoBehaviour { void Start() { this.OnMouseDownAsObservable() .Subscribe(x => { ・・・ }); UniRx.Triggersをusingすることで・・・ UniRx.Triggersに定義されている 「??? AsObservable()」 を直接拡張メソッドから呼べる
  45. 45. UniRxをどのように活用したか とても親切設計!
  46. 46. UniRxをどのように活用したか よし!この波に乗り遅れてはいけない! 我々も移行せねば! ということでTriggers化を行いました
  47. 47. UniRxをどのように活用したか PUNのLobbyに関わる通知をまとめたTriggerクラスを 例に説明します PhotonRx.ObservableMonoBehaviour から ↓ 使用用途に分割したPhoton用のTriggerクラスを作り それをまとめたPhotonRx.Triggersに移行しよう!
  48. 48. UniRxをどのように活用したか ObservableLobbyTriggerというクラス名にしました namespace PhotonRx.Triggersを付けることで、 PhotonRx.Triggersという名前でまとめている [DisallowMultipleComponent]で同じ GameObject上に重複させない
  49. 49. UniRxをどのように活用したか ObservableLobbyTriggerクラスに Subject型でOnJoinLobbyのコールバック監視役としてonJoinLobbyを宣言 監視対象のOnJoinLobbyコールバックには 引数がないためUnitを設定しています
  50. 50. UniRxをどのように活用したか OnJoinLobbyのコールバック関数を書き 処理には、通知がきた時に監視役であるonJoinLobbyのOnNextにより OnJoinLobbyAsObservableが呼ばれるようにした
  51. 51. UniRxをどのように活用したか Destroy時にOnCompletedを呼ばれるようにした
  52. 52. UniRxをどのように活用したか 使用する場合は ObservableLobbyTriggerを使用したいGameObjectにAddComponentし OnJoinLobbyAsObservableにWhereやSubscribeで処理条件や処理内容を設定
  53. 53. UniRxをどのように活用したか Photonの通知をTriggers化することができた
  54. 54. UniRxをどのように活用したか UniRxの機能も調べた Triggersという力も手に入れた
  55. 55. UniRxをどのように活用したか 今なら何にでも勝てるのでは! ( ↑調子に乗っています (>ω<)/ )
  56. 56. UniRxをどのように活用したか Photonを利用した通信においての切断復帰に UniRxを手に戦いを挑んだ
  57. 57. UniRxをどのように活用したか その前に簡単にPhoton側の接続に関して説明を
  58. 58. サーバー(Photon) UniRxをどのように活用したか Photonを利用するにあたり以下のような流れでゲームへ接続を行っています ①Photonに接続 ②接続できると自動的にLobbyに入室 ③ゲームRoomを作成または入室 ④ゲームを開始 ⑤ゲーム終了でLobbyへ戻る Lobby Room クライアント 端末 接続 ルーム作成または、入室 ゲーム終了
  59. 59. UniRxをどのように活用したか PUNにはRoomの入室管理 他プレイヤーの接続管理が行えるように いくつかのコールバックが準備されています
  60. 60. UniRxをどのように活用したか ・ (自分の)接続が切れた (切断した原因が返る) ・ (自分が)Roomに入室した ・ Roomにいる誰かが入室してきた (入室してきたプレイヤーの情報を返す) ・ 同じRoom内にいる誰かがRoomから退出した (退出したプレイヤーの情報を返す)
  61. 61. UniRxをどのように活用したか ゲームへの接続の流れと PUNに準備されているコールバックを踏まえて 通信が切断してしまった時の復帰処理を考える UniRxを駆使しすれば平和的に解決できるはず!
  62. 62. UniRxをどのように活用したか PUNの通知で必要なコールバック関数をTriggers化しました ・ Photonの接続に関する通知でまとめたTriggersクラス ・ Roomの入室・退出に関する通知でまとめたTriggersクラス ・ 他プレイヤーの接続(Room入退出)に関する通知でまとめたTriggersクラス 3つのTriggersクラスを作成
  63. 63. UniRxをどのように活用したか 接続確認 通信切断 再接続処理 接続ができなかった 時の画面へ 再接続の回数超過 Roomへ再入室処理 入室確認 Photonへの接続通知を 受ける 接続成功 ゲーム再開処理 入室成功 Roomの入室通知を 受ける Photonの切断通知を 受ける
  64. 64. UniRxをどのように活用したか Roomへ再入室処理 Photonへの接続通知を 受ける接続確認 接続成功 先ほどの流れを小分けにして要素ごとに考えることにした ・通知を受ける: 接続が成功したという通知 ・通知が来たら: Roomへ再入室処理を行う
  65. 65. UniRxをどのように活用したか Triggersの力を得た今 通知が来て、 判断し、 何かしらの処理を行う という流れはもう怖くない
  66. 66. UniRxをどのように活用したか ***ObservableTriggers.OnConnectedToPhotonAsObservable() .Subscribe(() => { // Roomへ再入室処理 }); ※OnConnectedToPhotonAsObservableはPhotonに接続が成功した時に 通知が来るTrigger 接続成功の通知を受けるためのTrigger 通知が来た時の処理をここに
  67. 67. UniRxをどのように活用したか 他の流れも同様に 用意した通知のTriggersを組み合わせ状況に見合った処理を行うことで 切断復帰の処理を実現することができた
  68. 68. UniRxをどのように活用したか 切断中に進行していた状態や表示の反映に関しても 各関連している値の格納先を「ReactiveProperty」など UniRxの機能を利用することで効率よく復帰処理を行うことができるはず ReactiveProperty<型> HPバーの表示 表示処理A 表示処理B 通知
  69. 69. UniRxをどのように活用したか 若輩エンジニアである自分でも TriggersやReactivePropertyなどUniRxの機能を組み合わせることで 復帰処理らしいことができた!
  70. 70. UniRxをどのように活用したか 考慮不足な部分はあったがそれなりの仕様実現を達成でき 処理の効率化とコードの複雑化を防ぐこともできた
  71. 71. UniRxをどのように活用したか UniRxには感心するばかりだ
  72. 72. しかし、全て問題なくというわけにも行かなかった・・・
  73. 73. 開発している中で 発生したアクシデント・苦労したこと
  74. 74. 突然のiOS端末だけクラッシュ! 開発している中で発生したアクシデント・苦労したこと
  75. 75. 開発している中で発生したアクシデント・苦労したこと エディタやAndroidでは問題なく動作するのに なぜかiOSだけが実行時にクラッシュして正常動作しない
  76. 76. 開発している中で発生したアクシデント・苦労したこと 端末に向かって謝りつつ とりあえずクラッシュログを拾って確認した
  77. 77. 開発している中で発生したアクシデント・苦労したこと クラッシュログを確認してみると・・・ Theard0にてUniRxのOnCompletedの処理直後に落ちている 「mono~trampoline」という処理がいくつかあるが“trampoline”って何だ!?
  78. 78. 開発している中で発生したアクシデント・苦労したこと 原因とtrampolineが分からず 河合さんに問い合わせと ネットで「Unity Trampolines エラー」を調べてみた その結果分かったこと
  79. 79. 開発している中で発生したアクシデント・苦労したこと trampolinesは type0 type1 type2 の3つある 再帰的なジェネリック関数を多く使うと trampolineを消費する
  80. 80. 開発している中で発生したアクシデント・苦労したこと UniRxでは再帰的なジェネリック関数を多く使用する Unityのデフォルトは極めて少ない数しか確保されていない (明確な数は分からないが128くらい?)
  81. 81. 開発している中で発生したアクシデント・苦労したこと 解決方法としては・・・
  82. 82. 開発している中で発生したアクシデント・苦労したこと PlayerSettings – iOS – OtherSettings - ATO Compilation Optionsに トランポリンサイズ変更コマンドを追加することで解決できた
  83. 83. 開発している中で発生したアクシデント・苦労したこと type0が足りない・・・ ntrampolines=2048 type1が足りない・・・ nrgctx-trampolines=2048 type2が足りない・・・ nimt-trampolines=1024 ※複数設定する場合は、カンマ区切りで設定 設定する値に関しては 開発されているプロジェクトごとに必要な値が違うと思いますが “2048”くらいあれば足りるのではとのことでした
  84. 84. 開発している中で発生したアクシデント・苦労したこと trampolinesを消費する要因として 再帰的ジェネリック関数内で さらに再帰的ジェネリック関数を書くような処理を組むと trampolinesを消費していくらしい
  85. 85. 開発している中で発生したアクシデント・苦労したこと (極端な例ですが、)こういう書き方 w.ToObservable() .Subscribe(x => { x.ToObservable() .Subscribe(y => { y.ToObservable() .Subscribe(z => { … });
  86. 86. まとめ
  87. 87. まとめ UniRxを利用することでさまざまな利点と注意点があった
  88. 88. まとめ ①MonoのイベントやPhotonのコールバックを UniRxを介すことでUniRxの機能を利用することができ 通知を受けた後の処理が書きやすくなる UniRxのv4.8以降を使用するのであればTriggers化は欠かせない
  89. 89. まとめ ②UniRxを使用することで発生したイベントに対しての処理や 値の変動を監視し対応した処理行うということが 安易に組むことができる
  90. 90. まとめ ③個人的感覚にも依りますが 条件と処理が見やすいコードを書くことができ 理解しやすいコードになることで修正が楽になり ヒューマンエラーを減らすことにもつながる ○○.AsObservable() .Where(x => mode == 1) .Subscribe(x => { // 処理; }); この時、この処理は動作するのか! この処理は x の値を使って○○を処理するんだね。
  91. 91. まとめ ④UniRxは再帰的ジェネリック関数を多く使う AOTのtrampolineサイズに関しては注意した方が良い! (実は2048でも足りなかったw) 再帰的ジェネリック関数の中に再帰的ジェネリック関数を 書くような処理を減らす努力をすると良い
  92. 92. 最後に 難解な命令(仕様実現)を達成するために UniRxを利用し救われました UniRxは戦場(エディタ上)で新兵(若輩エンジニア)が 活躍できる場を広げ救うための強力な武器(アセット)で あると感じました
  93. 93. 最後に UniRxを導入することをお勧めします!
  94. 94. ご清聴ありがとうございました

×