Vector3で演算子 '> ='を使用できないのはなぜですか?


9

私は私がように参照する2つの位置の間を移動するための四角形を取得しようとしている_positionAとします_positionB。どちらもタイプVector3です。長方形はうまく動きます。ただし、到達_positionBしても、本来のように反対方向に移動することはありません。

コードをもう一度見てみました。オブジェクトが移動ifすると、コード内のステートメントは、rectsの位置がに等しいフレームを逃したという結論に達しました_positionB。rectsの位置がより大きいか等しい 場合は、コードを変更して方向を逆にすることにしました_positionB。私のコードは長すぎないので、以下に表示します。

using UnityEngine;
using System.Collections;

public class Rectangle : MonoBehaviour 
{
    private Vector3 _positionA = new Vector3(-0.97f, -4.28f); //Start position
    private Vector3 _positionB = new Vector3(11.87f, -4.28f); //End position
    private Transform _rect_tfm;
    private bool _atPosA = false, _atPosB = false;

    public Vector2 speed = new Vector2(1f, 0f);

    private void Start()
    {
        _rect_tfm = gameObject.GetComponent<Transform>();
        _rect_tfm.position = _positionA;
        _atPosA = true;
    }

    private void Update()
    {
        /*NOTE: Infinite loops can cause Unity to crash*/
        Move();
    }

    private void Move()
    {
        if (_atPosA)
        {
            _rect_tfm.Translate(speed * Time.deltaTime);

            if (_rect_tfm.position == _positionB)
            {
                _atPosA = false;
                _atPosB = true;
            }
        }

        if (_atPosB)
        {
            _rect_tfm.Translate(-speed * Time.deltaTime);

            if (_rect_tfm.position == _positionA)
            {
                _atPosA = true;
                _atPosB = false;
            }
        }    
    }
}

ただし、変更すると、次のエラーメッセージが表示されました。

演算子> =は、タイプVector3およびVector3のオペランドには適用できません。

これは2つの理由で私を混乱させます。まず、両方の値が同じデータ型です。次に、==2つの値に比較演算子()を使用すると、エラーなしで機能します。なぜ私は、演算子を使用することはできません>=Vector3の?


補足:2 およびBoolslikeの使用は避けてください。必然的に、両方を同期させておくと間違いを犯し、バグにつながります。すべての位置(A、B、おそらく将来は他の位置)を含むを作成し、それを使用することをお_atPosA_atPosBenum
勧め

5
>=どういう意味Vector3ですか?コンポーネントごとに比較しますか?それは完全な順序ではありません。使用を検討してくださいVector3.MoveTowards
rwols 2017年

4
これを考慮してください:var vec1 = new Vector3(1, 0, 0)var vec2 = new Vector3(0, 1 ,0)。あるvec1 >= vec2真または偽?
gronostaj

回答:


16

答えを簡単にするために、名前空間によって提供されるVector3カスタムです。カスタムまたはタイプを作成するときは、その演算子定義する必要があります。そのため、オペレーターにはデフォルトのロジックはありません。で指摘したようにエフゲニー・Vasilyev、私たちが直接チェックすることができるよう、理にかなっている、との値を。a は3つの別々の値で表されるため、それほど意味がありません。structUnityEngineclassstruct>=_rect_tfm.position == _positionBVector3.xVector3.yVector3.z_rect_tfm.position >= _positionBVector3

理論的Vector3には、クラスをオーバーロードして適切な演算子を含めることもできますが、それはかなり複雑に見えます。代わりに、適切なメソッドを使用してクラスを拡張する方が簡単です。そうは言っても、あなたがこのロジックを動きに使用するつもりのようです。そのため、このメソッドを使用する方がはるかに簡単であることがわかります。その場合は、以下をお読みください。Vector3Vector3.Lerp

への拡張メソッドの追加 Vector3

前述のように、<=または>=への適用Vector3は非論理的です。動きについては、おそらくVector3.Lerpメソッドについてさらに読みたいと思うでしょう。とはいえ<= =>、他の理由で算術を適用したい場合があるので、簡単な代替案を紹介します。

Vector3 <= Vector3またはのロジックを適用する代わりにVector3 >= Vector3Vector3クラスを拡張してisGreaterOrEqual(Vector3 other)およびのメソッドを含めることをお勧めしisLesserOrEqual(Vector3)ます。私たちはできる拡張メソッドを追加structたりclass、それらを宣言することによってstatic継承しないクラス。我々はまた、ターゲット含めるclassか、struct最初のパラメータとして、使用してthisキーワードを。私の例では、私はあなたがすべての3つの主要な値ことを保証することを意味することを前提とし(注ことxy及びz)されている全てそれぞれ、以上、またはより小さいか等しいです。必要に応じて、ここで独自のロジックを提供できます。

