【Unity】物理演算を徹底解説!Rigidbody停止・精度変更・水や髪の実装まで


Unityでゲームを作る際にほとんどの場合物理演算を使いますが、想定外の動きになってしまい修正に時間がかかってしまうことがあると思います。

また、「物理演算を自作したい」や「物理演算の処理を軽くしたい」など、物理演算に対する悩みは多岐にわたると思います。

それらの悩みを解決するために、今回はUnityの物理演算を体系的に学べるように徹底的に解説します。

物理演算とは

物理演算とは物体の運動を物理法則(質量・速度・摩擦・風といった、古典力学的な法則)に基づいてシミュレーションすることです。物理シミュレーションとも呼ばれます。

Unityには3Dの物理演算と2Dの物理演算の両方が存在するので、3Dゲームでも2Dゲームでもあらゆる物理演算を表現することができます。

現実に限りなく近い物理法則にすることもできますし、現実ではありえないようなゲームならではの物理法則にすることもできます。

いかに物理演算を扱うことができるかが、ゲームのクオリティを左右することになります。

Unityの物理エンジン

Unityでは物理演算エンジンとしてNVIDIAが開発しているPhysXが使われています。

PhysXはリアルタイムの物理演算エンジンで、Unreal Engineでも使われています

2018年12月からオープンソースとしてGitHub上で公開されています。

オープンソースへのアクセスは、NVIDIAの開発者向けページから可能です。

Unityでの物理演算関連のコンポーネント

Unityの物理演算関連で使用する主なコンポーネントは以下の3つです。

  1. Rigidbody(リジッドボディ)
  2. Collider(コライダー)
  3. Joint(ジョイント)

これらのコンポーネントの特徴や使いどころについて以下で詳細に説明していきます。

1.Rigidbody

RigidbodyはUnityの物理演算を実現したい場合に利用するコンポーネントになります。

例えば、重力を設定する場合や空気抵抗を設定する場合に使います。位置や回転をXYZ軸毎に制限を設けることもできます。

Unityでの物理演算の基本の知識になるので、各項目でどのような設定ができるのかを把握し、自由に扱えるようにしましょう。

Rigidbodyに関して詳細に解説している記事があるので、是非ご覧ください。

参考記事)【Unity】Rigidbodyを使用して重力・空気抵抗を発生させる方法

また、Rigidbodyに関連することで、跳ね返りや摩擦が発生するようにする時に使用するのが物理特性マテリアルになります。

以下のような例が挙げられます。

  • ビリヤード
  • エアホッケー
  • 跳ねることができる地面
  • 球技スポーツ

マテリアルであるためゲーム上のステージから細かいアイテムまで簡単にアタッチができ、様々な場面での使用が可能です。

物理特性マテリアルでの跳ね返りと摩擦について解説している記事があるので、是非ご一読ください。

参考記事)【Unity】物理特性マテリアル(弾性)を用いた跳ね返り表現の方法

参考記事)【Unity】物理特性マテリアルを用いた摩擦表現の方法

2.Collider

Colliderはオブジェクト同士の当たり判定を行うために利用するコンポーネントになります。

例えば、ボールが壁に衝突したことを判定したり、敵に剣で攻撃したことを判定したりするときに使います。

例えからも分かるように、ゲームで当たり判定は必ず使われるため、正しく設定できるかどうかはゲームを作れるか否かに直結するほど重要です。

当たり判定には「接触した瞬間」・「接触している間中ずっと」「接触して離れた瞬間」など様々な種類があるので、状況に応じて使い分けましょう。

具体的にどういう当たり判定があり、何を基準に使い分けるべきなのか、については詳細に解説している記事がありますのでそちらをご覧ください。

参考記事)Colliderによる当たり判定を理解しよう!

3.Joint

Jointは複数のゲームオブジェクトを繋げて1つの連動した物体を作るために利用するコンポーネントになります。

主に揺れ物(鎖・ロープ・布・髪の毛など)をゲームに実装する場合に使います。

Jointをすることで、Jointされた複数のゲームオブジェクトが物理演算によって連動されるようになります。

Jointの種類は

  • Fixed Joint:複数のオブジェクトを不動状態で結ぶことができるJoint(距離や回転が不変)
  • Hinge Joint:特定の座標軸を中心に複数のオブジェクトを回転できるJoint(距離が不変で回転が可変)
  • Spring Joint:バネのように距離を伸縮できるJoint(距離が可変で回転が不変)
  • Character Joint:主にラグドール処理の時に使用する、各軸でJointを制限できる拡張Joint
  • Configurable Joint:自由にカスタマイズでき、あらゆるものを表現できる拡張Joint

の5つがあります。

また、Jointの設定の中で「一定限度以上の力が加わった時にJointが壊れる」といった特殊な設定も可能です。

