こんにちは、エンジニアのオオバです。
物理演算系ゲームを作るときによく登場するのは「AddForceメソッド」です。AddForceは値をセットするだけで簡単にオブジェクトに力を加え動かすことができます。ところが思ったように動かせないという声もよく聞きます。
そこで本記事ではRigidbodyのAddForceの仕組みを徹底的に解説してみました。ポイントはAddForceの第2引数「ForceMode」です。ForceModeの違いを正しく理解できているかどうかにかかっています。
AddForceの苦手意識が少しでもなくせるように、 わかりやすすく解説しています。ぜひ、最後まで読んでみてください。
👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!
RigidbodyのAddForceとは
Rigidbodyの「AddForceメソッド」を使うと 「オブジェクトに力を加えて移動させる」 といったシミュレーションが可能です。RigidbodyとColliderを使うことで、力を加えてオブジェクト同士を衝突させることもできます。
上の動画は力を「サンタクロース」に加え、ソリにぶつけた例です。ソリはサンタから力を受け前に進みます。この衝突は高校の物理の授業で習った 「運動方程式」 が使われています。
AddForceには4つのモードがあります。後述しますがモードを切り替えることで全く違う挙動になるため、AddForceを使う人はForceModeの違いをしっかり覚えておいたほうが良いでしょう。
RigidbodyのAddForceの基本的な使い方
簡単にAddForceメソッドの基本的な使い方を紹介します。AddForceは第1引数に力(Vector3型)、第2引数にForceMode(省略可)を指定します。
rigidbody.AddForce(new Vector3(1, 3, 4f), ForceMode.Force);
ちなみにAddForceはRigidbodyに設定された「質量(重さ)」が大きく関わってきます。重いものより軽いものの方が少ない力で動かせますよね。このような日常的な物理法則をAddForceで実現できるのです。
言葉だけではイメージしづらいため、実際に動いているところを紹介していきます。準備として「ソリ」を用意しました。
ソリのGameObjectにRigidbodyをセットし、ソリの質量(重さ)を1Kgに設定します。
RigidbodyのInspectorウィンドウから「Mass」を1にしましょう。 Massとは質量のことで単位はKgです。 重力の適用を外したいので「Use Gravity」のチェックを外してください。以上で準備は完了です。
繰り返しになりますが、 AddForceメソッド
の第1引数に力をセットします。力とは「ニュートン」です。物理の授業で習った運動方程式が「F = ma」という公式を覚えているでしょうか。「F」は力、「m」は質量(重さ)、「a」は加速度です。この公式を当てはめてソリを動かしてみましょう。
「質量1Kg」のソリに対して 「質量1Kg」「加速度1メートル毎秒毎秒」の力を正のZ軸方向へ1フレーム間与えると、ソリの速度はいくつになるでしょうか。 図にすると以下のイメージです。
答えは毎秒0.02メートル(m/s)です。
実際にUnityで動かしてみます。
すると結果ソリの速度は「毎秒0.02メートル(m/s)」になります。パッと見全く動いていないように見えますが、少しずつ動いています。ここで重要なのはAddForceメソッドの第2引数 「ForceMode」 です。今回使用したソースコードの一部を見てみましょう。
var m = 1f; // 質量
var a = 1f; // 加速度
var force = m * Vector3.forward * a;
rigidbody.AddForce(force, ForceMode.Force);
ForceModeは「Force」を選んでいます。前述の通りForceModeによってAddForceの挙動は大きく変わります。ForceMode「Force」に着目して解説します。
ForceMode「Force(フォース)」
ForceModeの「Force」とは速度を算出する際にΔt(デルタタイム)を使うモードです。 Δt(デルタタイム)とは力をかけ続けた時間 。つまり力を加えた時間を使って計算します。
具体的にはこちらの公式で速度を算出します。
// F・・・力
// m・・・質量
// Δt・・・かかった時間
速度 = F/m * Δt
Unityの物理演算はデフォルトで1フレームを 「0.02秒」 と設定しています。フレームレートやFPSと呼ばれる時間とは更新時間が違い、 物理演算の更新はUnityのUpdateメソッドとは全く別軸で実行されるのです。 ちなみに物理演算の1フレームにかかった時間Δtは Time.fixedDeltaTime
で取得できます。
この0.02秒を公式に当てはめてみましょう。
ソリの速度 = 1(F)/ 1(m) * 0.02(Δt)
となり、ソリの速度は「0.02m/s」だったというわけです。
もしソリのスピードを毎秒1メートルにしたい場合は、Δtを1秒にすればよいわけです。
ソリの速度 = 1(F)/ 1(m) * 1(Δt)
つまり1秒間力を加え続けるのです。次の動画は1秒間力を加え続けてみました。
多少の誤差はありますが、 1秒間力を加え続ける ことによって毎秒1メートルのスピードでソリが動き出したことが分かります。
力を加えるときはFixedUpdateを使う
ForceMode「Force」を使う場合はFixedUpdate内で行いましょう。前述の通り Unityの物理演算は独自の更新タイミング(FixedUpdate)で実行されるから です。1秒間「力」を加えるサンプルコードは次のとおりです。
private float _dt; // 力を加えている時間
void Update()
{
// Updateで1秒間を計測
_dt += Time.deltaTime;
}
private void FixedUpdate()
{
// 1秒間、力を加える
if (_dt < 1f)
{
// 運動方程式 : F = ma
var force = m * Vector3.forward * a;
rigidbody.AddForce(force, mode);
}
}
後述しますが、 AddForceはFixedUpdateで必ず実行するという覚え方は危険です。 ForceModeによってはFixedUpdateで力を加え続けてはいけないモードもあります。
ぜひこの後の章も読んでみてください。
ForceMode「Acceleration(アクセラレーション)」
AccelerationはForceと似ています。 違いは質量を無視する点 です。例えば質量の異なる「1Kgのソリ」と「20Kgの雪だるま」を用意します。
質量の変更はRigidbodyの「Mass」の値。単位は「Kg」です。
質量の異なるそれぞれのオブジェクトに 同じ力を加える とどうなるでしょうか。
まずはお互いForceモードにしてUnityで実行してみます。
すると、1Kgのソリの方が雪だるまより早く移動します。なぜならソリの方が雪だるまより「軽い」からです。
ゲーム開発ではときおり異なる質量のオブジェクトを同じ具合に動かしたくなるときがあります。そんなときにForceMode 「Acceleration」 を使うのです。
ソリと雪だるまのForceModeをそれぞれ「Acceleration」にしてみましょう。
// ForceModeをAccelerationに変更
rigidbody.AddForce(force, ForceMode.Acceleration);
するとソリと雪だるまは質量が異なるにも関わらず 「同じスピード」 で動くようになるのです。 ゲーム開発では物理法則を無視しなければならない場面はよく出てきます。 そんなときに「Acceleration」を覚えておくと良いでしょう。
ForceMode「Impulse(インパルス)」
「Impulse」はForce、Accelerationと違ってΔtを必要としません。つまり力を加えた瞬間に1秒間「力」を加えた状態になります。
前述の「ForceMode「Force(フォース)」」を思い出してください。「質量1Kg」のソリに対して「質量1Kg」「加速度1メートル毎秒毎秒」の力を正のZ軸方向へ1フレーム加えるとソリの速度は毎秒0.02メートルでした。
動いているの動いていないのか分からないくらいのスピードでしたね。
しかしForceModeを「Impulse」に変更すると1フレームしか力を加えていないにも関わらず毎秒1メートルでソリは動きます。
上の動画のように「Force」では毎秒0.02メートルのスピードだったのに対し、「Impulse」では毎秒1メートルで動いています。 理由はForceとImpulseでは計算式が全く違うからです。
// F・・・力
// m・・・質量
// Δt・・・力を加えた時間
速度 = F/m * Δt
// F・・・力
// m・・・質量
速度 = F/m
以上のとおりImpulseはForceと違い Δt(力を加える時間)を乗算しません。 言い換えると「Impulse」は1秒間「力」を加えた状態を1フレームで実現できるのです。 つまり「Impulse」はフレームをまたいで力を加え続けることは想定していないモードと言えます。
Impulseは「ForceMode「Acceleration(アクセラレーション)」」と同様、物理法則を無視する手段として覚えておきましょう。
ForceMode「VelocityChange(ベロシティチェンジ)」
最後のForceMode「VelocityChange」は、Impulseとほとんど同じです。違いは質量を無視する点です。「Force」と「Acceleration」の関係に似ていますね。
前述の「ForceMode「Acceleration(アクセラレーション)」」と同じように、1Kgのソリと、20Kgの雪だるまを用意し、それぞれ同じ力を加えてみましょう。
理解しやすいようにImpulseと比較してみす。
このように「VelocityChange」は「Impulse」と違い、質量の違いを無視して同じスピードを出せるのです。VelocityChangeはAccelarationと同じように質量が異なるオブジェクトを同じ具合で動かしたいニーズに答えます。例えば、大きい車と小さい車のレースゲームです。「レース中は物理法則に従うがレース外での動きは度の車も規則正しく同じ用に動かしたい」といった状況で生きます。
ぜひFoceModeそれぞれの違いを理解しておきましょう。
各ForceModeの早見表
最後にForceModeの早見表を作ってみました。重要なのは質量の影響とΔt(力を加える時間)です。
ForceMode | 質量の影響 | Δt | FixedUpdate |
---|---|---|---|
Force | ◯ | 必要 | ◯ |
Acceleration | × | 必要 | ◯ |
Impulse | ◯ | 不要 | △ |
VelocityChange | × | 不要 | △ |
「Δtが必要な場合」を 力をかけ続ける必要がある ということです。だから必然的にFixedUpdateを使用することになります。逆にΔtが「不要」な場合は「Impulse」と「VelocityChange」です。AddForceを実行した瞬間に、 1秒間押し続けた力 が適用されます。つまり「Impulse」と「VelocityChange」は 力をかけ続けてはいけません。
この辺りはぜひ、AddForceを手元で動かして確認してみてください。
RigidbodyのAddForceまとめ
この記事ではRigidbodyのメソッド「AddForce」について解説してきました。内容を簡単にまとめます。
①AddForceは運動方程式を使ってものを動かせる
②AddForceはForceModeによって全く異なる挙動になる
③ForceModeの違いは質量の影響を受けるか、Δtを必要とするかの2つ
④Δtを必要とする場合はFixedUpdateで力を加える
こんな感じです。
物理演算系ゲームを作るときにRigidbodyは活躍します。セットでAddForceを使うケースも多いと思います。 ぜひAddForceの第2引数ForceModeに注目してみてください。 さんざん解説してきましたが、ForceModeの選び方で大きく挙動が変わってきます。もし「質量違いのオブジェクトを同じように動かしたいなぁ」と思っている方、実はForceModeを変更するだけで簡単に実現できるのです。
AddForceの使い方で困ったときは、この記事に立ち返って復習してみてください。この記事があなたのゲーム開発に少しでもお役に立てたら嬉しいです。
最後に今回使用したアセットの紹介です。ソリとサンタはLow Poly Christmas Pack Freeという「無料」アセットを使いました。
ローポリで可愛いクリスマスデザインのオブジェクトが大量に同梱されています。 ファイル容量も小さく使いやすい ため、ぜひダウンロードしてみてください。しかも 無料 です。
→ Low Poly Christmas Pack Freeのダウンロードはこちら
この記事が気に入ったらフォローしよう
「Unity初心者大学」というUnity初心者向けのYouTube始めました!!
ぜひチャンネル登録をお願いします!
最後まで読んでいただきありがとうございました!
すばらしいRigidbodyライフをお過ごしください。
- Unity2020.3.31f1