Tại sao tôi không thể sử dụng toán tử '> =' với Vector3?


9

Tôi đang cố gắng để có được một hình chữ nhật để di chuyển giữa hai vị trí mà tôi gọi là _positionA_positionB. Cả hai đều thuộc loại Vector3. Các hình chữ nhật di chuyển tốt. Tuy nhiên, khi nó đạt đến _positionBnó không di chuyển theo hướng ngược lại, giống như nó nên.

Tôi quay lại mã để xem. Tôi đi đến kết luận rằng khi đối tượng di chuyển, các ifcâu lệnh trong mã đã bỏ lỡ khung trong đó vị trí của rects bằng _positionB. Tôi quyết định sửa đổi mã theo hướng ngược lại nếu vị trí của rects lớn hơn hoặc bằng _positionB . Mã của tôi không quá dài, vì vậy tôi sẽ hiển thị nó bên dưới:

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

Tuy nhiên, khi tôi thay đổi, nó đã cảnh báo tôi về thông báo lỗi sau:

Toán tử> = không thể được áp dụng cho toán hạng loại Vector3 và Vector3.

Điều này làm tôi bối rối vì hai lý do; đầu tiên, cả hai giá trị có cùng kiểu dữ liệu. Thứ hai, sử dụng toán tử so sánh ( ==) trên hai giá trị hoạt động không có lỗi. Tại sao tôi không thể sử dụng toán tử >=với Vector3s?


Lưu ý bên: bạn nên tránh sử dụng 2 Boolslike _atPosA_atPosB. Chắc chắn, bạn sẽ phạm sai lầm khi giữ cả hai đồng bộ hóa và điều đó sẽ dẫn đến lỗi. Tốt hơn là tạo một enumvị trí chứa tất cả các vị trí (A, B, có thể là các vị trí khác trong tương lai) và sử dụng vị trí đó
Alexander - Tái lập Monica

5
>=nghĩa là gì cho một Vector3? So sánh thành phần khôn ngoan? Đó sẽ không phải là một đơn đặt hàng tổng. Cân nhắc sử dụngVector3.MoveTowards
rwols

4
Xem xét điều này: var vec1 = new Vector3(1, 0, 0)var vec2 = new Vector3(0, 1 ,0). Là vec1 >= vec2đúng hay sai?
gronostaj

Câu trả lời:


16

Để đơn giản hóa câu trả lời, Vector3là một tùy chỉnh structđược cung cấp bởi UnityEnginekhông gian tên. Khi chúng ta tạo tùy chỉnh classhoặc structloại, chúng ta cũng phải xác định toán tử của nó . Như vậy, không có logic mặc định cho >=toán tử. Như đã chỉ ra bởi Evgeny Vasilyev , _rect_tfm.position == _positionBcó ý nghĩa, như chúng ta có thể trực tiếp kiểm tra Vector3.x, Vector3.yVector3.zcác giá trị. _rect_tfm.position >= _positionBkhông có ý nghĩa nhiều như vậy, do thực tế là a Vector3được biểu thị bằng ba giá trị riêng biệt.

Chúng ta có thể quá tải Vector3lớp để chứa các toán tử phù hợp trong lý thuyết , nhưng điều đó có vẻ khá phức tạp. Thay vào đó, nó sẽ dễ dàng hơn chỉ đơn giản là mở rộng các Vector3lớp học với một phù hợp phương pháp . Điều đó đang được nói, có vẻ như bạn có ý định sử dụng logic này cho chuyển động. Như vậy, bạn có thể thấy việc sử dụng Vector3.Lerpphương thức này dễ dàng hơn nhiều ; nếu vậy, đọc thêm bên dưới.

Thêm phương thức mở rộng vào Vector3

Như đã đề cập trước đây, áp dụng <=hoặc >=cho một Vector3thường là phi logic. Đối với chuyển động, có lẽ bạn muốn đọc thêm cho Vector3.Lerpphương pháp. Điều đó nói rằng, bạn có thể muốn áp dụng <= =>số học cho các lý do khác, vì vậy tôi sẽ cung cấp cho bạn một sự thay thế dễ dàng.

