В Unity почему добавление Vector2 и Vector3 неоднозначно, а назначение - нет?

11

Даны следующие два вектора:

Vector3 v3 = Vector3.one;
Vector2 v2 = Vector2.one;

Эта строка неоднозначна:

v3 += v2; // Unity reports dimension ambiguity

пока это назначение не:

v3 = v2; // Unity assigns despite different dimensions

Почему это?

SteakOverflow
источник
Итак, большое спасибо Anko и Xavi Montero за отличные ответы. Пытаясь воспроизвести проблему, я понял, что мой вопрос должен быть изменен. Это происходит при назначении Vector2 для transform.position, так что transform.position = a + b; где ОБА и В - это Vector2, компилируется правильно и присваивает x и y! Вместо этого он не работает, когда я изменяю строку на transform.position + = a; // или b Я использую Unity 5.0.0f, и скрипт присоединяется к элементу пользовательского интерфейса (Image), который не влияет на компилятор, но может быть важной информацией для воспроизведения сценария.
SteakOverflow
Так вы, ребята, говорите мне, что делать. Должны ли мы изменить вопрос и ваши ответы соответственно или мы начнем новый?
SteakOverflow
Vector3 + Vector2 и Vector3 + = Vector2 никогда не будут работать независимо от того, что вы хотите, потому что оба могут быть неявно преобразованы в другие, и вам нужно явно привести в соответствии с тем, что вы хотите сделать. Vector3 = Vector2 работает, потому что нет никакой двусмысленности. Я не вижу необходимости менять или публиковать новый вопрос.
Я говорю, чтобы изменить вопрос, потому что случай немного отличается от того, что я описал, что может затормозить поток вашего ответа.
SteakOverflow
Нах не волнуйся, ничего не ломается.

Ответы:

12

Полное сообщение о проблеме:

ошибка CS0121: вызов неоднозначен между следующими методами или свойствами: UnityEngine.Vector2.operator +(UnityEngine.Vector2, UnityEngine.Vector2)' andUnityEngine.Vector3.operator + (UnityEngine.Vector3, UnityEngine.Vector3) '

Unity предоставил способ неявного преобразования a Vector3в a Vector2и наоборот. Это вызывает двусмысленность в вашем случае, потому что оба оператора + могут быть применены.

Отдайте свой Vector2к Vector3явно в этой операции, так что компилятор знает использовать UnityEngine.Vector3.operator +(UnityEngine.Vector3, UnityEngine.Vector3).

Если вам интересно, документы Microsoft для этой конкретной ошибки здесь .


Редактировать после редактирования:

Vec3 += Vec2является неоднозначным по той же причине, описанной выше. Представь, Vec3 += Vec2что ты на самом деле Vec3 = Vec3 + Vec2. Vec3 + Vec2может давать как Vector2и Vector3в ответ в результате неявных преобразований, поэтому вы должны указать, что вы хотите.

Vec3 = Vec2не является двусмысленным, потому что Vec2 неявно преобразуется в a, Vector3а затем назначается начальному Vec3. Таким образом, вся операция на самом деле Vector3 = Vector3без необходимости Vector3вручную приводить Vec2 .


источник
Поэтому Unity предполагает, что я хочу, чтобы мои Vector3 х и у были назначены из моих Vector2 х и у, а мой z установлен в 0f. Правильно?
SteakOverflow
Да, представьте, что Vec3 = Vec2 эквивалентно Vec3 = (Vector3) Vec2. Вам не нужно кастовать вручную, потому что в этом случае в действительности не может быть ничего другого.
Да, на самом деле мой вопрос может быть назван примерно так: «Почему мне не нужно приводить к Vector3 при назначении Vector2». Я не лучший в названии и создании идеальных вопросов: / Спасибо, Алекс. Мое любопытство было (чрезмерно) удовлетворено.
SteakOverflow
3

Позвольте мне переименовать переменные (для ясности):

Vector3 pos3d = new Vector3 (1f, 2f, 3f);
Vector2 pos2d = new Vector2 (1f, 2f);

Ответ

Это из-за сечения pos3d + pos2dлинии. Эта часть действительно неоднозначна, в то время +=как нет. Позвольте мне уточнить, почему одно и почему другое.

Анализ 1

В этой строке

transform.position = pos3d + pos2d;

компилятор сначала пытается вычислить выражение, pos3d + pos2dпрежде чем продолжить, независимо от того, куда будет помещен результат.

Для этого система сначала пытается найти любую общедоступную статическую функцию, которая добавляет Vector3 плюс Vector2, например, такую ​​возможную сигнатуру:

public static Vector3 operator +(Vector3 a, Vector2 b);

или, например, эта возможная подпись:

