今回はUnityの物理演算に関する話題で、タイトルの通り
するという内容になっています。
Untiyでゲームを作っていると、Rigidbodyに力を加えてキャラクターやその他のゲームオブジェクトを動かしたくなることがよくあります。このときAddForce関数を使う場合が多いと思うのですが、初心者の方からしたら
- ForceModeってなんだ?それぞれ何が違うんだろう
- AddForceはFixedUpdate内で使わないとダメ?
- AddForceじゃなくてRigidbody.velocityを直接いじるのもダメなの?
といった問題や疑問が出てきますよね。
そこでここではその辺について基本的な部分から丁寧に解説していきますね。
AddForce関数とは?
まずはじめに「そもそもAddForce関数とは何か?」という点から解説しようと思います。
AddForce関数はその名の通りRigidbodyに力を加えるための関数です。第一引数に力のベクトル、第二引数に力のモード(※省略可能)を指定します。
この関数は「力を加える」ものなので力を加えた結果(=速度)は累積するのが特徴であり、
- Transform.positionを直接いじってゲームオブジェクトを移動させる
- Rigidbody.velocityを直接いじってRigidbodyの速度を変更する
といった移動方法と比較すると物理的に正しい挙動をします。したがってゲームオブジェクトを物理的に移動させたい場合はこれを使うのが一般的ですね。
ForceModeについて
さてAddForce関数の第二引数には「ForceMode」というものを指定することができます。これは力の加え方のオプションであり、どれを指定するかによって挙動が全然違ってくるのでしっかり理解しておきましょう。指定できるモードは全部で4つあります。
- Force
- Acceleration
- Impulse
- VelocityChange
それぞれ詳しく説明しますね。
Force
まずForceMode.Forceは「Rigidbodyの質量を考慮して継続的な力を加える」ためのモードです(デフォルトではこのモードが指定されています)。
このモードでは質量を考慮するため重い物体ほど動かすのに大きな力が必要となります。また、このモードは継続的にAddForceを呼び出して力を加えることを前提としているので、挙動としては慣性のついた重みのある動きになります。
公式マニュアルによると力の算出方法は次のようになっているそうです(注:DT=fixedDeltaTime)。
入力を力(ニュートンで測定)として解釈し、力*DT/質量の値によって速度を変更します。
Acceleration
次にForceMode.Accelerationは「Rigidbodyの質量を無視して継続的な加速度を加える」モードです。
こちらはForceと同じく継続的に力を加えることを前提としているので慣性がついたような動きになりますが、Accelerationの場合は質量を無視するため、物体がどんなに重くても第一引数の値が同じなら同じ動きとなります。
加速力の算出方法は次のとおりです。
パラメータを加速度(メートル/秒の2乗で測定)として解釈し、力*DTの値で速度を変更します。
Impulse
ForceMode.Impulseは「Rigidbodyの質量を考慮して瞬間的な力を加える」モードです。
こちらは上2つとは違って瞬間的な力を加える場合に適したモードであり、例えば
- キャラクターをジャンプさせる
- 爆発で破片が飛び散る
といった場合に適しています。また質量を考慮するので、物体が重いほど動かすのに大きな力が必要になります。
この場合の力の算出方法は次のとおりです。
パラメータをインパルス(ニュートン/秒で測定)として解釈し、力/質量の値によって速度を変更します。
VelocityChange
最後にForceMode.VelocityChangeは「RigidBodyの質量を無視して瞬間的に速度を変更する」モードです。
速度の算出方法は次のとおり。
パラメーターを直接の速度変化(メートル/秒で測定)として解釈し、力の値によって速度を変更します。
おまけ:ForceModeの比較表
ForceMode | 力の加え方 | 質量を考慮するか? |
---|---|---|
Force | 継続的 | 考慮する |
Acceleration | 継続的 | 無視する |
Impulse | 瞬間的 | 考慮する |
VelocityChange | 瞬間的 | 無視する |
AddForceはFixedUpdate内で処理を行ったほうがいい?
次に、Unity初心者の方にありがちな悩みとして
という疑問があると思うので、それについて簡単に説明しておきます。
まず結論から言うと
- 継続的に力を加える場合はFixedUpdate内のほうがいい
- 一瞬だけ力を加える場合はどちらでもいい
といえると思います。…なんだそりゃ、って感じですよね。
この結論を理解するにはまずUpdateとFixedUpdateの違いを知っておく必要があります。UnityのUpdate関数とFixedUpdate関数は次のような違いがあります。
- Update:1フレームに1回呼び出される(つまり呼び出し間隔が変化する可能性あり)
- FixedUpdate:フレームに関係なく、一定間隔で呼び出される
さて物理演算をフレーム依存(つまりUpdate内で実行)にするとどうなるかというと、使っているPCの処理速度やゲームの重さによって結果が変わってしまいます。参考としてAddForceをUpdate内とFixedUpdate内のそれぞれで実行すると、下のように若干違った結果になることが分かります。
これは私の環境で実行した結果であり、もしかしたら他の環境で実行するとUpdate内で処理を行ったほうは結果が変わってしまうかもしれません。一方でFixedUpdateは一定間隔で呼び出されるので、物理演算の結果が変わることはありません。
このようなことから一般的に「物理計算はFixedUpdate内で行いましょう」と言われているというわけですね。
ただし、ややこしい話なのですがこれは結果が累積するタイプの物理演算の話です。つまり「一瞬だけ力を加えてあとは何もしない」という場合はこの限りではありません。
AddForceを使わず、Rigidbody.velocityを直接いじるのはダメなのか?
では最後にAddForceとRigidbody.velocityの比較についてご説明しておこうと思います。
Rigidbodyでゲームオブジェクトを動かす方法として、AddForce以外では「velocityを直接操作する方法」が初心者の方向けによく紹介されています。なので結局どちらを使えばいいのか迷ってしまう方もいらっしゃると思いますが、結論から言うとどちらも一長一短あるので、どちらを使うべきなのかは作るゲーム次第だと言えます。
というのも、AddForceは物理的には正確ですがゲーム的には「モッサリした動き」になりがちなので、ゲームらしいパキッとした動きにしたい場合はvelocityをいじったほうがいいからです。
参考までにAddForceを使ったほうがいい場合と、velocityを直接変更したほうがいい場合の例を掲載しておきます。
AddForceを使ったほうがいい場合
- リアルな挙動を実現したい場合(シミュレーター系のゲーム等)
- 他のゲームオブジェクトから力を受ける場合
velocityを直接変更したほうがいい場合
- 物理的には正しくないが、ゲーム的な挙動をさせたい場合(2Dアクションゲーム等)
- 他のゲームオブジェクトから受ける力を考慮しなくてもよい場合
(※velocityを直接いじると速度が上書きされてしまい、他のゲームオブジェクトから力を受けてもそれが反映されない場合があるため)
おわりに
以上、RigidbodyのAddForce関数について色々とご説明しました。
Unity初心者の方にとってAddForce関数はイマイチよく分からないヤツだと思うのですが、使いこなせるとリアルな物理挙動を実現できて便利なのでぜひしっかりとご理解いただければと思います。
この記事がUnityでのゲーム開発のお役に立てば幸いです。