the only difference is that i want the fields to be (x,z) instead of (x,y)
so I would do vec.x and vec.z
I could just make a custom struct
but I have realized that i suddenly lose all the convenient method calls like, .normalize, and worse, Vector2.dot, Vector2.Perpendicular and all this stuff
should I just live with it and use vec.y and a normal vector2 or is there a simple way to workaround this without making code spagetti?
Basically my reason for this is that the directions of movement in my game are X,Z and I want to make the code easy to understand and do pos.z + vec.z instead of pos.z + vec.y
I understand I could just work around everything by casting the struct everytime back to a normal vector2 but that would just make the code a mess, which is what im trying to avoid
You could make an extension method named z (or GetZ() to be in line with naming conventions) which simply redirects to/from y.
What I often do in such cases is to create an extension method Vector3 ToVector3(this Vector2 v2) which simply does: return new Vector3(v2.x, 0f, v2.y);
This would be spiffy but again it puts you close to some very mysterious potential failures.
Image if you could make a C# extension property that maps Vector2.z to Vector2.y
You blaze ahead and make lots of code using this.
Somewhere deep in that code you inadvertently type Vector3 on one of your variable declarations, then use this Vector3 variable by implicit cast of a Vector2, either going into or out of it.
For that variable only, suddenly your .z extension method would be returning the true .z…
Veeeeeery subtle and baffling bugs could ensue, especially if that was deep inside some other calculation chain.
What you need is very common and your only mistake was to rely on automatic conversions along the way (which were a huge mistake by Unity to make these in the first place, especially the implicit ones, I can’t stress that enough).
CodeSmile (and others) already pointed out that extensions exist. But it is not necessary to do this via extensions, in fact, I’d argue it’s better to do this in situ (unless you’re sure you’re going to repeat this all over the place).
You should not remap one component to another, instead you remap one vector to another. Typically, you need this when you work in 2D space and then want to translate this into 3D and vice versa.
static public class MyVectorExtensions {
static public Vector3 toXY(this Vector2 v) => new(v.x, v.y, 0f);
static public Vector3 toXZ(this Vector2 v) => new(v.x, 0f, v.y);
static public Vector2 fromXY(this Vector3 v) => new(v.x, v.y);
static public Vector2 fromXZ(this Vector3 v) => new(v.x, v.z);
}
You can use these in a couple of ways
Vector2 myVector = Vector2.up;
drawCircle(myVector.toXZ(), 5f);
// or
drawCircle(MyVectorExtensions.toXZ(myVector), 5f);
I always write these things locally, or I make a project-wide static class for all kinds of utilities.
In the latter case, I remove the need for having to prefix everything with ClassName. by stating the following in the using block
using static MyVectorExtensions;
then the 2nd example becomes
drawCircle(toXZ(myVector), 5f);
Also check my tutorials (look in the signature) where you can find well articulated code examples where I regularly employ the local static functions technique (not the extensions). I have zillions of good reasons why this is a good practice, I’ve even named the most of them in one of the tutorials (I think it’s the intercepting one).
You could make a custom struct but because Unity can’t use that natively, you’d have to write explicit (or implicit) conversions between the types.
public struct MyVector2 {
public float x, y;
public MyVector2(float x, float y) {
this.x = x;
this.y = y;
}
// here go all kinds of functionalities one would expect from 2D vector
// this is an implicit casting between types
static public implicit operator UnityEngine.Vector2(MyVector2 v) => new(v.x, v.y);
// and the other way
static public implicit operator MyVector(UnityEngine.Vector2 v) => new(v.x, v.y);
}
Now you can do
MyVector2 myVector = new MyVector2(0f, 1f);
drawCircle(myVector, 5f); // this is what implicit means, the conversion happens without you asking for it
// where
// void drawCircle(Vector2 center, float radius) {}
However, I would advise you against this if you don’t have much experience, doing this might open a Pandora’s box of weird stuff. And if you’re not on top of these techniques, please do avoid implicit casting like a plague, because you’re likely to turn everything into a very complicated mess of unfixable code.
This is how explicit casting expression looks
MyVector2 myVector = (MyVector2)unityVector;
And btw if you want to go down this route and learn how to implement your own vectors and other value types, make sure to learn the following areas of C# before that
reference vs value types
implicit vs explicit casting
IEquatable interface (and value type equality in general)
IComparable interface
custom operators (including casting operators, but also ==, !=, +, -, *, /)
hash codes and their purpose
That’s the bare minimum, imo, but you can very well make even a more robust and faster implementation of vectors and such. Heck you can even implement custom swizzling!
Or you can use (or peek into) Unity.Mathematics which does all of this already and has everything that you need (more or less). But if you go ahead and use float2 and float3, you still have this problem of converting that to comply with the traditional API.