オブジェクト同士の連動をスクリプトで表現しようとすると非常に難しいので、大変便利な機能の一つです。

物理演算での知っておくべき知識やテクニック

ここからは物理演算で生じる課題や悩み事を出来る限り解説していきます。

オブジェクトが壁をすり抜けてしまう:物理演算の精度について

壁と衝突した場合は反射するという設定の時に、オブジェクトが高速で動くと壁を貫通してしまうことがあります。

明らかに壁に衝突しているはずなのに跳ね返らない理由は、物理演算の処理にあります。

オブジェクトの移動は毎フレームを瞬間移動しているイメージに近く点々と移動する形なので、常に衝突の判定をしているわけではありません。

なので、あまりに速すぎると壁と衝突している時のフレームが存在しないという場合があり、衝突のフレームがない=衝突していないという判定になりすり抜けてしまうのです。

この問題は速度を遅くしたり、壁をもっと厚くしたりすることで解決することができます。

また、フレーム数を増やし(FixedUpdateを増やし)瞬間移動の間隔を短くする(補間回数を増やす)ことで解決することもできます。

ただ、この方法は物理演算の精度は良くなる一方で、ゲームでの全ての物理演算に非常に重大な負荷を与えてしまいます。

したがって、状況に合わせて解決策を選択する必要があります。

Rigidbodyを一時停止する:物理演算の停止

ゲーム中にオブジェクトを一時停止したい場合には、velocityとangularVelocityを使用することが多いです。

上のスクリプトを使ったのが下の動画です。青のボールが上のスクリプトを使っていて、緑のボールが上のスクリプトから「UpArrowで止まる」というスクリプトを削除したものになります。

確かに青のボールがビタっと止まっていることが分かります。

このように、物体の物理演算を一度停止したい場合はvelocityとangularVelocityを0にすればよいのです。

ただし、上の方法では重力の影響を消すことはできないので、停止した後に重力でまた動き出してしまいます。

もしUse GravityをTrueにしている場合でビタっと止めたい時は、重力の影響を受けさせないように以下のスクリプトも記述します。

オリジナルの物理演算を使いたい:物理演算の自作

物理演算 自作

先述のように、Unityの物理演算にはPhysXという物理エンジンを使っています。

PhysX自体優秀な物理エンジンではあるのですが、PhysXでサポートされている機能しか使えないという制限もあります。

例えばUnityのRigidbodyはあくまでも剛体のシミュレーションしかせず、速度と重力の計算だけで空気抵抗などの計算が微妙で、ボールの軌道が非現実的になってしまうことがあります。

このように、ゲーム作成がより高度になると、Unity 上で最新の物理シミュレーション手法を動かしたいと考え、Unityの物理演算では満足できない場合が出てきます

その際にPhysXではなく自作の物理演算を使うことができます

(ただし、物理演算を自作するにはかなりのC#知識が必要なので、腕に自信がなければ基本はPhysXを使用しましょう。)

【自作の物理演算を使う方法】

①Rigidbodyを設定しない。

②物理演算を表現したC#スクリプトを記述する。

②GameObjectにC#スクリプトをCompornentとしてアタッチする。

以上で可能となります。

実際に物理演算を自作する場合は、論文や企業の特許情報などに記載されているシミュレーションを真似すると上手く行きます。

これらの情報はネット上に広く公開されていることが多いので、いかに数を集められるかで「自分が表現したい物理演算」に近くなります。

ぜ希望があれば、自作の物理演算を使用したゲームも作ってみたいと思います。

揺れ物(髪や服)を揺らすときの物理演算

髪や服などの、いわゆる「揺れ物」をより細かく表現したい場合に、簡単に実現できるスクリプトは、

SpringBone
DynamicBone

の2つになります。Boneという名前だけあって、boneにアタッチします。

SpringBone

SpringBoneはUnityが公開しているスクリプトで、揺らしたいパーツにアタッチします。

物理演算を使うわけではなく、ボーンを回転させるというシンプルな動きで表現しているので、揺れ方はかたい感じで、形を戻そうとする力が強い感じで揺れます。

例えば

  • スカート(短め)
  • アクセサリー
  • 髪の毛(ショート)

等に使うと良いです。

SpringBoneの特徴として、親boneに設定したときに子bone全てを1つずつ登録していく必要があります。

DynamicBone

DynamicBoneはAsset Storeで有料で販売してあるスクリプトになります。

物理演算が取り入れられているため弾力のあるような揺れ方をし、自然な動きを表現するのに適しています。

例えば

  • スカート(ロング)
  • 胸・おっぱい
  • 髪の毛(ロング)

等に使うと良いです。

また、パラメーターの調整や設定のカスタマイズ性が高いというメリットを有します。

かなり優秀なアセットなので、揺れ物をリアルに表現したい方であれば買って損はないものになります。

Asset Store)DynamicBone

液体の物理演算

