ทำไมฉันไม่สามารถใช้โอเปอเรเตอร์ '> =' กับ Vector3s ได้


9

ฉันพยายามที่จะได้รับรูปสี่เหลี่ยมผืนผ้าย้ายไปมาระหว่างสองตำแหน่งที่ผมพูดถึงและ_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

เรื่องนี้ทำให้ฉันสับสนด้วยเหตุผลสองประการ ก่อนค่าทั้งสองเป็นประเภทข้อมูลเดียวกัน ประการที่สองการใช้ตัวดำเนินการเปรียบเทียบ ( ==) กับทั้งสองค่านั้นทำงานได้โดยไม่มีข้อผิดพลาด ทำไมฉันไม่สามารถใช้โอเปอเรเตอร์>=กับVector3s ได้?


บันทึก Side: คุณควรหลีกเลี่ยงการใช้ 2 Boolsชอบและ_atPosA _atPosBอย่างหลีกเลี่ยงไม่ได้คุณจะทำผิดพลาดทำให้พวกเขาทั้งคู่ซิงค์และมันจะนำไปสู่ข้อบกพร่อง มันจะดีกว่าที่จะทำให้enumมีตำแหน่งทั้งหมด (A, B, บางทีคนอื่น ๆ ในอนาคต) และการใช้ที่
อเล็กซานเด - Reinstate Monica

5
สิ่งที่ควรมีความ>=หมายสำหรับVector3? เปรียบเทียบส่วนประกอบฉลาด? นั่นจะไม่เป็นการสั่งซื้อทั้งหมด พิจารณาใช้Vector3.MoveTowards
rwols

4
พิจารณานี้และvar vec1 = new Vector3(1, 0, 0) var vec2 = new Vector3(0, 1 ,0)เป็นvec1 >= vec2จริงหรือเท็จ?
gronostaj

คำตอบ:


16

เพื่อลดความซับซ้อนของคำตอบVector3เป็นแบบกำหนดเองที่กำหนดstructโดยUnityEngineเนมสเปซ เมื่อเราสร้างที่กำหนดเองclassหรือstructประเภทที่เราจะต้องยังกำหนดของผู้ประกอบการ ดังนั้นจึงไม่มีตรรกะเริ่มต้นสำหรับ>=ผู้ประกอบการ ในฐานะที่เป็นแหลมออกโดยEvgeny Vasilyev , _rect_tfm.position == _positionBทำให้ความรู้สึกที่เราโดยตรงสามารถตรวจสอบVector3.x, Vector3.yและVector3.zค่า _rect_tfm.position >= _positionBไม่สมเหตุสมผลเท่าที่ควรเนื่องจาก a Vector3ถูกแทนด้วยค่าที่ต่างกันสามค่า

เราสามารถโอเวอร์Vector3คลาสเพื่อให้มีตัวดำเนินการที่เหมาะสมในทางทฤษฎีแต่ดูเหมือนจะค่อนข้างซับซ้อน แต่มันจะง่ายขึ้นเพียงเพื่อขยายVector3ระดับที่มีความเหมาะสมวิธี ที่ถูกกล่าวว่าดูเหมือนว่าคุณตั้งใจที่จะใช้ตรรกะนี้สำหรับการเคลื่อนไหว คุณอาจพบว่าการใช้Vector3.Lerpวิธีนี้ง่ายกว่ามาก ถ้าเป็นเช่นนั้นอ่านเพิ่มเติมด้านล่าง

การเพิ่มวิธีการขยายไปยัง Vector3

ดังที่ได้กล่าวไว้ก่อนหน้าการใช้<=หรือ>=a Vector3มักจะไร้เหตุผล สำหรับการเคลื่อนไหวคุณอาจต้องการอ่านVector3.Lerpวิธีการเพิ่มเติม ที่กล่าวว่าคุณอาจต้องการใช้<= =>เลขคณิตด้วยเหตุผลอื่นดังนั้นฉันจะให้ทางเลือกอื่นแก่คุณง่าย ๆ