Thay vì áp dụng logic của Vector3 <= Vector3hoặc Vector3 >= Vector3, tôi đề xuất mở rộng Vector3lớp để bao gồm các phương thức cho isGreaterOrEqual(Vector3 other)isLesserOrEqual(Vector3). Chúng ta có thể thêm các phương thức mở rộng vào một structhoặc classbằng cách khai báo chúng trong một staticlớp không kế thừa. Chúng tôi cũng bao gồm mục tiêu classhoặc structlà tham số đầu tiên, sử dụng thistừ khóa. Lưu ý rằng trong ví dụ của tôi, tôi cho rằng bạn có nghĩa là để đảm bảo rằng tất cả ba giá trị chính ( x, yz) là tất cả lớn hơn hoặc bằng, hoặc thấp hơn hoặc tương đương, tương ứng. Bạn có thể cung cấp logic của riêng bạn, ở đây, như bạn yêu cầu.

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

Khi chúng ta cố gắng gọi các phương thức này từ Vector3lớp, localsẽ đại diện cho Vector3thể hiện mà chúng ta đang gọi phương thức từ đó. Bạn sẽ lưu ý rằng các phương thức là static; phương thức mở rộng phảistatic, nhưng bạn vẫn phải gọi chúng từ một thể hiện. Với các phương thức mở rộng ở trên, bây giờ bạn có thể áp dụng chúng trực tiếp cho các Vector3loại của mình .

Vector3 left;
Vector3 right;

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

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

Di chuyển Vector3vớiVector3.Lerp

Kêu gọi các Vector3.Lerpphương pháp cho phép chúng ta xác định vị trí chính xác giữa hai Vector3giá trị tại một thời điểm nhất định. Một lợi ích gia tăng của phương pháp này là các Vector3sẽ không vọt lố mục tiêu của nó . Vector3.Lerplấy ba tham số; vị trí bắt đầu, vị trí kết thúc và vị trí hiện tại được biểu thị dưới dạng giá trị từ 0 đến 1. Nó xuất ra vị trí kết quả là a Vector3, mà chúng ta có thể đặt trực tiếp làm vị trí hiện tại.

Giải quyết vấn đề của bạn, tôi đề xuất sử dụng Vector3.Lerpđể chuyển sang a targetPosition. Sau khi gọi Movephương thức trong mỗi phương thức Update, chúng ta có thể kiểm tra xem chúng ta đã đạt được mục tiêu đã nói chưa; Lerp.Vector3sẽ không vượt quá, vì vậy transform.position == targetPositiontrở nên đáng tin cậy. Bây giờ chúng ta có thể kiểm tra vị trí và thay đổi targetPositionthành leftPositionhoặc rightPositionđảo ngược chuyển động, theo đó.

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

Bạn có thể thấy điều này được thể hiện trong hình ảnh động sau đây. Tôi dịch khối lập phương màu xanh với Vector3.LerpUnclamped, cho chúng ta một kết quả tương tự với bản dịch đơn giản không được kiểm tra. Tôi dịch khối lập phương màu đỏ bằng cách sử dụng Vector3.Lerp. Không được kiểm soát, khối màu xanh chuyển sang quên lãng; trong khi khối màu đỏ dừng lại chính xác nơi tôi dự định. Bạn có thể đọc thêm về loại chuyển động này trong tài liệu Stack Overflow .

Không được kiểm soát, khối màu xanh chuyển sang quên lãng;  trong khi khối màu đỏ dừng lại chính xác nơi tôi dự định.


Wow, bạn thực sự đã đi thêm một dặm, cảm ơn bạn rất nhiều!
Javier Martinez

27

Xác định >=cho một Vector3loại không có ý nghĩa. Điều gì xác định nếu một vector lớn hơn một vector khác? Độ lớn của chúng hoặc các thành phần x, y, z riêng lẻ của chúng?

Một vectơ là một cường độ và một hướng. Vậy điều gì quyết định hướng nào lớn hơn?

Nếu bạn cần so sánh độ lớn bạn có thể sử dụng sqrMagnitude.

Trong trường hợp này, Vector3ghi đè ==chỉ đơn giản là so sánh các thành phần khác nhau để xem chúng có giống nhau không. (trong một ngưỡng)

Đây là cùng một lý do nhân hai vectơ sử dụng *là không thể. Đơn giản là không có cách làm toán học nào. Một số người sử dụng *cho sản phẩm chấm, nhưng đó là một thiết kế API không rõ ràng.


Unity Vector3là một struct, vì vậy đoạn về so sánh tham chiếu không hoàn toàn đúng.
31eee384