public static class ExtendingVector3
{
    public static bool IsGreaterOrEqual(this Vector3 local, Vector3 other)
    {
        if(local.x >= other.x && local.y >= other.y && local.z >= other.z)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public static bool IsLesserOrEqual(this Vector3 local, Vector3 other)
    {
        if(local.x <= other.x && local.y <= other.y && local.z <= other.z)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

Vector3クラスからこれらのメソッドを呼び出そうとすると、はメソッドの呼び出し元localVector3インスタンスを表します。メソッドは次のとおりstaticです。拡張メソッドはでなければなりませんstatic、インスタンスから呼び出す必要があります。上記の拡張メソッドを指定すると、Vector3タイプに直接適用できるようになります。

Vector3 left;
Vector3 right;

// Is left >= right?
bool isGreaterOrEqual = left.IsGreaterOrEqual(right);

// Is left <= right?
bool isLesserOrEqual = left.IsLesserOrEqual(right);

Vector3と一緒に移動Vector3.Lerp

メソッドを呼び出すとVector3.Lerp 2つのVector3値の間の正確な位置を特定の時間に特定できます。この方法の追加の利点はVector3がターゲットをオーバーシュートしないことです。Vector3.Lerp3つのパラメーターを取ります。開始位置、終了位置、および0と1の間の値として表される現在位置。結果の位置をとして出力します。これをVector3現在位置として直接設定できます。

あなたの問題を解決するには、を使用Vector3.Lerpしてに移動することをお勧めしますtargetPositionMoveUpdateでメソッドを呼び出した後、ターゲットに到達したかどうかを確認できます。Lerp.Vector3ではないオーバーシュート、そうtransform.position == targetPosition確実になります。これで、位置を確認し、それに応じてに変更targetPositionleftPositionたりrightPosition、逆に移動したりできます。

public Vector3 leftPosition, rightPosition;
public float speed;
public Vector3 targetPosition;

private void Awake()
{
    targetPosition = rightPosition;
}

private void Update()
{
    Move();

    if(transform.position == targetPosition)
    {
        // We have arrived at our intended position. Move towards the other position.
        if(targetPosition == rightPosition)
        {
            // We were moving to the right; time to move to the left.
            targetPosition = leftPosition;
        }
        else
        {
            // We were moving to the left; time to move to the right.
            targetPosition = rightPosition;
        }
    }
}

private void Move()
{
    // First, we need to find out the total distance we intend to move.
    float distance = Vector3.Distance(transform.position, targetPosition);

    // Next, we need to find out how far we intend to move.
    float movement = speed * Time.deltaTime;

    // We find the increment by simply dividing movement by distance.
    // This will give us a decimal value. If the decimal is greater than
    // 1, we are moving more than the remaining distance. Lerp 
    // caps this number at 1, which in turn, returns the end position.
    float increment = movement / distance;

    // Lerp gives us the absolute position, so we pass it straight into our transform.
    transform.position = Vector3.Lerp(transform.position, targetPosition, increment);
}

これは、次のアニメーションで示されています。青い立方体をVector3.LerpUnclampedで翻訳します。これにより、チェックされていない単純な翻訳と同様の結果が得られます。を使用して赤い立方体を翻訳しVector3.Lerpます。オフのままにすると、青い立方体が忘却の方向に移動します。赤い立方体は、私が意図したとおりに停止します。このタイプの動きの詳細については、Stack Overflowのドキュメントをご覧ください。

オフのままにすると、青い立方体が忘却の方向に移動します。 赤い立方体は、私が意図したとおりに停止します。


うわー、あなたは本当に余分のマイルを行きました、本当にありがとう!
ハビエルマルティネス

27

タイプの定義>=Vector3意味がありません。あるベクトルが別のベクトルよりも大きいかどうかは何によって決まりますか?それらの大きさまたはそれらの個々のx、y、z成分?

ベクトルは、大きさと方向です。それでは、どの方向がより大きいかを決定するものは何ですか?

マグニチュードを比較する必要がある場合は、使用できますsqrMagnitude

この場合、Vector3オーバーライド==して、異なるコンポーネントを比較して、それらが同じかどうかを確認します。(しきい値内)

これは、2つのベクトルを使用して乗算すること*ができない同じ理由です。それを行う数学的な方法はありません。一部の人々*はドット積を使用していますが、それは不明確なAPI設計です。


Unity Vector3はであるstructため、参照比較に関する段落は正しくありません。
31eee384

これは、可能性が各軸上の一個のベクトルの位置の各々が唯一のグループとして、2つの整数を比較すると同様他の、より大きい意味します。アプリケーションでは、各プロパティを個別に比較するよりも少し制限がありますが、少なくとも使用することはできます。
ピシス

これはJavaではありません。参照演算子は、equals演算子が定義されている構造体またはクラスでは当てはまりません
Gustavo Maciel

その部分を削除するように答えを変更しました。ただし、C#はある時点ではJavaでした。私の知る限り、クラスのコアは同じように機能し、==が上書きされない場合、Javaでの動作とまったく同じように動作します。
Evgeny Vasilyev

2

これは古い質問ですが、専門用語を少なくするために、Vector3は3つの浮動小数点値-x、y、zの「コンテナ」です。

2つのVector3のx値の比較など、それらは単なる数値なので、個々の値を比較できます。

ただし、2つを比較するために使用できる単一の値がないため、Vector3全体を別のVector3と比較することはできません。


0

Vector3クラスへの拡張メソッドの追加に関して、Gnemlockが投稿したものに追加するだけです。浮動小数点計算の処理方法が原因で、2つの浮動小数点値の間==で特定の比較演算子(<=>=)を使用すると、Unity(および他のゲームエンジンでは確実に問題)に問題があります。Mathf.Approximately代わりに使用する必要があるため、次の拡張メソッドを追加して、2つのベクトルが互いに> =または<=であるかどうかを確認できます。

using UnityEngine;

public static class ExtendingVector3
{
    public static bool IsGreaterOrEqual(this Vector3 local, Vector3 other)
    {
        bool xCond = local.x > other.x || Mathf.Approximately(local.x, other.x);
        bool yCond = local.y > other.y || Mathf.Approximately(local.y, other.y);
        bool zCond = local.z > other.z || Mathf.Approximately(local.z, other.z);

        if(xCond && yCond && zCond)
            return true;

        return false;
    }

    public static bool IsLesserOrEqual(this Vector3 local, Vector3 other)
    {
        bool xCond = local.x < other.x || Mathf.Approximately(local.x, other.x);
        bool yCond = local.y < other.y || Mathf.Approximately(local.y, other.y);
        bool zCond = local.z < other.z || Mathf.Approximately(local.z, other.z);

        if(xCond && yCond && zCond)
            return true;

        return false;
    }
}

値が少しアンダーシュートしたときに≤&≥テストの両方がtrueを返すようにする場合は、これを使用できます。ただし、通常、ほぼ等しいチェックは、単一の特定の値と等しいかどうかをテストする場合にのみ適用されます。これは、チェックを1つのポイント(見落としやすい)からどちらかの側の小さなエラーマージンに「広げます」。≤と≥にはすでにエラーのマージンが組み込まれています。ローエンドまたはハイエンドへのオーバーシュートはそれぞれキャプチャーされるため、計算のわずかな偏差により、望ましいケースを見逃す可能性ははるかに低くなります。
DMGregory

0

この質問を解釈する別の方法を提案したいと思います。このようなコードパターン:

if(myPosition >= patrolEnd || myPosition <= patrolStart)
    TurnAround();

基本的に>=/ <=演算子を使用しようとしているのは、「左側に到達したか、右側に合格したか」です。テスト。

>=/ <=を使用して「到達または通過」を意味するのは、私の位置がフロートだけの場合、1次元の意味で理にかなっています。

if(myX >= rightEnd || myX <= leftEnd)
    TurnAround();

しかし、3D空間では、どちら側が「高/遠」でどちらが「低/近」かを決定するために測定する1本の線はありません。たとえば、ポイント間をパトロールしようとしている可能性があります

patrolStart = (-10,  0,  5)
patrolEnd   = ( 10,  0, -5)

したがってpatrolStart <= myPosition <= patrolEnd、X軸に期待しますがpatrolEnd <= myPosition <= patrolStart、Z軸に期待します。「到達または通過」演算子は軸ごとに異なるため、しきい値を通過させるという概念と単純な不等式チェックの間に明確なマッピングはありません。

しかし、3D空間で1行だけを選択して、>=/ <=を、選択したこの行に沿った単一のフロートケースのように動作させる方法があります。

// Here we select the directed line from our start point to our end point.
Vector3 axis = patrolEnd - patrolStart;

// We can make a single number representing the "low" end of our range
// by taking the dot product of this axis with our start point.
float low = Vector3.Dot(axis, patrolStart);

// And the "high" end by dotting this axis with the end point.
float high = Vector3.Dot(axis, patrolEnd);

// And our progress between is the dot product of the axis with our position.
float progress = Vector3.Dot(axis, myPosition);

// Now we can use our turn-around logic just like we were in the 1D case:
if(progress >= high || progress <= low)
    TurnAround();

おまけとして、軸ベクトルを使用する前に正規化すると、すべての内積が距離を表すため、移動軸に沿って、どちらの端からどれだけ離れているかを正確に測定できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.