แทนการใช้ตรรกะของVector3 <= Vector3หรือVector3 >= Vector3ผมเสนอการขยายVector3ชั้นเรียนรวมถึงวิธีการและisGreaterOrEqual(Vector3 other) isLesserOrEqual(Vector3)เราสามารถเพิ่มวิธีการขยายไปยังstructหรือclassโดยการประกาศในstaticชั้นเรียนที่ไม่ได้รับมรดก นอกจากนี้เรายังรวมเป้าหมายclassหรือstructเป็นพารามิเตอร์แรกโดยใช้thisคำหลัก ทราบว่าในตัวอย่างของฉันฉันคิดว่าคุณหมายถึงเพื่อให้มั่นใจว่าทั้งสามค่าหลัก ( x, yและ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ชั้นเรียนlocalจะเป็นตัวแทนของVector3อินสแตนซ์ที่เรากำลังเรียกวิธีการจาก คุณจะทราบว่าวิธีการคือ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เมธอดช่วยให้เราสามารถกำหนดตำแหน่งที่แน่นอนระหว่างVector3ค่าสองค่าในเวลาที่กำหนด สิทธิประโยชน์เพิ่มเติมของวิธีนี้ก็คือว่าจะไม่แหกเป้าหมาย ใช้สามพารามิเตอร์ ตำแหน่งเริ่มต้นตำแหน่งสิ้นสุดและตำแหน่งปัจจุบันแสดงเป็นค่าระหว่าง 0 ถึง 1 โดยจะส่งออกตำแหน่งผลลัพธ์เป็น a ซึ่งเราสามารถตั้งค่าโดยตรงเป็นตำแหน่งปัจจุบันVector3Vector3.LerpVector3

การแก้ปัญหาของคุณผมขอเสนอการใช้ที่จะย้ายไปVector3.Lerp targetPositionหลังจากเรียกMoveวิธีการในแต่ละครั้งUpdateเราสามารถตรวจสอบว่าเราไปถึงเป้าหมายดังกล่าวหรือไม่ Lerp.Vector3จะไม่ทำเลยจึงtransform.position == targetPositionกลายเป็นที่เชื่อถือได้ ตอนนี้เราสามารถตรวจสอบตำแหน่งและเปลี่ยนtargetPositionไปleftPositionหรือ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ผมแปลก้อนสีแดงใช้ ไม่ได้ตรวจสอบเลยก้อนสีฟ้าเคลื่อนย้ายไปสู่การให้อภัย ในขณะที่ลูกบาศก์สีแดงหยุดตรงที่ฉันตั้งใจจะ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับประเภทของการเคลื่อนไหวนี้ในเอกสารกองมากเกิน

ไม่ได้ตรวจสอบเลยก้อนสีฟ้าเคลื่อนย้ายไปสู่การให้อภัย  ในขณะที่ลูกบาศก์สีแดงหยุดตรงที่ฉันตั้งใจจะ


ว้าวคุณไปได้เกินไมล์จริง ๆ ขอบคุณมาก!
Javier Martinez

27

กำหนด>=สำหรับVector3ประเภททำให้รู้สึกไม่ อะไรเป็นตัวกำหนดว่าเวกเตอร์หนึ่งมีค่ามากกว่าอีกเวกเตอร์หนึ่งหรือไม่ ขนาดหรือองค์ประกอบ x, y, z ของแต่ละคน?

เวกเตอร์เป็นขนาด & ทิศทาง ดังนั้นสิ่งที่กำหนดทิศทางที่ยิ่งใหญ่กว่าคืออะไร?

หากคุณต้องการเปรียบเทียบขนาดที่คุณสามารถsqrMagnitudeใช้ได้

ในกรณีนี้Vector3จะแทนที่==การเปรียบเทียบส่วนประกอบต่าง ๆ เพื่อดูว่าเหมือนกันหรือไม่ (ภายในเกณฑ์)

นี่เป็นเหตุผลเดียวกันที่คูณเวกเตอร์สองตัวที่ใช้*ไม่ได้ ไม่มีวิธีทางคณิตศาสตร์ในการทำมัน บางคนใช้*สำหรับผลิตภัณฑ์ dot แต่นั่นเป็นการออกแบบ API ที่ไม่ชัดเจน


Unity's Vector3เป็น a structดังนั้นย่อหน้าเกี่ยวกับการเปรียบเทียบการอ้างอิงไม่ถูกต้องนัก
31eee384

มันอาจหมายถึงตำแหน่งของเวกเตอร์หนึ่งตำแหน่งในแต่ละแกนนั้นมากกว่าตำแหน่งอื่น ๆ คล้ายกับการเปรียบเทียบจำนวนเต็ม 2 จำนวนเท่านั้นในฐานะกลุ่ม แอปพลิเคชั่นมีข้อ จำกัด มากขึ้นเล็กน้อยเมื่อเทียบกับการเปรียบเทียบคุณสมบัติแต่ละรายการแยกกัน แต่ยังสามารถใช้ได้อย่างน้อย
Pysis

นี่ไม่ใช่จาวา การเปรียบเทียบการอ้างอิงไม่เป็นความจริงใน structs หรือคลาสที่กำหนดตัวดำเนินการเท่ากับ
Gustavo Maciel

ฉันแก้ไขคำตอบเพื่อลบส่วนนั้น อย่างไรก็ตาม C # อยู่ที่จุดหนึ่ง Java เท่าที่ฉันรู้ว่าแก่นของคลาสยังคงทำงานเหมือนเดิมและถ้า == ไม่ถูกเขียนจนเกินไปก็จะทำงานเหมือนในภาษาจาวา
Evgeny Vasilyev

2

นี่เป็นคำถามเก่า แต่เพื่อให้คำศัพท์ทางเทคนิคน้อยลง Vector3 เป็น "คอนเทนเนอร์" สำหรับค่าทศนิยม 3 ค่าคือ x, y, z

คุณสามารถเปรียบเทียบค่าแต่ละค่าเช่นการเปรียบเทียบค่า x ของ Vector3 สองอันเพราะมันเป็นเพียงตัวเลข

อย่างไรก็ตาม Vector3 ทั้งหมดไม่สามารถเปรียบเทียบกับ Vector3 อื่นได้เนื่องจากไม่มีค่าเดียวที่สามารถใช้ในการเปรียบเทียบทั้งสองได้


0

เพียงเพิ่มสิ่งที่Gnemlockโพสต์เกี่ยวกับการเพิ่มวิธีการขยายไปยังคลาส Vector3 มีปัญหาในความสามัคคีเป็น (และผมมั่นใจว่าเครื่องเกมอื่น ๆ ) เมื่อใช้ดำเนินการเปรียบเทียบบางอย่าง ( ==, <=และ>=) ระหว่างสองค่าลอยเนื่องจากวิธีการคำนวณจุดลอยจะถูกจัดการ Mathf.Approximatelyควรใช้แทนดังนั้นจึงสามารถเพิ่มวิธีการขยายต่อไปนี้เพื่อตรวจสอบว่ามีเวกเตอร์สองตัวคือ> = หรือ <= ต่อกัน:

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;
    }
}

