とある相撲ゲーム開発の思い出

こんにちは。技術部平山です。

去年の末、「本格相撲ゲーム(仮)」なるtwitterアカウント が、相撲のようなもののゲーム動画をアップロードしておりましたが、 アレの開発に関わっていたので、そのあたりのお話をしようかと思います。

また、これが後々別の製品の元になっておりまして、そのあたりについても最後に触れます。

なお、ソースコード及びビルドをgithubにて 公開しておりますので、よろしければどうぞ。 ビルドはリリースページにあります。 残念ながら力士モデルやアニメーション、効果音等は購入したもので 再配布できませんので、ソースコードからは削除してあります。 音は鳴らないだけですが、人体モデルがない状態では動きませんので、 代わりにユニティちゃんを入れておきました。

企画の経緯

元々この企画は、社内イベント向けのものです。 「プレゼン対決で勝った企画は72時間の業務時間を使って開発できる」というイベントが定期的にあり、 そこにトモぞヴPが出した企画が通ったのです。

平山は昔あるリアル系スポーツの家庭用ゲームの開発に参加したことがあり、 その頃から「いつか相撲やりたいなあ」と思っていました。 Sumotori Dreams という物理シミュレーション相撲ゲームが流行ったこともあり、 「リアルなグラフィクスと、肉、塩、汗、砂、のシミュレーションで技術力を無駄遣いしたい!」 と思っていたのです。

それからずいぶん経った去年の秋、「リアルな相撲ゲーム作りたいなー」 という妄言をslackの自分のチャネルで吐いていたのを、トモぞヴPが拾って企画にしてしまったわけです。 その意味では平山が出所と言えなくもないのですが、 平山が想像していたのは億単位の開発費がかかる「フツーの家庭用ゲーム」でしたので、 まさか70時間そこそこで作る羽目になるとは思ってもみませんでした。

設計

さて企画には「リアル相撲ゲーム」と書いてあります。 とはいえ、元々私が思っていたようなリアルは到底実現できませんし、 何しろ社内イベントなのですからウケてナンボです。 72時間でインパクトのあるものとして仕上げる必要があります。 そこで、まず「リアル」の定義をし直すことが必要でした。

グラフィクス

まず「見た目」の面については、モデルとテクスチャがリアル寄りで、 レンダリング設計がフォトリアルであれば、まあリアルでしょう。 その意味では日常使っているURP(Universal Render Pipeline)も 設計はフォトリアルなので良いのですが、見慣れていることもあってインパクトには欠けます。 そこで、使ったこともないHDRP(High Definition Render Pipeline)を使うことにしました。 PCでしか動かないという欠点は、スマホやswitchで出すわけではないので問題ではありません。

さて、次はモデリングやテクスチャの制作ですが、これは単純に不可能です。 アセットを見つけてきて入れ、ライティングのパラメータをいじって終わり、 という程度が限界です。 試してみた結果、何もしなくてもGI(Global Illumination)とモーションブラー、光の散乱などが入り、 かなりハッタリの効いた絵になりました。

ただ、力士のテクスチャがHDRP準拠でなく、また、解像度が低かったので、 アート担当がかなり直してくれています。

挙動

次に問題になるのが挙動、つまり動きです。 動きに関する「リアル」は大きく分けて2種類あり、 「人間の動きとしてリアル」と「相互作用がリアル」ではやることが変わってきます。 前者は、モーションキャプチャーをしたり、細かく芝居を作り込んだりして、 実際の人間の動きに近づければリアルになります。 後者は、「張り手されたら肉が波打ち、身体がめりこんだりしない」 というような「物理のリアリティ」であり、物理のシミュレーションを高度にすれば達成できます。

さて、72時間です。前者はまず絶望的です。 張り手なり投げなりのアニメーションを揃える時間はありません。 ただでも相撲は投げや掴みといった相互作用のあるアクションが多く、 リアルにしようと思うと相当な数のアニメーションが必要になります。 アセットストアに都合良く十分な種類の相撲アニメーションが売られているとは 到底思えないわけで、「事前に作られたアニメーションは基本使わない」 という方針にしました。 人体はラグドール(物理シミュレーションする物体を関節でつないだもの) として実現し、動きは全部物理にまかせる、ということです。

