【Unity】Rigidbody.velocityトラップ



Unity Rigidbody Velocity Trap



先に話してください

私はUnityの初心者でもあります。記事に誤りがある場合は、訂正することをお勧めします。他にアイデアがあれば、アイデアを交換し、一緒に学び、一緒に進歩することを歓迎します。

テキスト

Unity開発で直接使用することがよくあります Rigidbody.velocity ほとんどの場合問題ではない、リジッドボディの現在の速度を取得するためのプロパティ。ただし、場合によっては、これで目的の結果が得られないことがあります。たとえば、 transform.Translate()transform.RotateAround()リジッドボディ.MovePosition()Vector3.MoveTowards() 他のメソッド「force」がリジッドボディのモーション状態を変更する場合、この時点でオブジェクトの速度を変更しても、Rigidbody.velocityは変更されません。




信じないで?最初にUnityで実験を行って、これが当てはまるかどうかを確認しましょう。

探索する Rigidbody.velocity それが正確であるかどうかにかかわらず、コントロールとして比較的正確に速度を計算する方法を見つける必要があります。ここでの方法は、前のフレームの剛体の位置を記録するVector3変数を定義することです。速度を計算するときは、Update()で前のフレームに格納されているオブジェクトの現在のフレームの位置を減算し、それをフレームの更新間隔で除算します。 deltaTime、オブジェクトの瞬間速度が取得され、最後にVector3変数の値がUpdate()の最後に更新され、次のフレームで計算されます。 この方法で得られた値をゲームシーンの「実際の速度」として使用してみましょう。




図のように、最初にそのようなシーンを作成します。シーンには水平な地面があり、赤、黄、青の3つの小さなボールが地面に並べて配置されており、すべてに剛体属性が与えられています。

red.cs、yellow.cs、blue.csを作成してこれらの3つの小さなボールにバインドし、以下のコードを配置しました。

// red.cs using UnityEngine public class red : MonoBehaviour { private Transform m_transform private Rigidbody body private Vector3 lastPos private int frameNum = 0 //Record the current frame void Awake() { m_transform = transform body = GetComponent() lastPos = m_transform.position//By finding the displacement length and time interval of two adjacent frames //The ratio of deltaTime calculates the instantaneous rate of the object } void FixedUpdate() { //Use rigidbody.MovePosition() to move objects body.MovePosition(transform.position + Vector3.forward * Time.deltaTime) float realspeed = (m_transform.position - lastPos).magnitude / Time.deltaTime lastPos = m_transform.position frameNum++ Debug.Log(' ' + frameNum + 'frame ' + '【Mobile only】' + 'True Rate:' + realspeed +'Rigidbody.velocity:' + body.velocity.magnitude) } }

他の2つのスクリプトには上記とは異なるFixedUpdate()しかないため、この部分のみを以下に掲載します。

//blue.cs void FixedUpdate() { //Use rigidbody.AddForce() Adding force body.AddForce(Vector3.forward * 5) float realspeed = (m_transform.position - lastPos).magnitude / Time.deltaTime lastPos = m_transform.position frameNum++ Debug.Log(' ' + frameNum + 'frame ' + '【Force only】' + 'True Rate:' + realspeed +'Rigidbody.velocity:' + body.velocity.magnitude) } //yellow.cs void FixedUpdate() { //Use rigidbody.MovePosition() Moving objects //Also use rigidbody.AddForce() Adding force body.MovePosition(transform.position + Vector3.forward * Time.deltaTime) body.AddForce(Vector3.forward * 5) float realspeed = (m_transform.position - lastPos).magnitude / Time.deltaTime lastPos = m_transform.position frameNum++ Debug.Log(' ' + frameNum + 'frame ' + '[Mixed Transformation]' + 'True Rate:' + realspeed +'Rigidbody.velocity:' + body.velocity.magnitude) }

3つのコードの違いは、red.csがMovePosition()メソッドを使用して赤いボールを移動し、blue.csがAddForce()メソッドを使用して青いボールを移動し、yellow.csが一緒に2つの方法。黄色いボール。それ以外の場合は、すべて同じ方法を使用して瞬間速度を計算します。同様の機能は、コンソールパネルでの情報の抽出を容易にするために、スクリプト実行のシーケンスをカスタマイズするために作成する3つのスクリプトに分割されています。スクリプトでは、計算された瞬間速度と、比較のためにコンソールパネルのRigidbody.velocityのサイズを比較します。実行後の結果は次のとおりです。



