11
\$\begingroup\$

Given the following two vectors:

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

This line is ambiguous:

v3 += v2; // Unity reports dimension ambiguity

whilst this assignment isn't:

v3 = v2; // Unity assigns despite different dimensions

Why is this?

\$\endgroup\$
  • \$\begingroup\$ So, thank you very much to Anko and Xavi Montero for the great answers. Trying to reproduce the problem I realized my question should be changed. The fact is happening when assigning a Vector2 to transform.position so that transform.position = a + b; where BOTH a and b are Vector2 compiles correctly and it assigns x and y! Instead, it doesn't work when I change the line to transform.position += a; // or b I'm using Unity 5.0.0f and the script is attached to a UI element (Image) which doesn't affect the compiler but may be important information for you to reproduce the scenario. \$\endgroup\$ – SteakOverflow Apr 21 '15 at 8:38
  • \$\begingroup\$ So you guys tell me what to do. Shall we change the question and your answers accordingly or shall we start a new one? \$\endgroup\$ – SteakOverflow Apr 21 '15 at 8:41
  • \$\begingroup\$ Vector3 + Vector2 and Vector3 += Vector2 will never work no matter what you want because both can be implicitly converted to the other one and you need to explicitly cast according to what you want to do. Vector3 = Vector2 works because there's no ambiguity. I don't see the need to change or post a new question. \$\endgroup\$ – user15805 Apr 21 '15 at 9:51
  • \$\begingroup\$ I'm saying to change the question because the case is slightly different from what I described, which might brake the flow of your answer. \$\endgroup\$ – SteakOverflow Apr 21 '15 at 9:55
  • \$\begingroup\$ Nah don't worry, nothing breaks. \$\endgroup\$ – user15805 Apr 21 '15 at 10:01
12
\$\begingroup\$

Full issue message:

error CS0121: The call is ambiguous between the following methods or properties: UnityEngine.Vector2.operator +(UnityEngine.Vector2, UnityEngine.Vector2)' andUnityEngine.Vector3.operator +(UnityEngine.Vector3, UnityEngine.Vector3)'

Unity provided a way to implicitly convert a Vector3 to a Vector2 and vice-versa. This causes an ambiguity in your case because both + operators can be applied.

Cast your Vector2 to a Vector3 explicitly in that operation, so that the compiler knows to use UnityEngine.Vector3.operator +(UnityEngine.Vector3, UnityEngine.Vector3).

If you're curious, Microsoft's docs for that specific error are here.


Edit after your edit:

Vec3 += Vec2 is ambiguous for the same reason described above. Imagine Vec3 += Vec2 being in fact Vec3 = Vec3 + Vec2. Vec3 + Vec2 can yield both Vector2 and Vector3 as answer due to implicit conversions, that's why you have to specify what you want.

Vec3 = Vec2 is not ambiguous because Vec2 is implicitly converted to a Vector3 and then assigned to the initial Vec3. So the whole operation is really Vector3 = Vector3 without you having to cast Vec2 to a Vector3 manually.

\$\endgroup\$
  • \$\begingroup\$ So Unity assumes I want my Vector3's x and y to be assigned from my Vector2's x and y and my z set to 0f. Right? \$\endgroup\$ – SteakOverflow Apr 21 '15 at 10:32
  • \$\begingroup\$ Yes, imagine Vec3 = Vec2 being equivalent to Vec3 = (Vector3)Vec2. You don't need to cast manually because it can't really be anything other than that in that case. \$\endgroup\$ – user15805 Apr 21 '15 at 10:34
  • \$\begingroup\$ Yeah, actually my question could be titled something like "Why don't I need to cast to Vector3 when assigning a Vector2". I'm not the best in titling and crafting perfect questions :/ Thank you Alex. My curiosity has been (over)satisfied. \$\endgroup\$ – SteakOverflow Apr 21 '15 at 10:37
3
\$\begingroup\$

Let me rename the vars (for clarity):

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

Answer