Điều đó nghĩa là vị trí của một vectơ trên mỗi trục lớn hơn vị trí khác, tương tự như so sánh 2 số nguyên, chỉ như một nhóm. Ứng dụng hạn chế hơn một chút so với việc so sánh từng thuộc tính riêng lẻ, nhưng ít nhất vẫn có thể được sử dụng.
Pysis

Đây không phải là Java. So sánh tham chiếu là không đúng trong các cấu trúc hoặc các lớp trong đó toán tử bằng được xác định
Gustavo Maciel

Tôi đã sửa đổi câu trả lời của tôi để loại bỏ phần đó. Tuy nhiên, C # đã ở một điểm Java. Theo như tôi biết, cốt lõi của các lớp vẫn hoạt động như nhau và nếu == không được viết xong thì nó hoạt động chính xác như thế nào trong java.
Evgeny Vasilyev

2

Đây là một câu hỏi cũ nhưng để đưa vào các thuật ngữ ít kỹ thuật hơn, Vector3 là một "thùng chứa" cho 3 giá trị float - x, y, z.

Bạn có thể so sánh các giá trị riêng lẻ, chẳng hạn như so sánh giá trị x của hai Vector3, vì chúng chỉ là số.

Tuy nhiên, toàn bộ Vector3 không thể so sánh với Vector3 khác vì không có một giá trị duy nhất nào có thể được sử dụng để so sánh cả hai.


0

Chỉ cần thêm vào những gì Gnemlock đã đăng, liên quan đến việc thêm các phương thức mở rộng vào lớp Vector3. Có một vấn đề trong Unity (và tôi chắc chắn game engine khác) khi sử dụng toán tử so sánh nhất định ( ==, <=>=) giữa hai giá trị float, do cách tính toán dấu chấm động được xử lý. Mathf.Approximatelynên được sử dụng thay thế, do đó có thể thêm các phương thức mở rộng sau để kiểm tra xem hai vectơ có> = hoặc <= với nhau không:

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

Bạn chắc chắn có thể sử dụng điều này, nếu bạn muốn cả hai phép thử ≤ & trở về đúng khi giá trị không thay đổi một chút. Thông thường, chúng tôi chỉ áp dụng kiểm tra xấp xỉ bằng nhau khi kiểm tra tính bằng cho một giá trị cụ thể duy nhất. Nó "mở rộng" kiểm tra từ một điểm duy nhất (dễ bỏ sót) sang một lỗi nhỏ ở hai bên. Và have đã có một biên độ lỗi tích hợp sẵn: mọi phần vượt quá mức thấp hoặc cao tương ứng đều bị bắt giữ, do đó chúng sẽ ít bị thiếu trường hợp mong muốn hơn do sai lệch nhỏ trong tính toán.
DMGregory

0

Tôi muốn đề xuất một cách khác để giải thích câu hỏi này. Một mẫu mã như thế này:

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

về cơ bản là cố gắng sử dụng >=/ <=toán tử vì "bên trái đã đạt hoặc vượt qua bên phải chưa?" xét nghiệm.

Sử dụng >=/ <=để có nghĩa là "đạt hoặc vượt qua" có nghĩa theo nghĩa một chiều, nếu vị trí của tôi chỉ là nổi:

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

Nhưng trong không gian 3D, chúng tôi không có bất kỳ một dòng nào để đo, để quyết định bên nào là "cao / xa" và bên nào là "thấp / gần". Ví dụ: chúng tôi có thể cố gắng tuần tra giữa các điểm

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

Vì vậy, bây giờ chúng tôi mong đợi patrolStart <= myPosition <= patrolEndtrên trục X, nhưng patrolEnd <= myPosition <= patrolStarttrên trục Z. Toán tử "đạt hoặc đạt" của chúng tôi khác nhau từ trục này sang trục khác, do đó, không còn ánh xạ rõ ràng giữa khái niệm vượt ngưỡng và kiểm tra bất bình đẳng đơn giản.

Nhưng, có một cách chúng ta có thể chọn chỉ một dòng trong không gian 3D và làm cho >=/ <=hành xử giống như trường hợp nổi duy nhất dọc theo dòng này mà chúng ta đã chọn:

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

Như một phần thưởng, nếu bạn bình thường hóa vectơ trục trước khi sử dụng nó, thì tất cả các sản phẩm chấm đại diện cho khoảng cách, do đó bạn có thể đo chính xác khoảng cách của bạn từ hai đầu, dọc theo trục di chuyển.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.