なぜRigidbodyオブジェクトをTransformで移動させてはいけないのか


こんにちは。ソウタロウです。
今回は私が駆け出しゲームエンジニアだった時(数年前)にハマった、UnityにおけるRigidbodyオブジェクトの移動制御について書こうと思います。

単語の定義

弊社は定義するのが好きという話を小耳にはさんだので、まずは本記事に出てくる単語の定義から記載します。

  • Rigidbodyオブジェクト:Rigidbodyコンポーネントをつけたオブジェクト

以上

本記事の概要

UnityではRigidbodyコンポーネントをオブジェクトにつけることで簡単に物理演算が可能になります。
重力、スリープ状態の閾値等のパラメータが簡単に設定できるのでとても便利なのですが、オブジェクトの移動制御に関して、注意しなければならない点があります。
本記事では、Rigidbodyオブジェクトの移動をtransformで制御してはいけない理由を解説していきます。

Rigidbodyオブジェクトをrigidbody.positionで移動させた場合

まずは正しく動作するパターンを紹介します。Rigidbodyコンポーネントをつけた球体に坂を上らせてみましょう。
添付動画のように、意図した挙動で移動を制御できています。
スクリプトは下記のとおりで、Dキーを押しています。

hoge.cs
void FixedUpdate(){
         if (Input.GetKey(KeyCode.D))
             rb.position += new Vector3(0.15f, 0, 0);
     }

Rigidbodyオブジェクトをtransform.positionで移動させた場合

次に、右矢印キーを押して坂を上らせてみましょう。
坂にめり込んでしまっていますね。前述のスクリプトを見ても、意図した挙動でないのは明らかです。

hoge.cs
void Update () {
         if (Input.GetKey(KeyCode.RightArrow))
             this.transform.position += new Vector3(0.15f, 0, 0);
     }

なぜめり込んでしまうのか

上記のgifでそれぞれの挙動が異なることは理解できたかと思います。
では、なぜtransform.positionだとめり込んでしまうのか。
答えは Unityのスクリプトライフサイクルのフローチャートを読むと分かります。
本記事に関連する部分のみ抜粋すると、「FixedUpdate内の物理演算」→「Update内の演算」→「レンダリング」の順で処理されるイメージです。

つまり、Rigidbodyオブジェクトをtransform.positionで移動させた場合、物理演算が走る前にレンダリングされているということです。
コライダー同士が重なった場合の座標の修正が行われる前にレンダリングされているので、坂にめり込んでしまっていたのですね。

まとめ

Rigidbodyオブジェクトはrigidbodyを使ってFixedUpdate()内で制御しましょう(例外あり)。

あとがき的なもの

初めてアドベントカレンダーに参加し、初めてQiitaに投稿しました。大変ではありましたが、私用PCを数か月ぶりに開いて、Unityを起動したときのワクワクは新鮮なものがありました。この機会をくれた同期に感謝です。
ゲーム創りは楽しいことばかりではありません。勉強しなければいけないことが本当にたくさんあります。
現在、私はゲームプランナーとしてゲーム創りをしていますが、プランナーとエンジニアという分野は違えど、本記事が多くのゲームクリエイターの役に立てばと思います。
より良いゲームをたくさん創って、より多くの人にゲームの素晴らしさを届けたいですね。

====================================================
この記事を読んで「面白かった」「学びがあった」と思っていただけた方、よろしければ Twitter や facebook、はてなブックマークにてコメントをお願いします!
また DeNA 公式 Twitter アカウント @DeNAxTech では、 Blog記事だけでなく色々な勉強会での登壇資料も発信してます。ぜひフォローして下さい!