Advertisement

Unityでオンラインゲーム作った話

torisoup
May. 29, 2019
Advertisement

More Related Content

Slideshows for you(20)

Similar to Unityでオンラインゲーム作った話(20)

Advertisement

Recently uploaded(20)

Advertisement

Unityでオンラインゲーム作った話

  1. オンラインゲームつくった話 @toRisouP
  2. 誰? • とりすーぷ ( @toRisouP ) – Webエンジニア(プログラマ) – Unity、C#、Scala、Ruby、Python、PHPとかその辺触ってる • 最近はScalaが一番お気に入り • いろいろやってる – 同人ゲーム制作 – VR開発 – Qiita書く – 技術書執筆
  3. この秋、 ゲームをリリースしました
  4. ハクレイフリーマーケット • Windows向けの東方2次創作ゲーム • 最大6人までネットワーク対戦可能なパーティアクションゲーム • ロビー機能あり、CPUあり
  5. ウリ • 物理演算をメインに使ったパーティゲーム – とりあえず物理演算をガバらせておけば面白くなるという発想 • ガバガバ物理演算は偉大 – 大量の物理演算オブジェクトを同期しながら通信対戦できる • 今思うと狂気の沙汰
  6. 使ってるもの • 使っているフレームワークとか – Unity 2017.2 – Photon Cloud – NCMB – ADX2 LE – Effekseer • 使ってるライブラリ – UniRx – Zenject – PhotonRx
  7. 本題
  8. オンラインゲームつくるの すごくツライ
  9. PhotonCloudの仕様 • クライアントで状態を管理する – サーバサイドに状態はもたせられない – サーバサイドにロジックはおけない – 誰か1人がマスタークライアントになる – クライアントサイドで全ての問題を解決しないといけない!
  10. 発生した問題 • 動きがカクカクする • 通信の待ち時間でテンポが悪くなる • 帯域、メッセージ数がオーバーする
  11. 発生した問題 • 動きがカクカクする • 通信の待ち時間でテンポが悪くなる • 帯域、メッセージ数がオーバーする
  12. オブジェクトの管理ルール • オブジェクトには親の概念がある – 同期オブジェクトにはPhotonViewコンポーネントがついている – PhotonView.IsMine = true だと自分が管理(送信モード) – PhotonView. IsMine = false だと通信相手が管理(受信モード) 親 (IsMine=true) 子 (IsMine=false) 子 (IsMine=false) 子 (IsMine=false)
  13. 図解 • 自分の世界 – IsMine = true • 相手の世界 – IsMine = false 同期されている2つのオブジェクトがある
  14. 図解 • 自分の世界 • 相手の世界 自分のオブジェクトが移動すると… アニメーション
  15. 図解 • 自分の世界 • 相手の世界 移動後の座標を送信して 相手側の座標を書き換える
  16. 図解 • 自分の世界 • 相手の世界 相手の世界の座標を上書きして同期
  17. 図解 • 自分の世界 • 相手の世界 自分の世界ではキレイにアニメーションして移動したが、 相手の世界には結果しか送ってないので突然ワープしたように見える → 動きがカクカクする
  18. カクカクする問題 • 同期メッセージの送信頻度が低いのが原因 – 根本対策は同期頻度を上げるしかないが、 帯域やメッセージ数の制限で上げるにも限界がある →別アプローチで解決する必要がある
  19. 解決策いろいろ • 同期頻度を単純に増やす方法 – これができたら苦労しない • キー入力を送ってしまう方法 – 入力から一意に結果が決まるゲームシステムならかなり有効 • がんばってごまかす方法 – 補間・補外・エフェクト・アニメーションでごまかす • そもそも同期しない方法 – ローカルで演算可能ならそっちを優先して使う
  20. 解決策いろいろ • 同期頻度を単純に増やす方法 – これができたら苦労しない • キー入力を送ってしまう方法 – 入力から一意に結果が決まるゲームシステムならかなり有効 • がんばってごまかす方法 – 補間・補外・エフェクト・アニメーションでごまかす • そもそも同期しない方法 – ローカルで演算可能ならそっちを優先して使う 採用した方法 (というかこれしかできない)
  21. がんばってごまかす • 補間処理でなんとかんする – Vector3.Lerp と Quaternion.Lerp を使う • 補間の割合を調整して違和感が少ないパラメータを見つける – 補間を強くする → 滑らかだが動きが鈍くなる – 補間を弱くする → 機敏だがカクカクが目立つ
  22. そもそも同期しない 相手の演算結果を待っているからラグが生じる ↓ 自分が演算して結果を送る側になれば ラグは起きない
  23. プレイヤの動向を観察してわかったこと • 「プレイヤは自キャラの周辺しかみてない」 – 正確には「自分の行動に対する周辺のリアクション」しか見てない • 自分が触ったオブジェクト • 自分が持ち上げたオブジェクト • 自分が投げたオブジェクトの挙動 – 少なくともここさえ滑らかに動いていれば、 他がカクカクしててもあまり気にならない
  24. やったこと • 自キャラが触れたオブジェクトの親権を奪う – 座標を受け取る側から送る側にしてしまう – 自キャラが触れた瞬間にIsMineをtrueに書き換えてしまう • (実際はIsMineに似た別のフラグ用意してそっちを書き換えているけどね)
  25. 効果 • かなり効いた – 少なくとも自分の周辺だけはラグがなく動いてくれる • 実装は複雑化してしまった – メンテナンス困難な実装になっている – 親権のスイッチングコストも結構かかる
  26. 発生した問題 • 動きがカクカクする • 通信の待ち時間でテンポが悪くなる • 帯域、メッセージ数がオーバーする
  27. テンポが悪くなる • 入力に対してプレイヤが機敏に反応してくれない – 原因は行動時に「通信」が発生するから • 特にこのゲームだと「アイテムを拾う」時に発生する – アイテムを拾えるかどうか?の判定に通信が発生するため
  28. 解決策 • 通信時間をアニメーションでごまかす – アイテムを拾う時に、 「構える」→「持ち上げる」の2段階のアニメーションを再生する – 構えモーションの間に通信を終わらせてしまう
  29. アニメーション 構えモーション 構え→持ち上げ 構え→持ち上げ (構えが長めのキャラ) 構えモーション再生中に裏で通信しちゃうことで、 通信によるテンポの悪化をごまかす
  30. 発生した問題 • 動きがカクカクする • 通信の待ち時間でテンポが悪くなる • 帯域、メッセージ数がオーバーする
  31. 帯域とメッセージ数 • 帯域 – 秒間に通信するデータ量 – 小さければ小さいほうが当然良い – PhotonCloud的には通信したデータの総量の方が大事 • メッセージ数 – 座標等の同期、RPCの実行命令などの送受信回数
  32. 制約 • 通信量 – 1ユーザあたり3GB/月まで – オーバーすると追加料金 • メッセージ数 – 1部屋につき500メッセージ/秒 – オーバーしても今のところはペナルティなし
  33. メッセージ数の制約がヤバイ • 部屋に参加している人数倍して計算される – 6人部屋で1メッセージ送信すると、 送信1+受信5=6メッセージ消費してしまう – 6人部屋だと約83メッセージ/秒しか送れない • 60FPSだと1フレームあたり1~2メッセージしか送れない
  34. このゲーム同期対象が多すぎる • フィールド上のアイテムを同期すると一瞬で上限超える – こんな仕様のゲームでネットワーク対戦とかバカじゃないの…
  35. 対策をうつ必要がある
  36. 対策前の状態 • データ通信量 – 1試合あたりの総量6MB/人 • メッセージ数 – 2000メッセージ/s/room – 規定値の4倍もいってた
  37. 取った対策一覧 • 次の位置が確実に予測可能なものは同期しない • 動いていないオブジェクトは同期しない • オンライン対戦時にはゲームのフレームレートを30に落とす • オブジェクトをシャーディングしてグループ単位でまとめて同期する
  38. 取った対策一覧 • 次の位置が確実に予測可能なものは同期しない • 動いていないオブジェクトは同期しない • オンライン対戦時にはゲームのフレームレートを30に落とす • オブジェクトをシャーディングしてグループ単位でまとめて同期する
  39. フレームレートを落とす • オンライン時のみ30FPSに落とす – メッセージ送信回数を60FPS時の半分以下にできる – 補間処理でごまかしやすくなる – 低スペックPCと挙動を揃えることが出来る – 割とメリットが大きかったので採用
  40. オブジェクトのグループ化 • オブジェクトの座標同期をグループ単位で行う – ようするにシャーディングして管理する
  41. グループ単位で分割するメリット • バラバラに送るよりメッセージ数を削減できる – 1メッセージ内に複数の座標データをまとめて送信できる • 挙動のチューニングができる – 「グループにわける数」 と 「何フレームごとに送信するか」の 組み合わせでカクカク度合いとメッセージ数のバランスを調整できる • 一斉に同期する時の違和感が消せる – 全オブジェクトが同じタイミングで動くと違和感が出る – バラバラに動いている感を出してごまかせる
  42. 後はログを見ながら調整 • 違和感と帯域・メッセージ数のバランスを見て模索
  43. チューニング結果 • 帯域 – 対策前 1試合あたり6MB → 1試合あたり2MB 60FPS,グループ化なし,毎フレーム送信 30FPS,3グループ分割,2フレームごとに送信
  44. チューニング結果 • メッセージ数 – 1000~1900/s → 450~600/s
  45. なんとか規定値には収まった • まだところどころオーバーするけど… – 怒られたら直す 500メッセージ
  46. まとめ
  47. まとめ • オンラインゲーム制作はとにかく手間がかかる – 工数がオフラインゲーム開発の5~10倍になる(体感) – 技術的に実装困難なため諦めた仕様もいくつかある – 不具合発生時の原因調査にエスパー能力が必要 • まじめに作るとつらいところはできるだけ誤魔化す – プレイヤが不快に感じさえしなければよい – ガバ物理はむしろラグった方が面白いから助かった
Advertisement