Unity物理におまかせゴルフ
〜ぐるぐるイーグル〜
株式会社 Aiming
ソフトウェアエンジニア
西尾 昌哲
http://sp-eagle.com
2014年 4月14日 iOS版リリース
2014年 12月15日 Android版リリース
ゲーム紹介
自己紹介
株式会社Aiming
ソフトウェアエンジニア
西尾 昌哲
http://www.facebook.com/syoutetu
このスライドの概要
Unity4の物理エンジンを利用して
ゴルフゲームを作ってみた結果、得られた経験
・ 物理オブジェクトの構成や設定周り
・ 物理エンジンにまかせっきりにできたこと
・ 特殊な制御しなければできなかったこと
などを紹介
・ ゴルフゲームを構成する物理オブジェクト
・ ゴルフボールの挙動について
・ パフォーマンスについて
・ デバッグについて
・ 物理エンジンを使ってみた感想
目次
Unityの物理エンジン
PhysXというメジャーな物理エンジンを
利用している
・ Unity5でPhysX3.3にアップデートされた
・ Unreal Engine4もこれ
現在はNVIDIAが開発している
NVIDIAの開発者登録すれば
githubのソースコードも見ることができる
https://developer.nvidia.com/physx-sdk
ゴルフゲームを構成する
物理オブジェクト
・ ゴルフコース
・ ゴルフボール
・ その他の障害物
・ 物理マテリアル
物理エンジン周りのアセット(データ)説明
ゴルフコース
Mayaで作成した描画用のメッシュから
そのまま当たり判定用のMesh Colliderを作成
ゴルフコース
描画用のメッシュをコリジョンにも使ったのは
以下の理由から
・ ゲームプレイと密接に関わるので精度を
下げたくなかった
・ コリジョンモデルを用意する手間を
減らしたかった
・ パフォーマンスが低下するようなら、コリジョン用に
別モデルを用意する予定だったが、その必要はなかった
ゴルフボール
球体のモデルにRigidbodyと
Sphere Colliderを適用している
直径は5cmで、実際のボールより
少し大きいのは画面に映ったときに
見えづらく感じたから
(そのため、カップも大きく作ってある)
その他の障害物
地面に配置してある建物や木
水面に浮かぶボートなど
その他の障害物
パフォーマンス低下を防ぐため
描画用のメッシュが細かい場合は
粗いコリジョンメッシュを用意している
(結局パフォーマンスはそれほど問題にならなかったので
こうする必要はなかったのかも)
描画用
1506Triangles
コリジョン用
218Triangles
物理マテリアル
地形や障害物のColliderに設定されている
物理マテリアルは、物理挙動と
地形判定に使用している
例えば、ボールが停止したときに
真下にRaycastして地形を判定
ゴルフボールの挙動について
・ショット時にボールに与える力
・ボールの停止制御
・コリジョン抜け対策
・落下予測地点の計算
・グリーン周りの調整
・カップ周りの調整
ショット時にボールに与える力
プレイヤーや地形のパラメータから
最終的にRigidbodyに力とトルクを設定
rigidbody.AddForce(force, ForceMode.Impulse);
rigidbody.AddTorque(torque, ForceMode.Impulse);
トルクによるボールの回転(AngularVelocity)は
空中でボール軌道が曲がる時のパラメータ
としても使っている
複雑な計算はしていなくてAddForceをしているだけ
ボールの停止制御
RigidbodyがSleep状態になると停止判定
・ スリープに入った時のイベントは見つからなかったので
Update()でIsSleepingをチェックしている
・ RigidbodyのSleepVelocityやSleepAngularVelocityも
ボールの転がり調整のために低く設定している
特殊な地形に対するのボールの挙動は
RigidbodyのDrag、AngularDragを調整
・ 例えば、バンカーに突入したときは
大きな値を設定してSleep状態に向かうようにする
コリジョン抜け対策
厚みのないメッシュコリジョンと
高速に移動するボールとの衝突は
判定の通り抜けが多発した
・ 1フレームでボールがメッシュを完全に
突き抜けてしまうと当たり判定が失敗する
・ RigidbodyのCollision Detectionの設定を
Continuous Dynamicにすると抜けなくなるが
ボールが跳ねる挙動などがおかしくなってしまった
Dont Go Through Things
前フレームから現フレームへのRigidbodyの移動が
メッシュを突き抜けていたら、
メッシュに軽く重なるくらいの位置に戻す処理をしている
・ 物理マテリアルに設定されている物理挙動も再現できている
・ ただし、このゲームでは1つ問題があった(後述のデバッグ)
現フレームの
修正したボールの位置
前フレームの
ボールの位置
現フレームの
突き抜けたボールの位置
http://wiki.unity3d.com/index.php?title=DontGoThroughThings
Dont Go Through Things
地面
落下予測地点の計算
このゲームには落下予測地点
と呼ばれるUIがあり、ショットした
ボールが地面に落下する地点
・ ただし、高低差なしで無風の場合
しかし、物理エンジンを使っているので
事前に落下地点を予測できない
・ 実際にシミュレーションてみないと、
どこに飛ぶかわからない
落下予測地点の計算
実際にいろんな条件でボールをショットした
パターンのデータを事前に作っておいて、
ゲームプレイ中はそのデータを補間計算して
落下予測地点としている
・ クラブの自動選択の時も、このデータを使っている
飛距離のサンプリング
プレイヤーのパワーパラメータのレベル(160段階) ×
パター以外のすべてのクラブ(13種類) ×
すべてのカーブショットの弾道(27種類) ×
パワーゲージの%(現状は20%ごとに5回サンプリング)
= 約28万回のショットをサンプリング
グリーン周りの調整
パターの時にボールが転がるように設定すると
外から打ち込んだときにも転がってしまい
とてもグリーンに乗りにくい
・ 物理マテリアルのパラメータだけではどうしても
調整できなかった
グリーンの物理マテリアルを2つ用意して
パターを使う時とそれ以外で切り替えている
・ プログラムでも少し調整している
カップ周りの調整
カップ側面でボールが止まる事が稀に発生
(速度が足りなくてRigidbodyがSleep状態になる)
結局、その状態を判定してカップ方向に
少しだけ力を加えてカップインさせる
というちょっと無理矢理な方法で解決
パフォーマンスについて
・最も重いゴルフコースは?
・物理エンジン周りの設定
最も重いゴルフコースは?
MeshColliderの数が一番多いのは
スイートシュガーエデン Hole5
219個のMeshCollider(合計31786Triangles)
最も重いゴルフコースは?
MeshColliderのトライアングル数が一番多いのは
ミヤビツシマ Hole8
164個のMeshCollider(合計65201Triangles)
物理エンジン周りの設定
動かないオブジェクトには
Staticフラグを付ける
Layer Collision Matrixで
衝突判定するレイヤーを
必要最小限に設定する
設定はメニューの
Edit - Project Settings - Physics
デバッグについて
カップインしたとき1万回に1回くらいの割合で
ボールがカップから抜け落ちることがある
ということがリリース後に発覚
手動では再現しない(開発中は1度も起きなかった)
自動的にカップにボールを打ち込む
プログラムを作って、現象の確認と問題修正
ボール抜け落ちチェック
ボールがカップから落下したら検出される
原因は、前述 Dont Go Through Things の
コリジョン抜け対策のところで、微細な移動の場合は
その処理を飛ばしていた為だったらしい
グリーン上のランダムな位置からカップ方向に
ランダムなパワーで自動的にパッティングを数万回実行
物理エンジン使ってみた感想
良かった点
・ 自前でやるより工数は削減できたと思う
・ モバイルでもパフォーマンスは良い
(過度な期待は禁物だけど、他のゲームでも演出などで
気軽に使う事ができると思う)
良くなかった点
(ただし、自前で実装しても同じような問題に遭遇しそう)
・ 物理マテリアルの調整は面倒
(エンジニア以外に任せることも可能)
・ デバッグも面倒

Unity物理におまかせゴルフ ぐるぐるイーグル