Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting error "Operator '+=' is ambiguous on operands of type 'Vector3' and 'Vector2'"

While trying to build I'm getting this error:

Operator '+=' is ambiguous on operands of type 'Vector3' and 'Vector2'

Here is the code of the script where the problem comes from:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move2D : MonoBehaviour
{
    public float moveSpeed = 5f;
    public bool isGrounded = false;

    [SerializeField] private Rigidbody2D rigidbody;

    private void Awake()
    {
        if (!rigidbody) rigidbody = GetComponent<Rigidbody2D>();
    }

    public void Jump()
    {
        if (isGrounded)
        {
            rigidbody.AddForce(new Vector3(0f, 5f), ForceMode2D.Impulse);
        }
    }

    public void Move(float value)
    {
        Vector2 movement = new Vector3(value, 0f, 0f);
        transform.position += movement * Time.deltaTime * moveSpeed;
    }
}

Any help or information is really appreciated!


1 Answers

Vector2 has implicit conversion to and from Vector3, but they are not really equivalent, with Vector2 having only X and Y components, while Vector3 has X, Y and Z.

When you try to use someVector3 += someVector2 (or vice-versa), both += operators, from Vector3 and Vector2, are valid.

The compiler cannot be sure of which one you want to use, so that exception is it's way of telling you it can't safely make a decision, and you need to give it a non-ambiguous set of types to work with. In this case, both sides need to be Vector2 or Vector3; not a mix.

You can do that in 2 ways:

  1. Working with the compatible type from the start, which is what @Ruzihm already pointed out in the comments.
  2. Or you can cast the second operand in-place: transform.position += (Vector3)movement * Time.deltaTime * moveSpeed;

Apart from that, I can also see an unhealthy mix of the two types in other portions of the code. I'd strongly suggest you to be consistent. If you are working with 2D, use Vector2s.

Vector2 has it's own constructor. And the Vector3 constructor with 2 parameters only makes the Z default to zero; it does not create a Vector2.

The only reason it works in the first place is that the compiler is using that implicit conversion I talked about above. That, however, has a performance cost.

  • rigidbody.AddForce(new Vector2(0f, 5f), ForceMode2D.Impulse);
  • Vector2 movement = new Vector2(value, 0f); (Or Vector3movement)

A detail I only learned recently myself is that Unity only syncs Transforms in Update, while movement of a Rigidbody (2D or 3D) is only synced in FixedUpdate. This can cause issues when you set Transform.position on a Rigidbody'ed object.

And that is one of the reasons why so many sources tell you to "do physics stuff in FixedUpdate". However, while other stuff related to forgetting Time.deltaTime can happen, as long as the transform and rigidbody are not desynced, you can set position directly. You do that by using Rigidbody.position instead of Transform.position.

An additional nicety of this is that Rigidbody2D.position is a Vector2, eliminating the need to work with Vector3 or cast for that += operation entirely.

public void Move(float value) {
    var movement = new Vector2(value, 0f);
    rigidbody.position += movement * Time.deltaTime * moveSpeed;
}

One last thing, although it is definitely premature-optimization 🤣 (although not of the bad kind, as it has zero impact on readability in this case), is that you can save a multiplication operation by doing it "vector-last" there: rigidbody.position += Time.deltaTime * moveSpeed * movement

The reason is explained in this other answer.

like image 193
XenoRo Avatar answered Oct 22 '25 14:10

XenoRo