NVIDIA FleX

水やマグマなどの液体の物理演算を実現したい場合は、パーティクルシステムとNVIDIA FleX for Unityという物理エンジンを使用することがほとんどです。

NVIDIA FleX for Unityとは、パーティクル(粒体)ベースのUnity向け物理演算機構で、流体用のシェーダーも附属されており、粒子同士の結合力を強固にした剛体、フレキシブルにした柔体、個々のパーティクルが粘り気を持って集合体を成してる流体などを表現できます。

この物理演算は以下のアセットストアからダウンロードして、プラグインとしてUnityに反映させます。

Asset Store)NVIDIA FleX for Unity (1.0 BETA)

お気づきの方もいらっしゃると思いますが、NVIDIAとはUnityに標準搭載されている物理エンジンPhysXも作成しています。

同じ会社が作っているため、粒体と物体の衝突なども違和感なく実装することができます。

FleX for Unityの使い方についてはNVIDIAが公式で出しているチュートリアル動画を参考にすると良いと思います。

処理が重いときの確認事項

物理演算は実装の仕方によっては処理が非常に重くなったりします。

そもそも物理演算自体重いCPU処理を伴っているのですが、いくつかの注意点を配慮してスクリプトを改良すると処理を軽くすることができます。

【主な注意点】

  • Time.fixedDeltaTime(Project settings内のTimeにて調整できる)を大きくすることで、物理演算結果の更新に消費する時間を減らすことができます。要は物理演算の精度を意図的に下げることになります。そのため、素早いアクションが求められるゲームでは大きくしすぎると衝突を正しく認識しないなどの不具合が起こります。あくまで、許容できる範囲でfixedDelitaTimeを調整しましょう。

 

  • Mesh Collider(メッシュコライダー)を使用している場合は、Primitive Colliderで代替することで処理を軽くすることができます。これは、Mesh ColliderがPrimitive Colliderより重い処理を有しているためです。

 

  • Cloth(クロス)オブジェクトの使用を極力少なくしましょう。Clothは3Dのメッシュを表現できるのですが、3D処理なので非常に重い処理をしなければなりません。

 

  • Rigidbody の使用を必要な場合のみに制限し、処理の回数を減らしましょう。

 

  • Static Collider(即ちRigidbodyのないCollider)を動かさないようにしましょう。そもそも動かされることを想定されていないColliderなので処理が重くなります。

 

  • (推奨しない方法だが)Maximum Allowed Timestep in the Time Managerを8–10fps 程度にして、物理演算に消費される時間に上限値を設定することで軽くすることができます。ただし、ゲームの処理に不具合が生じうるので十分に気を付けて設定ください。

動きがカクカクしてしまう現象の対応策(物理演算の最適化)

ゲーム作成において動きがカクカクしてしまう場合には、RigidbodyをUpdate関数の中で使っていないかをご確認ください(具体的にはrb.velocityやrb.AddForceなど)

RigidbodyはFixedUpdate関数の中で使用するようにしましょう。

理由はUpdateとFixedUpdateの特徴にあります。

Updateとは

Updateは1フレーム毎に呼び出される関数です。

フレームは画面を描画する単位時間のことで、ゲームなどでは60fps(frames per second)というように1秒間に60フレームが描画されています。

しかし、フレームの最大の特徴は端末の処理能力(CPU)によって変動してしまう点です。

普通は出来る限り指定のfpsに近い速度で処理をしますが、急に重い処理をさせようとするとfpsが小さくなることがあります。

更にUpdateはフレーム毎に呼び出されるので、不規則に呼び出されてしまいます。

この不規則さが動きがカクカクしてしまう原因なのです。

FixedUpdateとは

一方でFixedUpdateは指定した秒ごとに呼び出される関数です。

指定された秒の間隔は自分で設定することができますが、デフォルトでは0.02秒になっています。

もちろん端末の処理能力によって若干のブレは出るものの、Updateよりは安定した間隔の処理になります。

そのため、RigidbodyはFixedUpdate関数内が適しているのです。

まとめ

今回はUnityの物理演算について解説しました。

記事の冒頭でも記載した通り、物理演算の仕組みや実装方法を正しく理解し、様々な知識やスキルを身に付けていくことで、ゲームのクオリティは格段に良くなります。

是非この記事で少しでもクオリティの高いゲームが作れるようになれば幸いです。

Unityに関連した知識やスキルをまとめた記事があるので、良かったらこちらもご覧ください。

参考記事)【Unity入門】ゲーム開発ができるようになる基礎知識まとめ


この記事はいかがでしたか?よければシェアをお願いします。

あきごろう

株式会社x garden CGO(Chief Growth Officer) Unityエンジニアとして日々Unityと奮闘中 自他共に認めるゲーマー Splatoon最高記録:世界6位(リグマペア) ApexLegends最高記録:5連チャンピオン