11
\$\begingroup\$

What is the most efficient and fastest way to convert a Vector3 to a Vector2?

Casting:

Vector2 vector2 = (Vector2)vector3;

Initializing a new Vector2:

Vector2 vector2 = new Vector2(vector3.x, vector3.y);

Or is there another method that I don't know?

\$\endgroup\$
  • 10
    \$\begingroup\$ These types of struct operations are never going to be the performance-determining bottleneck in your game, so rather than getting bogged down in micro-optimizations like this, I'd recommend just using whichever is clearer to understand in the context you're using it. ;) \$\endgroup\$ – DMGregory Jan 2 '16 at 15:00
  • 1
    \$\begingroup\$ @DMGregory: Unless, of course, OP has already done a performance analysis and, perhaps due to boxing, actually has this in a nested loop causing a performance problem. Such a nested loop might, for example, be an A-star or Dijkstra implementation. \$\endgroup\$ – Pieter Geerkens Jan 2 '16 at 15:29
  • 4
    \$\begingroup\$ @PieterGeerkens Fair, but if OP were already doing performance analysis, they'd have already tried both ways and would have numbers on both. ;) From watching the trajectory of a number of new Unity users (including myself), I'm pretty confident that in this case it's micro-optimization, so I wanted to make a strong (if possibly overstated) warning against it. That way lies weeks or months of code tweaks and fretting over optimality in ways that don't make our games any better. \$\endgroup\$ – DMGregory Jan 2 '16 at 15:36
11
\$\begingroup\$
Vector3 v3 = Vector3.one;
Vector2 v2 = v3;

Vector3s can be implicitly converted to Vector2 (z is discarded).

http://docs.unity3d.com/ScriptReference/Vector2-operator_Vector3.html

If you have to make a lot of conversions you may have to change the way you use your vectors. Make two tests and time them to see which one works for you.

UPDATE WITH TESTS: Since you asked which one is the fastest I created a test running 10000000 conversions of each in Unity. It appears that the Initializing version is quickest in this case. BUT, you should always use the one that suits your own context, so I advise you to run your own tests in your game.

TestConvertByOperation 10000000 instances: 0.2714049s

TestConvertByCasting 10000000 instances: 0.286027s

TestConvertByInitializing 10000000 instances: 0.1458781s

using UnityEngine;

public class TestVector3Conversion : MonoBehaviour
{

    readonly int iterations = 10000000;
    Vector3 testVector = new Vector3(3f, 14f, 42f);

    void Start()
    {
        Debug.Log(string.Format("TestConvertByOperation {0} instances: {1}s", iterations, TestConvertByOperation()));
        Debug.Log(string.Format("TestConvertByCasting {0} instances: {1}s", iterations, TestConvertByCasting()));
        Debug.Log(string.Format("TestConvertByInitializing {0} instances: {1}s", iterations, TestConvertByInitializing()));
    }

    float TestConvertByOperation()
    {
        var timeStart = Time.realtimeSinceStartup;

        for (int i = 0; i < iterations; i++)
        {
            Vector2 v2 = testVector;
        }

        return Time.realtimeSinceStartup - timeStart;
    }

    float TestConvertByCasting()
    {
        var timeStart = Time.realtimeSinceStartup;

        for (int i = 0; i < iterations; i++)
        {
            Vector2 v2 = (Vector2)testVector;
        }

        return Time.realtimeSinceStartup - timeStart;
    }

