Mengapa saya tidak bisa menggunakan operator '> =' dengan Vector3s?

9

Saya mencoba untuk mendapatkan persegi panjang untuk bergerak di antara dua posisi yang saya sebut sebagai _positionAdan _positionB. Keduanya bertipe Vector3. Persegi panjang bergerak dengan baik. Namun, ketika mencapai _positionBitu tidak bergerak ke arah yang berlawanan, seperti seharusnya.

Saya kembali ke kode untuk melihatnya. Saya sampai pada kesimpulan bahwa ketika objek bergerak, ifpernyataan dalam kode melewatkan bingkai di mana posisi rects sama dengan _positionB. Saya memutuskan untuk memodifikasi kode untuk membalikkan arah jika posisi rect lebih besar atau sama dengan _positionB . Kode saya tidak terlalu panjang, jadi saya akan menampilkannya di bawah:

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

Namun ketika saya mengubahnya, itu memperingatkan saya akan pesan kesalahan berikut:

Operator> = tidak dapat diterapkan pada operan tipe Vector3 dan Vector3.

Ini membingungkan saya karena dua alasan; pertama, kedua nilai memiliki tipe data yang sama. Kedua, menggunakan operator perbandingan ( ==) pada dua nilai berfungsi tanpa kesalahan. Mengapa saya tidak bisa menggunakan operator >=dengan Vector3s?

Javier Martinez
sumber
Catatan: Anda harus menghindari penggunaan 2 Boolslike _atPosAdan _atPosB. Tidak dapat dihindari, Anda akan membuat kesalahan dengan menyinkronkan keduanya, dan itu akan menyebabkan bug. Lebih baik membuat yang enumberisi semua posisi (A, B, mungkin yang lain di masa depan), dan menggunakannya
Alexander - Reinstate Monica
5
Apa >=artinya bagi seorang Vector3? Bandingkan komponen-bijaksana? Itu tidak akan menjadi pemesanan total. Pertimbangkan untuk menggunakanVector3.MoveTowards
rwols
4
Pertimbangkan ini: var vec1 = new Vector3(1, 0, 0)dan var vec2 = new Vector3(0, 1 ,0). Apakah ini vec1 >= vec2benar atau salah?
gronostaj

Jawaban:

16

Untuk menyederhanakan jawaban, Vector3adalah kebiasaan yang structdisediakan oleh UnityEnginenamespace. Saat kita membuat custom classatau structtipe, kita juga harus mendefinisikan operatornya . Dengan demikian, tidak ada logika default untuk >=operator. Seperti yang ditunjukkan oleh Evgeny Vasilyev , _rect_tfm.position == _positionBmasuk akal, karena kita dapat langsung memeriksa Vector3.x, Vector3.ydan Vector3.znilai - nilai. _rect_tfm.position >= _positionBtidak masuk akal, karena fakta bahwa a Vector3diwakili oleh tiga nilai yang terpisah.

Kita bisa membebani Vector3kelas untuk memuat operator yang sesuai secara teori , tapi itu agak rumit. Sebaliknya, akan lebih mudah untuk hanya memperpanjang dengan Vector3kelas dengan sesuai metode . Yang sedang berkata, tampaknya Anda bermaksud menggunakan logika ini untuk gerakan. Dengan demikian, Anda mungkin merasa lebih mudah menggunakan Vector3.Lerpmetode ini; jika demikian, baca lebih lanjut di bawah ini.

Menambahkan metode ekstensi ke Vector3

Seperti disebutkan sebelumnya, menerapkan <=atau >=ke Vector3sering tidak masuk akal. Untuk gerakan, Anda mungkin ingin membaca lebih lanjut untuk Vector3.Lerpmetode ini. Yang mengatakan, Anda mungkin ingin menerapkan <= =>aritmatika karena alasan lain, jadi saya akan memberi Anda alternatif yang mudah.

