Unity3DでのLerp関数の使用と理解について

ハブロフスク市民への挨拶。 私は最近知り合いとのコミュニケーションで触れた1つのトピックに関する記事を書くことにしました。 このトピックは、この関数の一般的な使用法を理解するほど使用することに関するものではありません。 実際、私は特にプログラマーでも開発者でもないので、私の意見はいわば普通の素人の意見です。



記事を書くには、私のような非専門家と通信し、さまざまなチュートリアルでLerp関数を使用して、人気のあるコードの動作に関する不一致を特定する必要があります。



Unity3DのLerp関数に興味があります。 より具体的には、Vector3DクラスのLerpメソッド。 ただし、Unityのすべての「レルペ」はほぼ同じように機能します。 そのため、UnityでLerpを使用する典型的な例は、多くのチュートリアルで取り上げられています。



... Vector3 destinationPoint; float smoothing; ... void Update() { // ""      transform.position = Vector3.Lerp (transform.position, destinationPoint, smoothing * Time.deltaTime); }
      
      





理論的には、このコードでは、スクリプトが属するオブジェクトは、 destinationPointに即座に転送されるのではなく、そこでスムーズに移動します。 コードは基本的にシンプルです。 ただし、その単純さには特定のニュアンスがあります。



最初の瞬間は、このコードがオブジェクトの位置を現在のオブジェクトからdestinationPointに線形補間すると言った人がいたときです



実際、Lerp関数は実際には単なる線形補間です。 しかし、これは関数そのものです。 ただし、前述のUpdateメソッドのコードアクションはそれほど明白ではありません。 適用すると、オブジェクトが正しい方向に動いており、プロセスの速度が低下していることが明らかです。 「真の」線形補間の場合、オブジェクトは動き始め、固定速度で動き、その後突然フリーズします。 これは、チュートリアルではなく、スクリプトの公式ヘルプ(具体的には、Lerpメソッドを使用)からLerpを使用するためのオプションを見ると、まさに起こります。 しかし、言及されたコードはこのように動作しません。



2番目の瞬間は、これがまったく機能しないはずだと別の人が言ったときでした。 Lerp関数は、0から1まで変化するパラメーター(従来はtと呼びましょう)を取る必要があります。したがって、ゼロでは、位置はパスの開始点に対応します。 団結が有限であるとき。 したがって、パラメーターが0から1までの値を実行すると、開始点から終了点への移動があります。 しかし、上記のコードでは、パラメーターtは実質的に変更されません! モーションの「滑らかさ」を設定するスムージング変数は固定されています。 deltaTimeはランダムな制限内で変化しますが、ほぼ同じレベルです。 フレームレートが一定の場合、一定になります。 したがって、パラメータtがまったく変化しないため、ポイントの位置は変化しないと想定できます。 そして、動きはありません。



繰り返しますが、現実はこれがそうではないことを示しています。 コードは機能し、オブジェクトは必要な場所に移動します。 スローダウンもあります。



なぜこれが起こっているのですか? 更新のたびに補間の開始点の位置が変わるためです。



簡単にするために、フレームレートは安定しており、スムージングパラメーターとフレーム長の積は0.25(1/4)であると想定しています。 開始点から最終点までパスdを移動する必要があるとします。



画像






の結果として



 Vector3.Lerp (transform.position, destinationPoint, 0.25);
      
      





最初から4分の1の距離にポイントを取得します。 この点がオブジェクトの新しい位置になり、次にUpdate()メソッドが呼び出されたときに、そこから踊ります。 これが問題です。なぜまだ機能するのか。



画像






新しい距離d 'は (オブジェクトが移動するにつれて)小さくなりました。 したがって、この距離の4分の1も短くなります。 その結果、オブジェクトは目的地により近く移動しますが、すでにより短い距離に移動します。



画像






次の更新時に-オブジェクトはさらに小さくなります。



これはすでに疑問です。なぜ線形補間を使用するのか、しかし非線形の動きが得られるのです。 オブジェクトが目的地に近づくほど、オブジェクトまでの距離は短くなりますが、Zenoのアポリアのオブジェクトのように、ステップは小さくなります。



実際、オブジェクトは宛先ポイントに逆指数関数的に移動し、常にそれに近づきますが、到達することはありません。 まあ、これは数学的な観点からです。 実用的な面では、選択したスケールに依存します。



このようなLerp関数の使用には、間違いなく生命権がありますが、明確にはなりません。 「線形補間」というフレーズを聞く人々は、しばしば異なる行動をとる。 さらに、補間を線形から別の補間に切り替えることができる興味深い機能が数多くあります。 それらは通常、パラメータtの変更に基づいています。 秘Theは、この例を使用すると、これらすべての開発が予想とはまったく異なる動作をすることです。 Unity3Dの開発者自身がそのようなコードの機能を理解しているが、そのようなニュアンスを説明せず、明らかに不必要な(彼らの意見では)情報をロードしたくないと信じています。



Lerp関数などは、多くの中間値(通常は開始から終了まで)を取得するために使用されます。 このコードでは、1つの特定のポイントを取得する必要があります。Update()を呼び出すたびに、パスのセグメントを特定の比率で分割するポイントの値を見つけます。



興味深い質問が聞こえましたが、私にはよくわかりませんでした。補間パラメーターの値は変わらないので、なぜdeltaTimeがまったくないのですか? まあ、実際には、Unityでの優れたコーディングの実践は、フレームレートの独立性を意味します。 もちろん、フレームレートが不安定なため、コードの動作の違いはTime.deltaTimeによる乗算では顕著ではありません。 しかし、事実は事実です。



私がすでに自問した別の質問:なぜFixedUpdate()メソッドでTime.deltaTimeを掛けるのですか? 結局のところ、開発者は、このメソッドの呼び出し間の経過時間が厳密に固定されていると主張しています(ほぼ...以下を参照)。 ただし、チュートリアルでは、前述と同様のコードがFixedUpdate()メソッドにもあります(たとえば、 こちら )。



いくつかの可能なオプションがあります。おそらく、このテンプレートに慣れているこのチュートリアルのプレゼンターは、ためらうことなく単純にそれを実行しました。 または、物理的な更新( FixedUpdate()の呼び出しの頻度が何らかの理由で変更された場合に、コード実行の結果が同一であることを保証しました。 または、単に「魔法の定数」を追加しないことを決めたが、同時に、Update()メソッドとFixedUpdate()メソッド間でコードの互換性(および移植性)を提供します。そうしないと、1番目と2番目のメソッドに個別のスムージングが必要になるためです。



一般に、これらの更新時間では、物事もスムーズに進みません。 FixedUpdate()には独自の変数fixedDeltaTimeがあり、名前によって判断して呼び出し間の時間を指定する必要があります...しかし、 FixedUpdate()呼び出しの頻度が固定されているため、開発者自身はFixedUpdate()およびUpdate()deltaTimeの使用を推奨-修正済みですが、あまりそうではありません。



いずれにせよ、結果。



Lerp関数は、実際には線形補間関数です。 ただし、人気のあるテンプレートでは、オブジェクトの動きと回転を直線的に補間しません。 コードは一定の簡潔さで異なりますが、線形補間の動作を変更するために確立された方法を適用しようとすると困難を引き起こします。 同時に、私が知る限り、このコードの効果はチュートリアルのどこにも説明されていないため、その動作について多くの人が混乱しています。



一般的に、 SmoothDamp関数を好みます。この関数は、追加の変数を格納する必要がありますが、より予測可能な動作をします(たとえば、特定の場所でのオブジェクトの「到着」の概算時間を設定できます)。



ご清聴ありがとうございました。



All Articles