    float TestConvertByInitializing()
    {
        var timeStart = Time.realtimeSinceStartup;

        for (int i = 0; i < iterations; i++)
        {
            Vector2 v2 = new Vector2(testVector.x, testVector.y);
        }

        return Time.realtimeSinceStartup - timeStart;
    }

}
\$\endgroup\$
  • 1
    \$\begingroup\$ They are implicitly casted. This is done by defining new conversion operators. Ironically Unity violates the '...if the conversion is guaranteed not to cause a loss of data.' part. \$\endgroup\$ – Athos vk Jan 2 '16 at 14:15
  • 1
    \$\begingroup\$ Updated my answer with a code example to test the different approaches. Let me know which one is faster in your case. \$\endgroup\$ – Mattias Jan 2 '16 at 15:46
  • 1
    \$\begingroup\$ The results shift a little in a release/non-debug build, or when the Vector2 data has a lifetime outside of the for loop (which keeps the compiler from doing certain types of optimization). I get a spread of 110 to 151 milliseconds, or a maximum difference of about 4 nanoseconds per assignment. So unless we're doing this hundreds of thousands of times every frame, this probably isn't a case to worry about, even if there is a measurable difference in a synthetic example like this. \$\endgroup\$ – DMGregory Jan 2 '16 at 17:29
  • 1
    \$\begingroup\$ @DMGregory Agreed. That is why it is always a good idea to run performance tests in the correct context, with real data. \$\endgroup\$ – Mattias Jan 2 '16 at 17:41
  • 1
    \$\begingroup\$ The problem with the implicit conversion is that y is up. When converting a Vector3 to a Vector2, you almost always want x and z, not x and y. \$\endgroup\$ – Kevin Krumwiede Dec 4 '17 at 3:14
6
\$\begingroup\$

Both Vector2 and Vector3 are a struct in the Unity engine, so the creation of one from the other simply involves the allocation of a storage on the stack (unless the destination is an attribute of a class object, which would allow skipping this first step) and the copying of the two component values. Both mechanisms you give should be compiled to exactly this IL code.

If you are encountering a performance problem with a conversion of this type then you probably have a boxing issue, with the struct being converted to and then from a class object. In that case you should investigate whether, when, and how, boxing could be avoided in performance-critical portions of your code.

\$\endgroup\$
0
\$\begingroup\$

From my test, the best way to do it is manual assign its value by yourself.

Vector2 vector2;
vector2.x = vector3.x;
vector2.y = vector3.y;

This is my result that I extend from Mattias.

TestConvertByOperation 10000000 instances: 0.3220527s

TestConvertByCasting 10000000 instances: 0.3226218s

TestConvertByInitializing 10000000 instances: 0.1916729s

TestConvertByManualAssign 10000000 instances: 0.09500527s

using UnityEngine;

namespace myTest
{
    public class test: MonoBehaviour 
    {
        readonly int iterations = 10000000;
        Vector3 testVector = new Vector3(3f, 14f, 42f);

        void Start()
        {
            Debug.Log(string.Format("TestConvertByOperation {0} instances: {1}s", iterations, TestConvertByOperation()));
            Debug.Log(string.Format("TestConvertByCasting {0} instances: {1}s", iterations, TestConvertByCasting()));
            Debug.Log(string.Format("TestConvertByInitializing {0} instances: {1}s", iterations, TestConvertByInitializing()));
            Debug.Log(string.Format("TestConvertByManualAssign {0} instances: {1}s", iterations, TestConvertByManualAssign()));
        }

        float TestConvertByOperation()
        {
            var timeStart = Time.realtimeSinceStartup;
            Vector2 v2;
            for (int i = 0; i < iterations; i++)
            {
                v2 = testVector;
            }

            return Time.realtimeSinceStartup - timeStart;
        }

        float TestConvertByCasting()
        {
            var timeStart = Time.realtimeSinceStartup;
            Vector2 v2;
            for (int i = 0; i < iterations; i++)
            {
                v2 = (Vector2)testVector;
            }

            return Time.realtimeSinceStartup - timeStart;
        }

        float TestConvertByInitializing()
        {
            var timeStart = Time.realtimeSinceStartup;
            Vector2 v2;
            for (int i = 0; i < iterations; i++)
            {
                v2 = new Vector2(testVector.x, testVector.y);
            }

            return Time.realtimeSinceStartup - timeStart;
        }
        float TestConvertByManualAssign()
        {
            var timeStart = Time.realtimeSinceStartup;
            Vector2 v2;
            for (int i = 0; i < iterations; i++)
            {
                v2.x = testVector.x;
                v2.y = testVector.y;
            }

            return Time.realtimeSinceStartup - timeStart;
        }
    }
}

Please note, I test it with unity version 5.6.5

\$\endgroup\$

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.