Alih-alih menerapkan logika Vector3 <= Vector3atau Vector3 >= Vector3, saya mengusulkan memperluas Vector3kelas untuk memasukkan metode untuk isGreaterOrEqual(Vector3 other)dan isLesserOrEqual(Vector3). Kami dapat menambahkan metode ekstensi ke structatau classdengan mendeklarasikannya di statickelas yang tidak diwariskan. Kami juga memasukkan target classatau structsebagai parameter pertama, menggunakan thiskata kunci. Perhatikan bahwa dalam contoh saya, saya berasumsi bahwa Anda bermaksud memastikan bahwa ketiga nilai utama ( x, ydan z) semuanya lebih besar atau sama, atau lebih rendah atau sama, masing-masing. Anda dapat memberikan logika Anda sendiri, di sini, sesuai kebutuhan Anda.

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

Ketika kita mencoba memanggil metode ini dari Vector3kelas, localakan mewakili Vector3contoh dari mana kita memanggil metode tersebut. Anda akan mencatat bahwa metodenya adalah static; metode penyuluhan harus menjadi static, tetapi Anda masih harus memanggil mereka dari sebuah contoh. Dengan adanya metode ekstensi di atas, Anda sekarang dapat menerapkannya langsung ke Vector3jenis Anda .

Vector3 left;
Vector3 right;

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

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

Pindah Vector3denganVector3.Lerp

Memanggil para Vector3.Lerpmetode memungkinkan kita untuk menentukan posisi yang tepat antara dua Vector3nilai pada waktu tertentu. Manfaat tambahan dari metode ini adalah bahwa yang Vector3tidak akan overshoot target . Vector3.Lerpmembutuhkan tiga parameter; posisi awal, posisi akhir, dan posisi saat ini direpresentasikan sebagai nilai antara 0 dan 1. Ini menampilkan posisi yang dihasilkan sebagai a Vector3, yang dapat langsung kita atur sebagai posisi saat ini.

Memecahkan masalah Anda, saya usulkan menggunakan Vector3.Lerppindah ke a targetPosition. Setelah memanggil Movemetode di masing-masing Update, kita dapat memeriksa apakah kita telah mencapai target tersebut; tidakLerp.Vector3 akan melampaui, jadi menjadi dapat diandalkan. Kita sekarang dapat memeriksa posisi, dan mengubah ke atau untuk membalikkan gerakan.transform.position == targetPositiontargetPositionleftPositionrightPosition

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

Anda dapat melihat ini ditunjukkan dalam animasi berikut. Saya menerjemahkan cube biru Vector3.LerpUnclamped, yang memberi kami hasil yang mirip dengan terjemahan sederhana yang tidak dicentang. Saya menerjemahkan kubus merah menggunakan Vector3.Lerp. Bila tidak dicentang, kubus biru bergerak ke arah terlupakan; sementara kubus merah berhenti tepat di tempat saya bermaksud. Anda dapat membaca lebih lanjut tentang jenis gerakan ini di dokumentasi Stack Overflow .

Bila tidak dicentang, kubus biru bergerak ke arah terlupakan;  sementara kubus merah berhenti tepat di tempat saya bermaksud.

Gnemlock
sumber
Wow, Anda benar-benar berusaha keras, terima kasih banyak!
Javier Martinez
27

Mendefinisikan >=suatu Vector3tipe tidak masuk akal. Apa yang menentukan jika satu vektor lebih besar dari yang lain? Besarnya atau masing-masing komponen x, y, z mereka?

Vektor adalah besar & arah. Jadi apa yang menentukan arah mana yang lebih besar?

Jika Anda perlu membandingkan besaran yang dapat Anda gunakan sqrMagnitude.

Dalam hal ini Vector3mengganti ==untuk hanya membandingkan komponen yang berbeda untuk melihat apakah mereka sama. (dalam ambang batas)

Ini adalah alasan yang sama mengalikan dua vektor menggunakan *tidak mungkin. Tidak ada cara matematis untuk melakukannya. Beberapa orang menggunakan *untuk produk titik, tetapi itu adalah desain API yang tidak jelas.