事前作成アニメーションでゲームを作ると、 例えば「張り手を顔に食らった時に毎回同じのけぞり方をする」 といった不自然さが出やすいのですが、全て物理にすれば この問題は解決します。当たる角度、速度によって勝手に毎回反応が変わるからです。

なお、平山が妄想していた「肉、汗、砂、塩」は全てカットです。 ただ砂に関しては、アート担当の土俵の質感へのこだわりと、HDRPの塵が散るような エフェクトで、なんとなく表現できている感じはします。

実装

というわけで基本方針は立ったのですが、大きな不確定要素がありました。 人体を物理で制御できるのか、つまり、 ラグドールに対して何か力を加えることで、立たせたり、張り手をさせたり、投げ技をさせたりは可能なのか? ということです。

当然平山はそんなことはやったことがありません。ロボット工学の経験者が都合良く隣にいたりもしません。 そこで、「どこまで単純化しても社内でウケを狙える動きが作れるか」、 と問題を単純化し、できそうなことから順に加えていくことにしました。

最初の制御設計

最初の設計は以下のようなものです。

  • 腰(pelvis)のRigidbodyはkinematicsで直接制御する。
  • 腰はconstraintを加えて動きを制約する。xz回転を止め、y位置を固定する。
  • 両手両足のrigidobyにAddForce()することで打撃を表現する。

腰にフックがついてぶらさがっていて、足は浮いていて、手はぶらさがっていて、 足と手を敵の方に発射して攻撃する、という感じですね。

もはや何が相撲なのかわかりませんが、「力士が土俵の上にいるのだからこれは相撲」 と強弁するしかありません。投げ技なんて作れるはずがないのです。 蹴りはないだろと私も思いますが、絵として面白いのでとりあえず作りました。

youtu.be

首の制御

さて、一応ゲームとして成立できそうな気はしてくるわけですが、 問題は山積みです。

まず、見た目に首が死んでるのが問題ですね。相手の方を向ける必要があります。 Quaternion.LookRotation()を使えば、無理矢理向けることはできますが、 ラグドールの関節には回転角制限があり、真後ろを向くことはできないはずです。 Quaternionを直接書き換えてしまうとそういった制限を無視してしまうことになります。 ですので、Rigidbody.AddTorque()に渡す角速度を計算して渡さねばなりません。

当時はこのあたりの経験が足りなかったため、今見るとよくわからない方法で計算していますが、 基本的には「向けたい方に前方ベクトルを向けるような回転軸を見つけ、それに適当な係数をかけてトルクとする」 という方法になっています。

その「適当な係数」を強くするほど首が強く相手を向き、弱くすれば、 あまり無理をしない、といったことになるので調整できるわけです。 この手法は身体を敵の方に向ける際にも使っています。

youtu.be

動画はうまく行く前のものです。

押したい

さて最初の設計では、身体のxz座標はkinematicsなので固定でした。 コントローラで左を入れれば左に動き、右を入れれば右に動きます。 しかしこれだと、押されても殴られても動きませんし、 そもそも相手をすり抜けてしまいます。

衝突で動かされるためには、kinematicsを外さねばなりません。 現実には、足と土俵に摩擦があり、それによって踏んばるわけですが、 今は腰が宙に浮いており、足の摩擦などというものはないので、 kinematicsを外すといくらでも押されてしまいます。

そこで、ある程度今いる場所に踏んばるような力を AddForce()でかけ続けることにしました。

土俵から落としたい

腰を一定の高さに保っていると、当然ですが転倒や落下が発生しません。

普通の格闘ゲームのように「HPゲージを削り切れば勝ち」とすれば、 このままでもゲームとして成立させられるのですが、 相撲なのにダメージ制なのは違和感があります。 そこで、転倒や落下が発生しうるように拡張することを考えたわけです。

まず、腰の高さを一定に保つことをやめなければなりません。 そのためには、腰のRigidbodyのconstraintからposition Yを除き、 毎フレームAddForce()をいい具合にかけて高さを保つようにする必要があります。 そして、土俵から外れたら、その力を切って落下させます。

そのため、足にコライダをつけて土俵との接触を監視し、 触っている時だけ腰へのAddForce()を行って高さを維持するようにしました。 そして「どれくらいAddForce()するか」はPID制御 を応用しています。

