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.

ロボット好き集まれ!こいつ、動くぞ。星と翼のパラドクス開発事例

5,631 views

Published on

講演動画:https://www.youtube.com/watch?v=bKhH2NU99Gw

2018年10月14日に行われた「UNREAL FEST EAST 2018」における株式会社バイキング様の講演で使用されたスライドです。

●公式サイト
https://unrealengine.jp/unrealfest/
===
大型可能筐体を用いたハイスピードロボット対戦アクションゲーム「星と翼のパラドクス」の制作事例としてキャラクタープログラム、カメラ、筐体制御、ネットワーク周りについてお話したいと思います。本作品で行っている8vs8の計16体のプレイヤーと多数のモブキャラや弾をマルチプレイで実現したノウハウについてもご紹介させていただきます。

Published in: Engineering
  • Be the first to comment

ロボット好き集まれ!こいつ、動くぞ。星と翼のパラドクス開発事例

  1. 1. ロボット好き集まれ! こいつ、動くぞ 星と翼のパラドクス開発事例 株式会社バイキング 橘川 優樹 奥井 健
  2. 2. 登壇者紹介
  3. 3. 登壇者紹介 橘川 優樹 プログラマー 「星と翼のパラドクス」ではキャラクターアクション、 演出用カメラ、操作周り等を担当。 奥井 健 癒し系プログラマー ゲーム業界歴13年 「星と翼のパラドクス」ではカメラや椅子の制御を担当。 趣味はボードゲーム
  4. 4. バイキングってどんな会社?
  5. 5. • アーケード(業務用)ゲームをよく作ってます。 • 多人数対戦アクションが得意です。 バイキングってどんな会社? © 2012 SQUARE ENIX CO., LTD. All Rights Reserved.
  6. 6. • 「星と翼のパラドクス」のご紹介 • コックピット型可動筐体 • UE4を用いたキャラクター制作 • カメラについて • 武器・弾の作り方 本日のお話
  7. 7. 「星と翼のパラドクス」ご紹介
  8. 8. 「星と翼のパラドクス」ご紹介
  9. 9. 作品のコンセプト  ハイスピードロボット対戦 アクション  2レバー、2ペダルの 専用筐体でロボットを自在に操作  1チーム8人、計16人のプレイヤーによる オンライン対戦 多数の雑魚AIが試合に参戦
  10. 10. ・開発期間 約30ヶ月(試作込) プロジェクト概要 ・UE4のバージョン : 4.9.1 ~ 4.16.3
  11. 11. コックピット型可動筐体
  12. 12. 筐体スペック この中に2本の アクチュエーター タッチパネル フットペダル 耳元にスピーカー ゲームと連動して 色が変わるLED ロマンがてんこ盛り! 操作レバー 帰還ボタン
  13. 13. タッチパネルの機能 • 出撃前にキャラクターと ハイタッチ! • 武器を選択したり • マップで味方に指示を 出したり
  14. 14. 動いている筐体の動画
  15. 15. • 椅子の調整時は一日中こんな感じで 作業しています • 筐体からリモートデスクトップで 作業用PCにアクセス • プログラムをビルドしDLLのみを 筐体に転送して実行 筐体でのプログラミング
  16. 16. • 左右はカメラのZロールに連動 • 前後はキャラの高度変化に連動(浮遊感を演出) • いろんなアクションで1ショットの揺れにカーブを使う • ダメージなどによる振動 それっぽい揺れに仕上げる のにとても苦労しました 揺れの制御
  17. 17. 前に進むときは前に 右に進むときは右に 傾ける 超高速移動のとき だけ逆
  18. 18. UE4を用いたキャラクター制作
  19. 19. 本作の開発ではブループリントでは極力処理を書かずC++で作成 UE4を用いたキャラクター制作 C++を使用している理由の詳細はUnreal Fest2017で講演した 「バイキング流UE4活用術 ~BPとお別れするまでの18ヶ月~」を参照してください。 YouTubeに動画があります。 処理速度が速い デバッグしやすい マージしやすい BPで作成してから後でC++へ移行するのはコストがかかる etc …
  20. 20. • UE4には組み込みでキャラクタークラスが用意されている • キャラクタークラスにはMovementComponentという人型キャラクター を動かすためのコンポーネントが組み込まれている • 標準機能だけで足りるのであればそのまま使えば良い • 足りないのであればカスタマイズするか完全に自作する必要がある UE4を用いたキャラクター制作
  21. 21. UE4のキャラクターアーキテクチャ ACharacter APawn AActor ・人型のキャラクターを作成するための機能が集約されている (スケルタルメッシュ、モーション、コリジョン等) ・キャラクターの移動を制御する「UCharacterMovementComponent」を持っている ・UE4のレベルに配置できる基本クラス ・プレイヤーのコントローラーやAIによって制御可能
  22. 22. UE4のキャラクターアーキテクチャ UCharacterMovementComponent UPawnMovementComponent UActorComponent 省略 ・他のアクターに持たせることができるコンポーネントの基本クラス ・APawnの動きを制御するコンポーネント ・ACharacterの動きを制御するコンポーネント ・歩きだけでなくダッシュやジャンプ等、人型キャラクターを想定した機能が実装され ている
  23. 23. UE4におけるキャラクター制作の選択肢 ACharacter APawn AMyCharacter APawn AMyCharacter 1.ACharacterを継承してタイトル用の キャラクタークラスを作成 2.APawnを継承してACharacterに相当する クラスを独自で作成 <メリット> UE4のキャラクターシステムの恩恵 を受けられる <デメリット> カスタマイズしようと思ったとき に元のシステムを熟知していない と問題が起きやすい <メリット> 自由度が高い <デメリット> 実装コストが高い ※ACharacterで用意されている物を自前 で実装する必要がある ※UCharacterMovementに相当する物も自 前で実装する必要がある
  24. 24. • ハイスピードロボットなので、ダッシュをした時は初速が速くそこから 少し減速した状態でダッシュを継続したい • ダッシュ中、曲がるときや逆方向に移動する時に速度を落としたくない →例えば逆方向に移動する場合、速度を落とさず弧を描くように移動 本作が目指すキャラクター挙動
  25. 25. 本作が目指す挙動 ダッシュ中に右へ移動入力を行い、左へ移動入力を行うと速度を落とさず弧を描いて旋回 前から後ろへ移動入力を行った場合も同様
  26. 26. • 現状のUE4のキャラクターの速度変化は基本的に加速 • 入力方向を元に加速度をVelocityへ加算していく • 設定された最大速度でClampする UE4標準機能によるキャラクター挙動
  27. 27. • 基本的に加速しかない 減速は移動入力を止めた時や障害物へ当たったときしかできない • 移動入力を入れたままの減速ができない ダッシュの初速が速く、その後に減速するような動きができない • ダッシュ中、90度を超える方向へ移動すると速度が落ちてしまう 本作の目指す挙動とは異なる カスタマイズするか完全に自作する必要がある UE4標準機能によるキャラクター挙動 ※90度以内でも見た目では分かりづらいが速度が落ちる
  28. 28. UE4標準の挙動 右へ移動入力を行い、左へ移動入力を行うと速度を落としてから再加速する
  29. 29. 本作の開発開始時、プロジェクト内にはUE4経験者がいなかった そのため、キャラクターを完全に自作するのではなく UE4組み込みのキャラクター機能を利用しつつカスタマイズする方針に ACharacter AMyCharacter UCharacterMovementComponet
  30. 30. MovementComponentの差し替え ACharacter AMyCharacter UCharacterMovementComponet UCharacterMovementComponet UMyCharacterMovementComponet UCharacterMovementComponentを継承して カスタム用ムーブメントクラスを作成 ムーブメントコンポーネントはACharacterクラスが保持 しているコンポーネントなので、それをカスタマイズし たコンポーネントで差し替えたい
  31. 31. MovementComponentの差し替え AMyCharacter::AMyCharacter(const FObjectInitializer& ObjectInitializer) : • Super(ObjectInitializer.SetDefaultSubobjectClass<UMyCharacterMovementComponent>(ACharacter::CharacterMovementComponentName)) • キャラクタークラスのコンストラクタにFObjectInitializerを 受け取るようにする • FObjectInitializerのSetDefaultSubobjectClassで カスタマイズしたムーブメントクラスを指定
  32. 32. • 移動処理(座標更新)はムーブメントに集約する • ムーブメント内のメンバ変数はpublicになっている物があるが 他のクラスからは更新しない 例えばVelocityがpublicになっているが、 ムーブメント以外から更新すると キャラクターが想定外の挙動をした場合の原因特定に時間がかかる キャラクター挙動のカスタマイズ方針 実際、開発中にVelocityがムーブメント以外の様々な箇所で変更するコードがかかれていた。 プロジェクトの規模がある程度の大きくなった段階で問題が顕著に。
  33. 33. ムーブメント内の主な修正はCalcVelocity関数に入れた キャラクター挙動のカスタマイズ
  34. 34. • CalcVelocityは摩擦の影響を適用して速度と加速度を更新する関数 キャラクター挙動のカスタマイズ UCharacterMovementComponent::CalcVelocity void UCharacterMovementComponent::CalcVelocity(float DeltaTime, float Friction, bool bFluid, float BrakingDeceleration) { // Apply acceleration const float NewMaxSpeed = (IsExceedingMaxSpeed(MaxSpeed)) ? Velocity.Size() : MaxSpeed; Velocity += Acceleration * DeltaTime; Velocity += RequestedAcceleration * DeltaTime; Velocity = Velocity.GetClampedToMaxSize(NewMaxSpeed); if (bUseRVOAvoidance) { CalcAvoidanceVelocity(DeltaTime); } } ※省略 毎フレーム、Velocityへ加速度を加算し て最大速度でClampする関数です
  35. 35. • この関数をオーバーライドして独自の挙動に書き換える • 本作では親クラスのCalcVelocityの中身をそのまま子クラスの CalcVelocityへコピーして書き換えるという手段を取った キャラクター挙動のカスタマイズ UCharacterMovementComponent::CalcVelocity UMyCharacterMovementComponent::CalcVelocity オーバーライド ※エンジン更新でCalcVelocityに修正が入った場合、注意が必要
  36. 36. ダッシュ中、曲がる時や逆方向へ行く場合に 速度を落とさず弧を描いて曲がるようにする いくつか旋回のタイプを用意 キャラクター挙動のカスタマイズ ・ワールドのZ軸を基準に旋回 → 地上ダッシュ時 ・プレイヤーカメラ基準に旋回 → 空中ダッシュ時 ・弧を描かずに旋回 → 地上歩き時 アクションによって旋回タイプを切り替え
  37. 37. <地上ダッシュ時> ワールドのZ軸を基準に旋回
  38. 38. <空中ダッシュ時> プレイヤーカメラ基準に旋回
  39. 39. <地上歩き時> 弧を描かずに旋回
  40. 40. キャラクター挙動のカスタマイズ switch (TurnVelocityType) { case ETurnVelocityType::MoveCameraDirection: UpdateMoveCameraDirection(DeltaTime, RequestedAcceleration); break; case ETurnVelocityType::TurnWorldAxisZ: UpdateTurnWorldAxisZVelocity(DeltaTime); break; default: { const float NewMaxSpeed = (IsExceedingMaxSpeed(MaxSpeed)) ? Velocity.Size() : MaxSpeed; Velocity += Acceleration * DeltaTime; Velocity += RequestedAcceleration * DeltaTime; Velocity = Velocity.GetClampedToMaxSize(NewMaxSpeed); } break; } UE4標準の処理 旋回タイプに応じて Velocityを加工
  41. 41. 多彩なアクションを実装
  42. 42. • UE4標準のキャラクターシステムは機能が豊富 すぐにキャラクターを動かせるので開発期間が短縮できる • カスタマイズが容易なので、本作のような 人間の動きと異なる物も開発可能 キャラクター制作のまとめ
  43. 43. 非常に便利ではありましたが キャラクター制作のまとめ
  44. 44. ディレクターの細かい要求に対応するのが難しかった キャラクター制作のまとめ もっとロボットっぽい動きにしてよ! 僕の考える最高のロボットの動きに!! 石田ディレクター 元々の機能が豊富がゆえ、本作には不要な処理も多かった。 アクションの細かい動きに拘ると、元々の処理が障壁になることも。
  45. 45. • それでも初めてのUE4開発において UE4のキャラクターシステムを利用したのは正解だったと 思います。 • 再びUE4でアクションゲームを作る機会があれば より自由な実装を目指してACharacterと UCharacterMovementに相当する物を独自に実装しようと 思います。 キャラクター制作のまとめ
  46. 46. カメラについて
  47. 47. • アクションゲームでは至るところでカメラ振動を行います 攻撃を受けたときや死んだとき 着地したとき 強力なレーザービームを撃ったとき • そんなカメラ振動についてのお話です カメラ振動
  48. 48. • UE4には組み込みで「CameraShake」ブループリントというカメラ振 動用の機能が用意されている • 本作では、よりゲームに合わせた自由な振動を行いたかったため振動の 機能を自作することにした • UE4では「UCameraModifier」というクラスを拡張することで、最終的 なカメラの計算結果を調整する機能を自作することができる カメラ振動
  49. 49. • UCameraModifierのModifyCameraをオーバーライド • 引数のInOutPOV(LocationとRotation)の値を設定することで カメラの最終的な結果を調整することができる カメラ振動 bool UCPP_CameraShaker::ModifyCamera(float DeltaTime, struct FMinimalViewInfo& InOutPOV) { Super::ModifyCamera( DeltaTime, InOutPOV ); InOutPOV.Location += InOutPOV.Rotation.RotateVector(Offset); InOutPOV.Rotation += OffsetRot; return false; } ※省略
  50. 50. • 振動データの設定はUE4の「VectorCurve」を利用 カメラ振動
  51. 51. 何故、FPS視点ではなくTPS視点? コクピット型の筐体に乗って遊ぶロボットゲームならFPS視点にすべきでしょ? 通常カメラ
  52. 52. • 開発当初、FPS視点による検証を行ったが ・「高速で飛び回るロボットアクション」 ・「大型ディスプレイ」 ・「可動筐体」 という酔いやすい要素にFPS視点を加えると更に酔いを誘発する 通常カメラ
  53. 53. ロボットの速度やカメラ速度を調整することで酔い対策は可能だが 通常カメラ • ロボットとカメラの速度を落とす • ロボットとカメラの速度変化を等速にする • カメラを上下に回転させないようにする(左右回転のみ) etc …
  54. 54. これはロボットシミュレーターではなく 「ハイスピードロボットアクションである」 FPS視点にすることでロボット操作の臨場感を出すことも重要 しかし、FPS視点を重要視することによって 本来実現したいアクションスピードに影響が出てはいけない 通常カメラ
  55. 55. 理由は色々ありましたが 通常カメラ
  56. 56. 結局はディレクターの趣味です 通常カメラ 僕、3D酔いしやすいから。 僕が酔わないゲームにしないとダメ。 TPS視点でいこう。 石田ディレクター 自分の機体を見せたいという理由もあった(自機の見た目をカスタマイズする要素もあるため) 「ハイスピードロボットアクション」というコンセプトは守りつつ 「多くの人に遊んでもらいたい」という事を選びました。
  57. 57. ゲームのスピードは落とさずに 酔いやすい人でも遊べるゲームに仕上がりました 椅子の振動も設定で止められるので酔いやすい人でも安心して遊べます!
  58. 58. 武器・弾の作り方
  59. 59. 対戦アクションゲームにおいて ゲームを面白くできるかどうかは 武器の魅力にかかっている。 そして、作るのがとても大変。 なぜ武器の話をしようと思ったか
  60. 60. • AttackParam(エクセル) • ProjectileParam(エクセル) • WeaponParam(エクセル) エクセル→Csv→DataTable データ構造
  61. 61. • 属性(ビーム系、実弾系など) • ダメージ量 • やられタイプ • ダウン値削り量 • ぶっ飛び量 • ヒットストップ時間 などパラメータ34個! AttackParam
  62. 62. • 攻撃ID • 初速 • 最高速 • 重力 • ホーミング性能 などパラメータ90個! ProjectileParam
  63. 63. • 発射する弾ID • ロックオン距離 • アニメーションタイプ • 連射間隔 • リロード時間 などパラメータ120個! WeaponParam
  64. 64. パラメータ多すぎない? 石田ディレクター
  65. 65. すいません。 多すぎました。
  66. 66. • 安易にDataTableに定義してはいけない! • DataTableをロードすると全武器のアセットがロードされてしまう • AssetIDを使えば参照を切れるが、 1つずつ対応しているとロードが面倒 • 武器単位でBlueprintを作ってアセットをそこに設定 エフェクトやモデルデータの設定は?
  67. 67. 武器Blueprint 武器モデル、マズルエフェクト、薬莢、SEなど
  68. 68. • こちらは弾BP 同じくエフェクトなどの アセットデータを設定 • それ以外にプランナーに公開したくないパラメ ータなどが入っている • プランナー:エクセル • デザイナー&プログラマ:Blueprint • パラメータ例: 弾の消え方、拠点バリア貫通設定など 弾も種類分Blueprintがある
  69. 69. 処理はCPPに書いてBPはパラメータを設定するだけ BPもCPPも大量にある・・・ 弾クラスの継承図 ACPP_ProjectileBase ACPP_PrjStandardBase ACPP_PrjExplosionBase ACPP_PrjMachineGunBullet01 ACPP_PrjFireBombCoop01 … …… BP_PrjMachineGunBullet01 (マシンガン) BP_ PrjFireBombCoop01 (火炎爆弾)
  70. 70. • エクセルにBPのパスを指定して文字列でBPアセット を取得する • 文字列で取るのはそれなりに遅いのでキャッシュして おくのがおすすめ BPはパス(文字列)で参照
  71. 71. • どうしても生まれてしまうバランスブレイカーに対してすば やく対処する必要がある • 通常のアップデートだと更新の反映に時間がかかってしまう リリース後の武器調整はとても大事 そこで DataTableの動的書き換え
  72. 72. 1. ファイルサーバから該当csvファイルをダウンロード 2. (1)をメモリへ読み込む 3. FTableRowBaseを継承した構造体からUScriptStructを取得 UScriptStruct* ScriptStruct = FTableRowBase::StaticStruct(); 4. データテーブルを作成(自前クラス) DataTable* DataTable = UDynamicDataTable::Get().CreateDataTable(UObject* object, UScriptStruct* scriptStruct, const FString& dataTableName, const FString& csvString); 5. (3)の構造体テーブルへAddしていく DataTable書き換えの流れ
  73. 73. • DataTableはお手軽で使いやすいが、 実行中に書き換えることはできない • エンジンを修正してできるようにした • 具体的にはソースの「WITH_EDITOR」を外すだけ • DataTableCSV.cpp(.h) • DataTable.cpp(.h) DataTableのランタイム生成
  74. 74. • 自分の弾は味方や敵よりも見た目をちょっと大きくする • ヒットエフェクト • 遠くても小さくなりすぎないようにスケールをかける • 通信ラグで位置やタイミングがズレないように(後で説明) • 置いてくエフェクトと、キャラにくっつけるエフェクト2種類ある ヒット感、当てたときの気持ちよさ1 置いてく くっつける
  75. 75. • 敵を画面の中心にとらえて撃つとクリティカル表示 ヒット感、当てたときの気持ちよさ2 相手を中心にとらえて トリガーを引くと、 ホーミング力が強い 弾が発射され、 それが当たると 「CRITICAL」
  76. 76. • 弾を見て避けるという遊びをさせるためには 弾がゆっくり飛んでいかないといけない。 • しかも8vs8で雑魚AIが沢山。弾も大量に発生 • すべての弾の位置同期していたら通信帯域が足りない! マルチプレイで問題が
  77. 77. • マシンガン、ビームライフルなど大量に生成するものは 位置同期しないことにした • トリガーのOnとOffだけを送信し、サーバーとクライアントは それぞれローカルの敵の位置をめがけて飛ぶ • そうすることで起きる新たな問題。位置やタイミングのズレ 非同期弾
  78. 78. • ヒット判定はサーバーのみで行うためタイミングや 位置がズレる • サーバーからの通知でヒットエフェクトを出す場合はヒット 位置をワールド座標ではなくキャラ相対座標で送る • 弾の消えるタイミングがズレるのでコリジョンを 少し後ろに付けている 通信ラグで位置やタイミングにズレ
  79. 79. • それでも気になる弾は同期弾にする • 爆発等フィールドに設置されるもの、サイズが大きいものは 非同期にすると座標のズレが目立つので同期弾にしている • 同期弾はサーバーで生成、クライアントにレプリケートされ るのでクライアントでは生成する部分をスキップする 同期弾
  80. 80. • 見た目だけで当てられた感を出すのは難しかった • UIで画面端に赤い枠を表示すると情報量が多すぎたり 閉塞感が出て良くなかったのでかなり薄くしている • SE大事 • ダメージを受けた方向から衝撃がくるように 椅子の振動でダメージを表現 攻撃を当てられた感
  81. 81. まとめ
  82. 82. • 調整しやすい環境を作る • 納得感を出すために通信を考慮した作りにする • バリエーションを沢山用意するための労力を惜しまない 魅力的な武器、弾を作るためのポイント
  83. 83. ご清聴ありがとうございました
  84. 84. 業務拡大につき全職種積極採用中! エントリーは弊社HPまで http://byking.jp/recruit/ 本日配布のチラシも見てね! スタッフ募集!!

×