Evgeny Vasilyev
sumber
Unity's Vector3a struct, jadi paragraf tentang perbandingan referensi kurang tepat.
31eee384
Ini bisa berarti masing-masing posisi satu vektor pada setiap sumbu lebih besar dari yang lain, mirip dengan membandingkan 2 bilangan bulat, hanya sebagai sebuah kelompok. Ini sedikit lebih terbatas dalam aplikasi dibandingkan membandingkan setiap properti secara individual, tetapi setidaknya masih bisa digunakan.
Pysis
Ini bukan Java. Perbandingan referensi tidak benar dalam struct atau kelas di mana operator sama didefinisikan
Gustavo Maciel
Saya mengubah jawaban saya untuk menghapus bagian itu. Namun C # pada satu titik Java. Sejauh yang saya tahu inti dari kelas masih bekerja sama dan jika == tidak lebih dari tertulis itu berperilaku persis seperti di java.
Evgeny Vasilyev
2

Ini adalah pertanyaan lama tetapi untuk memasukkan ke dalam istilah yang kurang teknis, Vector3 adalah "wadah" untuk 3 nilai float - x, y, z.

Anda dapat membandingkan nilai individual, seperti membandingkan nilai x dari dua Vector3s, karena mereka hanya angka.

Namun, seluruh Vector3 tidak dapat dibandingkan dengan Vector3 lain karena tidak ada nilai tunggal yang dapat digunakan untuk membandingkan keduanya.

Dez Boyle
sumber
0

Hanya menambahkan apa yang diposting Gnemlock , tentang menambahkan metode ekstensi ke kelas Vector3. Ada masalah di Unity (dan saya yakin mesin game lainnya) saat menggunakan operator perbandingan tertentu ( ==, <=dan >=) antara dua nilai float, karena cara perhitungan titik mengambang ditangani. Mathf.Approximatelyharus digunakan sebagai gantinya, sehingga metode ekstensi berikut dapat ditambahkan untuk memeriksa apakah dua vektor saling> = atau <=:

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;
    }
}
Anthony
sumber
Anda tentu dapat menggunakan ini, jika Anda ingin kedua tes ≤ & ≥ kembali benar ketika nilainya undershoots sedikit. Namun biasanya, kami menerapkan pengecekan yang kira-kira sama hanya ketika menguji kesetaraan untuk satu nilai tertentu. Ini "melebarkan" cek dari satu titik (mudah dilewatkan) ke margin kesalahan kecil di kedua sisi. ≤ dan ≥ sudah memiliki margin kesalahan bawaan: setiap overshoot ke low atau high end ditangkap, jadi mereka sudah jauh lebih rentan kehilangan kasus yang diinginkan karena penyimpangan kecil dalam perhitungan.
DMGregory
0

Saya ingin mengusulkan cara berbeda untuk menafsirkan pertanyaan ini. Pola kode seperti ini:

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

pada dasarnya mencoba menggunakan >=/ <=operator sebagai "telah sisi kiri mencapai atau melewati sisi kanan?" tes.

Menggunakan >=/ <=berarti "mencapai atau melewati" masuk akal dalam arti satu dimensi, jika posisi saya hanyalah pelampung:

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

Namun dalam ruang 3D kami tidak memiliki satu garis untuk diukur, untuk menentukan sisi mana yang "tinggi / jauh" dan sisi mana yang "rendah / dekat". Misalnya, kita bisa mencoba berpatroli di antara titik-titik

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

Jadi sekarang kita berharap patrolStart <= myPosition <= patrolEndpada sumbu X, tetapi patrolEnd <= myPosition <= patrolStartpada sumbu Z. Operator kami "mencapai atau melewati" berbeda dari satu sumbu ke yang lain, sehingga tidak ada lagi pemetaan yang jelas antara konsep kami melewati ambang batas dan pemeriksaan ketimpangan sederhana.

Tapi, ada cara kita dapat memilih hanya satu baris dalam ruang 3D, dan menjadikan >=/ <=berperilaku seperti kotak pelampung tunggal di sepanjang garis yang kita pilih:

// 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();

Sebagai bonus, jika Anda menormalkan vektor sumbu sebelum menggunakannya, maka semua produk titik mewakili jarak, sehingga Anda dapat mengukur dengan tepat seberapa jauh Anda dari kedua ujungnya, di sepanjang sumbu perjalanan.

DMGregory
sumber