PID制御について詳しく書くと、それだけで記事が作れてしまいますので、 要望があれば後日ということにしましょう(実は相撲で初めて知ったので説明できるほどわかっていません)。 とにかくPID制御を使えば「ある目標値にいい具合に近づけるように加える力を求める」ことができます。

youtu.be

とはいえPID制御の調整にはコツがあります。この動画は、PID制御導入直後のもので、 I制御が強すぎて、空高くジャンプしてしまいます。

かくして「土俵から落とすゲーム」にすることができ、一歩相撲に近づきました。 また、「着地しているかどうか」の情報を使って、 「着地している時だけふんばる力が発生する」ようにすることで、 浮いている時に押されると落とされやすくなるようにしました。

youtu.be

落ちるようになった直後の動画です。

倒れたい、倒したい

土俵からは落ちるようになりましたが、腰のXZ回転は固定していますので、倒れません。 「張り手を顎に食らって意識が飛んで崩れ落ちる」といった印象的なシーンが表現できないのは、 相撲ゲームとしては問題です(それ以前の問題はありますが)。

そこで、腰Rigidbodyの回転constraintを外す作業に取りかかりました。 これは首を相手に向けるのと同様、腰RigidbodyのZ+ベクトルを、 特定方向に向けるようなトルクを生成する計算です。

これまた当時の私は経験が足りず、今見るとよくわからない計算をしていますが、 ともかくもそのようなトルクを生成して身体を起こすことに成功しています。 目標回転と現在の状態の差異を用いてPID制御で力の大きさを求め、 対称な位置にある2点に逆方向にAddForceAtPosition()で力を加えて回します。 この際、Rigidbody.maxAngularVelocity をある程度(20とか)大きくしておかないと この手の回転制御が効かないので注意が必要です(これがわかるのに半日を要しました)。

ダメージ表現

次に、ダメージを定義します。 ダメージによって何が変わるかと言うと、 PID制御で得られた力を使う率です。 例えばダメージが0点なら力の100%、100点なら0%、 というようにし、ダメージにはある程度の自然回復を設けます。 これによって、殴られた直後は力が弱まって 姿勢や腰の高さを保てなくなって倒れたり崩れたりし、 回復するに従って元の姿勢に戻る、ということが表現できます。 また、自然回復しない蓄積部分を設けることで、 だんだんと力が弱まって不利になるように設計しました。 「HPゲージのない格闘ゲーム」と言えば、当然 スマッシュブラザーズですね。 本当に偉大なアイディアだと思います。

youtu.be

その他雑作業

これでゲームの根幹部分はだいたいできました。 あとは、ヒット時に点光源を発生させたり、 カメラ制御を入れたり、 アート担当からもらったエフェクトを出したり、 効果音を鳴らしたり、 PlayStation4のコントローラで操作できるようにしたり、 といった雑作業と、パラメータ調整をして、完成としました。

社内のお披露目をし、例のtwitterアカウントができたのが、12/24のことです。 開発開始が12/1ですから、およそ3週間少しと案外長期間になりましたが、 本業を止めて相撲に集中していたわけではなく、 作業をしたのはせいぜい10日前後かと思います。

プログラム以外にも、アート担当者によるライティング設定、土俵作成、 人体テクスチャの直し作業もありましたし、 トモぞヴPによるバトル開始演出、 UIデザイナーによるUI要素の制作などもあり、 参加者全員の合計で72時間に収まっていたかはかなり怪しいのですが、 まあ、そういうものですよね。 余計に時間を使ったということは、それだけ楽しかったということです。

なお、今回公開するのであれば、ということで、 デバグ機能だった2PのAIを有効化するボタンを足しておきました。 一人でもなんとなく遊べます。

そしてDrawSaberへ

「ラグドールをAddForce()とAddTorque()だけで制御して、 事前に作ったアニメーションを作らない」 というコンセプトは結構面白いなと思っていました。 アニメーターがいなくても済み、 アニメーションの遷移を細かく設定する手間もありません。 ごく短いプログラムで多彩な動きが生じ、 殴られてのけぞるような相互作用も勝手に生じます。 そして何より、不自然な動きが笑いを誘います。

完成度が高まればもっと自然になるのかもしれませんが、 きちんとバランスを取って立ったり動いたりをさせるには、 ロボット工学のように高度な手法が必要であり、 少なくとも今の私には縁遠いものです。 この「笑える動き」で何か製品を作れないだろうかと思っていました。