public static Vector2 operator +(Vector3 a, Vector2 b);

Тем не менее, в API нет ни одной из этих сигнатур, поэтому компилятор пытается «привести» параметры к известным сигнатурам.

Затем компилятор находит эти две потенциальные сигнатуры:

public static Vector3 operator +(Vector3 a, Vector3 b);
public static Vector2 operator +(Vector2 a, Vector2 b);

Они описаны здесь: http://docs.unity3d.com/ScriptReference/Vector3-operator_add.html и здесь: http://docs.unity3d.com/ScriptReference/Vector2-operator_add.html.

Итак, есть две возможности:

Так как возможны оба приведения, pos2d может быть преобразован в Vector3, а pos3d - в Vector2, компилятор затем находит возможные способы компиляции одного и того же исходного кода (при условии наличия автоматических скрытых приведений).

Можно либо привести pos3d в Vector2 и перейти ко второй подписи, либо привести pos2d в Vector3 и перейти к первой подписи.

Поскольку выражение pos3d + pos2dвычисляется в первую очередь, прежде чем принимать во внимание «где будет применен результат», тогда компилятор не знает, какое приведение вы, как кодер, хотели бы выполнить.

Если вы хотите перейти к 3D, вы можете написать это:

transform.position = pos3d + ( Vector3 )pos2d;

и проблема исчезла, как теперь понятно: сначала переместите pos2d в другой объект типа Vector3, затем выполните суммирование Vector3 + Vector3. При условии, что есть эта статическая подпись

public static Vector3 operator +(Vector3 a, Vector3 b);

доступно, что один будет использоваться без двусмысленности вообще.

Анализ 2

С другой стороны, когда вы делаете

transform.position = pos3d;
transform.position += pos2d; 

нет никакой двусмысленности: первая строка назначает Vector3 в Vector3 (без сомнения).

Вторая строка эквивалентна

transform.position = transform.position + pos2d;

с учетом специфики transform.position оценивается только один раз, и поэтому принимается во внимание тип, как вы можете видеть на этой странице Microsoft об +=операторе:

https://msdn.microsoft.com/en-us/library/sa7629ew.aspx

Кроме того, в нем говорится: «Оператор + = не может быть перегружен напрямую, но пользовательские типы могут перегружать оператор + (см. Оператор)». поэтому мы должны думать, что оператор Vector3s +=действует так, как описано в Microsoft, где он говорит:

x += y

эквивалентно

x = x + y

за исключением того, что х оценивается только один раз. Значение оператора + зависит от типов x и y (сложение для числовых операндов, конкатенация для строковых операндов и т. Д.).

поэтому мы можем быть уверены, что второй подход вызывает операнд + Vector3класса, который имеет сигнатуру:

public static Vector3 operator +(Vector3 a, Vector3 b);

так что нет другого способа добиться этого, кроме как преобразовать pos2d в Vector3 благодаря неявному скрытому приведению, которое не может быть в любой другой форме.

Надеюсь помочь !!


редактировать

В связи Unity 5.0.1f1 Personalс тем MonoDevelop-Unit 4.0.1, как говорит Алекс М., строки:

transform.position = pos3d;
transform.position += pos2d;

все еще наложил ошибку "Assets/Scripts/CubeScript.cs(15,27): error CS0121: The call is ambiguous between the following methods or properties: 'UnityEngine.Vector2.operator +(UnityEngine.Vector2, UnityEngine.Vector2)' and 'UnityEngine.Vector3.operator +(UnityEngine.Vector3, UnityEngine.Vector3)'"

так что на самом деле + = использует обе подписи

public static Vector3 operator +(Vector3 a, Vector3 b);
public static Vector2 operator +(Vector2 a, Vector2 b);

независимо от того, что уже известно, «где» результат должен быть помещен (я полагаю, что вывод Vector2 можно преобразовать в место назначения (Vector3), и, возможно, если это приведение было невозможно, возможно, компилятор выберет тот, который имеет тип выхода).

Спасибо за точку зрения Алекс М.

Хави Монтеро
источник
+=тоже не работает, все равно Vector3+ Vector2. Смотри мой ответ.
Правильно, голосовал ваш, отредактирую мой.
Хави Монтеро
Только что отредактировал, чтобы включить ваш комментарий.
Хави Монтеро
transform.position + = pos2d компилируется. Завтра я буду более точным (возможно, это просто ошибка версии)
SteakOverflow
После правок я потерялся в последовательности ... так что ... pos3d += pos2d(согласно вашему отредактированному вопросу) происходит сбой, но transform.position += pos2dкомпилируется (согласно вашему комментарию выше) ?? Казалось бы странно, как .positionесть Vector3- не могу ясно понять, если это то, что вы имеете в виду.
Хави Монтеро