It is because of the section pos3d + pos2d of the line. This part is really ambiguous while the += is not. Let me clarify why one and why the other.

Analysis 1

In this line

transform.position = pos3d + pos2d;

the compiler first tries to evaluate the expression pos3d + pos2d before proceeding, regardless of where the result is going to be placed.

To do so, the system first tries to find any public static function that adds a Vector3 plus a Vector2, for example this possible signature:

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

or for example this possible signature:

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

Nevertheless there is not any of those signatures in the API so the compiler tries to "cast" parameters to known signatures.

Then the compiler finds those two potential signatures:

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

These are documented here: http://docs.unity3d.com/ScriptReference/Vector3-operator_add.html and here: http://docs.unity3d.com/ScriptReference/Vector2-operator_add.html

So there are two possibilities:

So as both castings are possible, pos2d can be casted to a Vector3 and pos3d is castabale into a Vector2, the compiler then finds to possible ways to compile the same source code (provided automatic hidden castings are in place).

It is either possible to cast pos3d into Vector2 and proceed with the second signature, or either cast pos2d into Vector3 and proceed with the first signature.

As the expression pos3d + pos2d is evaluated first, before taking in consideration "where the result will be applied" then the compiler does not know what cast would you -as coder- like it to perform.

If you want to move towards 3D, you can write this:

transform.position = pos3d + ( Vector3 )pos2d;

and the problem is gone, as now it is clear: first move pos2d into another object of type Vector3, then do the sum of Vector3 + Vector3. Provided that there is this static signature

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

available, that one will be used with no ambiguity at all.

Analysis 2

On the other hand, when you do

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

there is no ambiguity: The first line assigns a Vector3 into a Vector3 (no doubts).

The second line is equivalent to

transform.position = transform.position + pos2d;

with the particularity the transform.position is only evaluated once, and therefore the type is taken in consideration, as you can see in this Microsoft page about the += operator:

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

It in addition says "The += operator cannot be overloaded directly, but user-defined types can overload the + operator (see operator)." so we should think the Vector3's += operator acts as described by microsoft where it says:

x += y

is equivalent to

x = x + y

except that x is only evaluated once. The meaning of the + operator depends on the types of x and y (addition for numeric operands, concatenation for string operands, and so forth).

so we can be sure the second approach invokes the + operand of the Vector3 class, which has the signature:

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

so there is not any other way to achieve this than converting the pos2d into a Vector3 thanks to an implicit hidden cast that cannot be of any other form.

Hope to help!!


Edit

In Unity 5.0.1f1 Personal with MonoDevelop-Unit 4.0.1, as Alex M. says, the lines:

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

still cast the error "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)'"

so really the += is using both signatures

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

regardless of the fact of already knowing "where" the result shall be placed (I guess because outputting a Vector2 is castable to the destination (Vector3) and if that cast was not possible probably, maybe, the compiler would choose the one with the proper output type).

Thanks for the point Alex M.

\$\endgroup\$
  • \$\begingroup\$ += doesn't work either, it's still a Vector3 + Vector2. See my answer. \$\endgroup\$ – user15805 Apr 20 '15 at 22:08
  • \$\begingroup\$ Right, upvoted yours, shall edit mine. \$\endgroup\$ – Xavi Montero Apr 20 '15 at 22:13
  • \$\begingroup\$ Just edited to incorporate your comment. \$\endgroup\$ – Xavi Montero Apr 20 '15 at 22:21
  • \$\begingroup\$ transform.position += pos2d is compiling. I'll be more precise tomorrow (might be just a version bug) \$\endgroup\$ – SteakOverflow Apr 20 '15 at 22:23
  • \$\begingroup\$ After the edits I got lost in the sequence... so... pos3d += pos2d (as per your edited question) fails but transform.position += pos2d compiles (as per your comment above)?? Would seem weird as .position is Vector3 - Can't clearly see if that's what you are meaning. \$\endgroup\$ – Xavi Montero Apr 22 '15 at 6:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.