最初はとりあえず普段やっているスマホ向けカジュアルゲームとして 無理矢理スマホ(URP)に移植して出してしまおうと思ったのですが、 全世界に出すのに相撲という題材は果たして伝わるのか、 というところに不安がありましたし、 操作系があまりにもスマホと合いません。

ボタン操作でのパンチ、キック、それに移動も手動、 という操作体系は明らかにゲーム機向けであり、 スマホのタッチパネルでカジュアルに遊ぶなら、 移動か攻撃のどちらかだけを人が操作して残りは自動化するか、 あるいはスマホ固有の操作であるドラッグを活かした別のゲームにするか、 どちらかだろうと思ったわけです。 ファイナルファイト のようなベルトアクションにして移動を自動化することも考えましたが、何かしっくり来ません。

そんなこんなで3ヶ月ほど経ったある日、 昔好きだったブシドーブレードと、 一時ステージを作りまくっていたParkMasterとが 不意に結びついて思いついたのが、 DrawSaber(Android, iOS) でした。

www.youtube.com

どうにかドラッグで操作したい、と思うと、 まず思い浮かぶのは手や足をドラッグで敵に引っぱって攻撃することです。 しかし、操作対象が複数あるのは複雑すぎます。 ここで武器があれば、操作対象は自然に一つになります。

また、武器が長ければ、ドラッグの範囲を広く取れ、軌道も多様なものにできます。 そして、ドラッグで武器の軌道を描くのであれば、 リアルタイムのゲームにすると指が邪魔で見辛くなりますので、 ParkMasterのように軌道を指定し終わってから動き出す方が良いでしょう。 敵の軌道が前もって見えていてそれに対応した線を引く、 という遊びにすれば「複数物が同時に動き出して意外な結果をもたらす」 というParkMasterの遊びの核をそのまま表現できます。

さて、剣ということになると手足や首の切断が欲しくなるわけですが、 人でそれをやると残虐ゲームになってしまって 多くのお客さんに遊んでもらうのは難しくなります。 そこで、人形にしました。人形であれば、 動きが多少不自然でも許されますし、 むしろそれが面白さにもなるでしょう。

武器は元々日本刀をイメージしていましたが、 日本刀は刃が当たらないと切れません。 しかし、上の記述を読んでいただいておわかりのように、 私は制御に関してそれほど高度な経験はありませんので、 そこまで制御するのは困難です。 そこで、どう当たっても切れそうな武器、 つまり「ビームの剣」にすることになりました。 ブルームエフェクトを足すことで見た目も派手になりますし、 色を敵味方で変えれば敵味方の軌道もはっきりします。 さらにブルームエフェクトで光っていれば元々のマテリアルは 単なるUnlitで良いので、調整の手間もありません。

そんな感じに相撲がDrawSaberになったわけですが、 人生、何がどうつながるかわからないものですね。

あなたもカヤックでそんなノリで開発してみませんか?

「SRE NEXT 2022」にSREチームの藤原が登壇します

SREチームの長田です。

5/14(土)・5/15(日)に開催される「SRE NEXT 2022」にカヤックSREチームの藤原が登壇します。

sre-next.dev

「1年間のポストモーテム運用と、そこから生まれたツールsre-advisor」というタイトルでポストモーテムの運用と、 そこから生まれたツールについて紹介させていただきます。

sre-next.dev

カヤックではSREが関わっている社内の複数プロダクトで、ポストモーテムを2020年末から運用してきました。

社内には多数のプロダクトがあるため、エンジニアは自分が関わっているもの以外の事故や事例に疎くなりがちでした。ポストモーテムの運用を通して、それがどう変わっていったかを紹介します。

ポストモーテムからは、普段は問題なく動いていても高負荷時や長期間の運用で問題になる、インフラやミドルウェアの設定が要因として見つかることもありました。その経験から、問題がある設定を自動で検出し、報告するsre-advisorというツールが生まれました。ツールの作成と、プロダクトに導入してからの運用についても紹介します。

(トーク紹介文より引用)

ポストモーテムの運用事例をお探しの方、あるいは社内ツールに関する知見に興味がある方におすすめです。

ポストモーテムの運用については先日当ブログに投稿された以下の記事でも紹介しています。 あわせてご覧ください。

techblog.kayac.com

カヤックではSREチームで一緒に働く仲間を募集しています!