คุณสามารถใช้สิ่งนี้ได้อย่างแน่นอนหากคุณต้องการให้การทดสอบทั้ง≤ & ≥คืนจริงเมื่อค่าต่ำกว่าเล็กน้อย แม้ว่าโดยทั่วไปแล้วเราจะใช้การตรวจสอบที่เท่ากันโดยประมาณเท่านั้นเมื่อทำการทดสอบเพื่อความเท่าเทียมกับค่าเฉพาะหนึ่งค่า มัน "ขยาย" การตรวจสอบจากจุดเดียว (ง่ายต่อการพลาด) เพื่อข้อผิดพลาดเล็ก ๆ น้อย ๆ ในด้านใดด้านหนึ่ง ≤และ have มีข้อผิดพลาดในตัวอยู่แล้ว: การโอเวอร์เฮดไปยังจุดสูงสุดหรือต่ำสุดตามลำดับจะได้รับการบันทึกดังนั้นพวกเขาจึงมีความเสี่ยงที่จะพลาดกรณีที่ต้องการเนื่องจากความเบี่ยงเบนเล็กน้อยในการคำนวณ
DMGregory

0

ฉันต้องการเสนอวิธีที่แตกต่างในการตีความคำถามนี้ รูปแบบรหัสเช่นนี้:

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

พยายามใช้ตัวดำเนินการ>=/ โดยพื้นฐาน<=ว่า"ด้านซ้ายมาถึงหรือผ่านทางด้านขวาแล้ว" การทดสอบ

ใช้>=/ <=เพื่อหมายถึง "ถึงหรือผ่าน" ทำให้รู้สึกในแง่หนึ่งมิติถ้าตำแหน่งของฉันเป็นเพียงลอย:

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

แต่ในพื้นที่ 3 มิติเราไม่มีเส้นวัดใด ๆ เพื่อพิจารณาว่าด้านใดเป็น "สูง / ไกล" และด้านใดเป็น "ต่ำ / ใกล้" ตัวอย่างเช่นเราอาจพยายามลาดตระเวนระหว่างจุดต่างๆ

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

ตอนนี้เราคาดว่าpatrolStart <= myPosition <= patrolEndบนแกน X แต่patrolEnd <= myPosition <= patrolStartบนแกน Z ผู้ประกอบการ "ถึงหรือผ่าน" ของเรานั้นแตกต่างจากแกนหนึ่งไปยังอีกแกนดังนั้นจึงไม่มีการแมปที่ชัดเจนระหว่างแนวคิดของเราในการผ่านเกณฑ์และการตรวจสอบความไม่เท่าเทียมกันอย่างง่าย

แต่มีวิธีที่เราสามารถเลือกออกเพียงหนึ่งบรรทัดในพื้นที่ 3 มิติและทำให้>=/ <=ประพฤติของเราเหมือนเคสลอยเดี่ยวตามแนวนี้ที่เราเลือก:

// 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.