画像
画像

[Move Only]スクリプトでは、MovePosition(transform.position + Vector3.forward * Time.deltaTime)ステートメントにより、赤いボールがdeltaTimeの時間内にVector3.forward * Time.deltaTimeの距離だけ移動します。真のレートは1です。これは、コンソールでのスクリプトの出力と一致しています。しかし同時に、そのRigidbody.velocityは0として表示されており、これは前に述べた結論を裏付けています。

さらに、いくつかの興味深い現象があります。

  • Rigidbody.velocityは、ボールが力の下にある場合にのみ真の速度と一致する可能性があります
  • ボールにストレスがかかり、MovePositionが変換された場合、真のレートは同じフレームの真のレートの合計[移動のみ]と[力のみ]であり、Rigidbody.velocityは[力のみ]と一致します。

そして、すべてのボールのリジッドボディコンポーネントのIs Kinematicプロパティを確認するときは、実行結果を見てみましょう。
画像
ああ、神様! [移動のみ]ボールのRigidbody.velocityは、実際には実際の速度と同じです。

このシーンでは、MovePosition()が[MovementOnly]変換の関数として使用されています。実際、transform.Translate()、transform.RotateAround()、rigidbody.MovePosition()、Vector3.MoveTowards()なども同じ結果を生成できます。ここでは実験は繰り返されません。

分析

属性Rigidbody.velocityが内部でどのように定義されているのかわかりません。公式文書には関連する説明はなく、インターネット上にも関連する情報は見つかりませんでした。私はこれらの現象に基づいていくつかの推測しかできません。

リジッドボディのキネマティックがチェックされていない場合、リジッドボディのモーションはUnity物理エンジンによって制御され、オブジェクトのモーションと状態は実世界の物理法則に従います。ニュートン力学では、 オブジェクトのモーション状態を変更するには、オブジェクトに力を加える必要があります 、UnityはAddForce()メソッドも提供します。ただし、MovePosition()などのメソッドを使用すると、オブジェクトを希望どおりに移動できるようになり、任意の位置に任意の速度で到達できるため、オブジェクトを非常に大きな速度に瞬時に上げることができます。明らかに、運動状態へのこの「強制的な」変化は、すでに行われている力によって達成されてはなりません。 現実世界の物理法則から あまりにも。物理エンジンの不正操作によって制御されるオブジェクト、 Rigidbody.velocityは、この「違法な」操作によって引き起こされた速度変化を記録しません 、またはこの違法な操作の速度のゼロへの変更。

それどころか、 リジッドボディのキネマティックがチェックされている場合 、リジッドボディの動きはUnityの物理エンジンの制御不能です。風水が順番に向きを変え、天と地が生まれ変わります。この場合、MovePosition()は正当な操作になり、AddForce()は不正な操作になります。憎しみを復讐したいMovePosition()は、長年の不満を積み重ね、違法行為の制限が厳しくなっている。以前の状況では、違法な操作によってオブジェクトの動作状態が変更されることもありました。今回は、AddForce()の役割が完全にブロックされました。 。前のスクリーンショットからわかるように、今回はフォースオブジェクトでさえ静止しています。現時点では MovePosition()は、最終的にRigidbody.velocityによって正当な操作として認識されます。 オブジェクトの真の速度を反映できるようにします。

総括する

上記の場合、私の考えでは、Is Kinematicがチェックされていないリジッドボディには、transform.Translate()、transform.RotateAround()、rigidbody.MovePosition()、Vector3.MoveTowards()などのメソッドを使用しないのが最善です。結局のところ、これらの型破りな操作は、物理シミュレーションの信頼性に間違いなく影響します。これを使用する必要がある場合は、Rigidbody.velocityがシーンおよびゲームビューでのオブジェクトの実際の速度ではないことにも注意してください。これらの方法とこのプロパティを悪用して、誤って「トラップ」に陥らないようにしてください。

実装メカニズムが不明確であるため、これ以上要約することはできません。どなたでもご意見、ご提案をお待ちしております。