TATIKUNLOG.

主にゲーム作りについて書き留めるブログ。

【考える①】Unityの等速移動を考える【Unity】



PR

調べたことをまとめるというよりは、考えたことや検証したことをまとめるカテゴリ 考える を作りました。

今回のトピックは Unityでの等速移動 で、以下の三種類方法について考えます。

それぞれ紹介した後、比較検証をします。

  • transform.positionに加算
  • rigidbody.velocityに代入
  • rigidbody.MovePositionを使う

それぞれの実装について

transform.positionに加算

Unity上で等速で物体を動かそうと思った場合、固定の値を物体の座標に対して加え続けるという実装が一番簡単です。

座標に対して直接値を足していく実装なので、動作が分かり易く、入門書籍でもこの実装を見かけます。

一方で、Unityの物理演算を使いたい場合にはこの実装は正しく動作しないという理由から避けられる傾向がある気がします(代わりにAddForceを使っている記事が多い気が)。

特にどういった時に不都合なのかは、後述する検証①に書きます。

void FixedUpdate(){
    Vector3 vec = new Vector3(Input.GetAxis("Horizontal"),Input.GetAxis("Vertical"),0);
    transform.position += vec * Time.deltaTime;
}

rigidbody.velocityに代入

物理演算をある程度機能させた状態で等速運動を実装する方法として、直接velocityに速度を代入してしまうというパターンがあります。

void FixedUpdate(){
    Vector3 vec = new Vector3(Input.GetAxis("Horizontal"),Input.GetAxis("Vertical"),0);
    rigidBody.velocity = vec;
}

公式ドキュメントでは、非現実的な物理シミュレーションになってしまうから、直接velocityを変えないでAddForce関数を使ってくださいと書かれています。とはいえ、そもそも完全な等速運動を実装するケースで現実・非現実感を気にしている方が稀な気がするので、直接代入しても良いとは思います。

rigidbody.MovePositionを使う

RigidBodyにはMovepositionという関数が存在して、引数に指定した特定の座標に向かって移動させる関数です。

ちなみに検証②でも確かめますが、Rigidbody の Is Kinematic を設定していないと、上記のpositionに加算する挙動と同じになるようです。

void FixedUpdate(){
    Vector3 vec = new Vector3(Input.GetAxis("Horizontal"),Input.GetAxis("Vertical"),0);
    rigidBody.MovePosition(transform.position + vec * Time.deltaTime)
}

検証

検証① positionに加算とvelocityに代入の差

positionに加算velocityに代入 の差について調べます。

まず差がない部分ですが、物体の移動距離は 時間 × 速度 で求まるので、 positionに加算する量をvelocityと同じにして、FixedUpdateの間隔時間(今回の実装だとFixedUpdate内でのTime.deltaTime)をかけることで、全く同じ距離の移動を実装することができます。

一方大きな違いとしては、positionに加算では物体との衝突がほとんど考慮されません。 Unityにはある物体A物体Bに衝突する時、Aが1フレームでBを飛び越えるほどの速度でも、 位置と速度の関係からABを飛び越えないように計算するシステムが備わっています(詳しくはRigidbodyのCollision Detectionで)。

しかし、positionに直接加算する場合にはこのシステムが適用されず無視して壁抜けします。 positionに加算の場合は1フレーム1フレームワープしているという表現がピッタリかもしれません。 なので、例えば物体に重なるような移動速度だと、相手にめり込んで無理やり追い出される(追い出す)力を受けます。

f:id:tat1kun:20220227173529g:plain
↑めり込んでしまって変な跳ね返りの力を受けている例

f:id:tat1kun:20220227151919g:plain
↑壁抜けする物体としない物体 壁抜けが不都合な場合にはvelocityを使う方が良いのではないでしょうか。

ちなみに衝突された物体側のリアクションとしても、 velocityの方は速度のある運動量を伴う物体に衝突されるため、ぶつかられた物体に速度がのっていますが、 positionに加算する方は速度0なので、ぶつかられた物体は動いていません。

検証② rigidbody.MovePositionのIs Kinematicによる変化

rigidbody.MovePosition は Is Kinematic のオンオフで結果が変わります。

※オン/オフよりtrue/falseの方が表現が適切かもしれませんが、ここではオンオフと表記します。

Is Kinematicがオフの場合は transformに加算 と同じ結果になります。

オンの場合は目標座標に移動するように速度が与えられるようです。 よって、今回の場合だとvelocityに代入するのと同じ挙動になります。

比較してみた動画も用意しました。

f:id:tat1kun:20220227182237g:plain
※transformに加算もvelocityに代入のどちらも Is Kinematic はオフになっています

余談ですが、velocityへ代入の場合はIs Kinematicをオンにすると速度0から変化しなくなります。一方でMovePositionを使った場合には速度が与えられます。なのでIs KinematicがオンであってもMovePositionでは例外的に物体へ速度を与えることができるようです。

終わりに

物理演算そのものの精密さが要求されることはあまり多くは無いと思いますが、 キャラクターの動作一つ一つがプレイの気持ちよさにつながるケースでは 細かい動きまで調整したいですよね。

ゲームエンジンの物理演算に頼る場合でも、頼らない場合でも、 どういった計算が行われているのか、どういった方法でキャラクターを動かすのが適切なのか、 考えてみるのは大事ですし面白いテーマだと思います。

その他参考記事

[Unity]RigidbodyのDragから終端速度を得る - Qiita

Unityでプレイヤーを移動させるときは、本当にAddForceが良いのか - Qiita

最適な衝突判定を即選択。Collision Detection 早見表【Unity 2018.3 以降】 | VirtualCast Blog

[Unity] positionとMovePositionの違いを比べた

【Unity】RigidbodyのIsKinematic(物理演算の影響の有無)を変えて実験 │ エクスプラボ