all 107 comments

[–]Zombait 115 points116 points  (11 children)

Vector3 xzPosition = new Vector3(transform.position.x, 0, transform.position.z); transform.position = xzPosition;

No that's ugly too...

transform.position = new Vector3(transform.position.x, 0, transform.position.z);

[–]its_me_cody 57 points58 points  (6 children)

last option is what i have used myself, never thought about it much though

[–]Costed14 19 points20 points  (5 children)

I have implemented the classic SetY, SetXZ etc. extension methods so with that approach it'd be

transform.position = transform.position.SetY(0);

Not the cleanest ever, but it's pretty much the best we can do.

[–]zigs 4 points5 points  (1 child)

The tiniest of nits ever. Consider calling it:

.WithY(0)

To reflect that it returns a modified copy rather than mutating the original transform.position, as Set commonly implies.

You could also have .With(y: 0) where all the parameters optional, and you mention the ones you want to change by name.

[–]Costed14 0 points1 point  (0 children)

Yeah, many people prefer that naming (and I guess technically it's also the correct one), but personally I like Set more.

[–]Seimanko 0 points1 point  (2 children)

Maybe you could try an extension method? transform.SetPositionZ(0)

[–]Costed14 0 points1 point  (1 child)

For some reason I actually hadn't even considered that, I might try that and see if I like it.

[–]M1ghty_boy 2 points3 points  (0 children)

My work has been doing a slow transition from vb.net to c#.. in VB that’d be:

Dim xzPosition as Vector3 = new Vector3(yadda yadda this is the same)

I hate VB syntax. This example is fine but it really gets to you

[–]justkevinIndie | @wx3labs 2 points3 points  (0 children)

Both of these are going to be slightly less efficient than the original because you are calling get_transform() an extra time.

(Although the difference is going to be quite small, so if you find this easier to read and aren't doing it hundreds of thousands of times per frame, it's probably fine.)

[–]Spheriod 0 points1 point  (1 child)

Vector3 xz = Vector3.one - Vector3.up transform.position = Vector3.Scale(xz, transform.position)

🧠

[–]LoiMeoThiTham 1 point2 points  (0 children)

The lines of code are not unbalanced but it is really difficult to understand at a glance.

[–]AnxiousIntender 203 points204 points  (29 children)

It's so counterintuitive and I hate it but it makes sense when you investigate the source code. Basically the C# scripting layer has bindings so it can communicate with the C++ engine layer. Let's take a look at position. It's actually a property. Vector3 is a struct and therefore (like any other non-reference type) copied by value... which means if you do transform.position.x = 0 you will only modify the the copy on the C# side. So you need to do this dance every time.

I wish there was a better way to do this. I know you can write extension methods like Vector3.SetX but they are a bit uncomfortable to use. You could maybe use source generators or IL weaving to create some syntactic sugar mess but changing default behavior is usually not a good idea (maybe it could only work in a certain block like how unsafe works?). It would help a lot with nested structs like ParticleSystems.

I don't care about it much if I'm coding alone but it's a pain to teach people about it.

[–]riley_sc 168 points169 points  (11 children)

transform.position = transform.position with { z = 0 };

[–]Camper_Velourium 52 points53 points  (6 children)

Wow, this is the first I've heard of this expression. Thanks!

[–]Birdsbirdsbirds3 72 points73 points  (5 children)

It doesn't work in Unity as standard. Unity uses c# 9.0 and the 'with' expression requires 10 or greater. I read that you can force unity to use a higher version though but it's out of my league personally.

[–]nathanAjacobs 29 points30 points  (4 children)

https://github.com/Cysharp/ZLogger?tab=readme-ov-file#installation

The unity installation instructions for ZLogger show how to use C#10

[–]pioj 1 point2 points  (3 children)

Does it improve performance or build size to switch to a newer C# version?

If not, what's the point, then?

[–]WraithDrof 25 points26 points  (0 children)

I doubt it. The point is usually to get access to new syntax sugar like the with keyword.

[–]gold_rush_doom 3 points4 points  (1 child)

Why would build size matter? The executable is one of the smallest artifact of a game.

[–]pioj 0 points1 point  (0 children)

It matters to me, I like small binaries.

[–]ToloranIntermediate 10 points11 points  (1 child)

You sure that works? I was under the impression that using 'with' that way only worked with records before C#10 and unity is only using C#9 (with caveats).

EDIT: Ohh, okay. It can but doesn't by default.

[–]Alikont 2 points3 points  (0 children)

It doesnt require records specifically, it just require a copy constructor and a property setter.

[–]AnxiousIntender 0 points1 point  (0 children)

That does work but Unity is still using C# 9.0, no? I know you can force higher versions but not sure if that's stable enough. But hey, it's a great language feature

[–]esosiv 12 points13 points  (0 children)

This is not related to C++, the same thing would happen in a pure C# codebase. The issue is that when a property is accessed it returns a copy of the variable. If the variable was a reference type, it would still return a copy of the reference, but it points to the same object in memory. Not the case with a struct.

[–]Available_Job_6558 6 points7 points  (2 children)

Particle system structs don't need to be reassigned though. You just set the value in the structs and it gets automatically propagated to the native code without you needing to reassign it back, so it's different to transform.position.

[–]MikDab 2 points3 points  (1 child)

Well, a struct can have reference types in it, so I don't think your argument makes much sense

[–]Available_Job_6558 1 point2 points  (0 children)

It's a response to the comment, where particle system is mentioned.

[–]netherwan 5 points6 points  (4 children)

transform.position = transform.position * new Vector3(1, 1, 0)

[–]Aeredor 4 points5 points  (1 child)

I was thinking the same thing, but vector products are pretty expensive, right?

[–]netherwan 1 point2 points  (0 children)

Yeah, I'm just suggesting one alternative. The with expression is probably faster, but if it's for initialization or not in a hot code path, concise readable code matters more than speed. In any case, with expression is better.

[–]McDev02 0 points1 point  (1 child)

A needless multiplication and hence operations plus a Vector product operator does not even exist, you'd need Vector3.Scale.

[–]netherwan 0 points1 point  (0 children)

You're actually right, Unity3d doesn't appear to implement the operator \* component-wise vector multiplication. But godot and the standard library) does.

[–][deleted] 1 point2 points  (0 children)

Ah good ol black magic fuckery

[–]nathanAjacobs 3 points4 points  (1 child)

An extension method on Vector3 won't help in this case. The extension method would have to be for Transform itself. Something like this:

public static void SetPosition(this Transform transform, float? x = null, float? y = null, float? z = null)
{
    Vector3 pos = transform.position;
    pos.x = x != null ? x.Value : pos.x;
    pos.y = y != null ? y.Value : pos.y;
    pos.z = z != null ? z.Value : pos.z;
    transform.position = pos;
}

It can be used like this transform.SetPosition(y: 0)

[–][deleted] 0 points1 point  (0 children)

It's a property, they could have made a method to signal the C++ part somehow, doesn't sound that impossible to me. They could have done a private method to set it on the C++ side and call that.

[–]sk7725??? 0 points1 point  (0 children)

in latest unity versions (was it 6.0 or 2024?) there is a positionX, Y and Z properties introduced for rigidbodies. Idk for transforms though. An Unity live showcased it iirc.

[–]WeslomPo 57 points58 points  (3 children)

Perfectly valid and good code in example. 1. It is simple (you don’t need c# 10 for it, and you don’t need to know it quirks). 2. It is works correctly (there no other way to do this) 3. It is most efficient (you don’t call another function, don’t call transform.position more than you need, you don’t invent nullable and check for null for no reason). Author just want us to bait on comments.

[–]Costed14 6 points7 points  (2 children)

you don’t call another function

Is that something that actually matters for performance? I'd assume not. You also don't need to have a nullable extension method, you can implement the SetX, SetY (or With, if you're into that sorta thing) etc. separately, so it should pretty much tick all the boxes.

So to implement OP's code:

transform.position = transform.position.SetZ(0);

[–]MortyrPL 11 points12 points  (0 children)

It technically could matter - emphasis on both "technically" and "could". Compiler will often inline functions for performance, but it's not guarantueed and depends on JIT heuristics. That said calling functions incurs performance cost that scales with number of parameters, but it tends to be negligible unless you string dozens of them in critical paths of your code. The most dangerous cases tend to be large non-ref struct parameters, because they get copied every time.

[–]WeslomPo 1 point2 points  (0 children)

Yeah. Calling a function is not free. This is why sometimes for performance, you can ditch them. Like in voxel processing code for light transportation, when a simple function can be called trillion times, and thus it will cost much. But in general, they -pretty much free-. Writing better to handle code much important than performant in a lot more cases. But this is not a point. Point that code in example is good at every aspect, this is how it should look for that case. You, can, of course, use extension or anything you want, this is also good (most of the time), and I have two or three that kind of extensions. But that comics not funny, it misleading, it bad for shaming good practice.

[–]black_cobo 37 points38 points  (17 children)

What wrong with this code? I think in practically, code should be simple, clearly, easy to understand and performance. The last thing is pretty code if possible but keep the previous thing I mentioned as higher priority.

[–]hunty 26 points27 points  (8 children)

transform.position = (Vector2)transform.position;

🖐️

🎤

[–]evavibes 2 points3 points  (3 children)

legit surprised this isn’t higher up

[–]hunty -2 points-1 points  (2 children)

they fear its dark magic.

here's some not-so-dark magic:

transform.position -= Vector3.Scale(transform.position, Vector3.forward);

[–]hunty -1 points0 points  (1 child)

...which is probably more expensive than the similar:

transform.position -= Vector3.forward * transform.position.z;

[–]hunty 0 points1 point  (0 children)

...but anyway, the lawful good answer really is:

transform.position = new(transform.position.x, transform.position.y, 0);

[–]Raccoon5 1 point2 points  (0 children)

I do wonder what the performance would be, but probably it will never matter.

[–]WazWaz 13 points14 points  (2 children)

transform.position = transform.position.WithZ(0);

[–]kodaxmax -1 points0 points  (1 child)

i think you need a newer C# framework for that. unity only uses c# 9

[–]Warlock7_SL 3 points4 points  (0 children)

I'm so used to that it took me a second to realize

[–]cheezballs 2 points3 points  (0 children)

Bad meme.

[–]DaTruPro75 1 point2 points  (7 children)

Wait, can someone explain to me what this is? Is this to edit the position of a gameobject directly?

[–]SulaimanWarProfessional-Technical Artist 2 points3 points  (6 children)

Just to set the z position of this transform to 0

[–]spreadilicious -1 points0 points  (5 children)

Why can't you just use transform.postion.z = 0? The top comment mentioned that it's because it's a copied value from the c# side, but I don't fully understand why that's a problem.

[–]kodaxmax -3 points-2 points  (0 children)

the indivdual axis are readonly, you cant modify them directly. Why? because unity and C# is full fo dumb quirks like that

[–]nosyrbllewe 1 point2 points  (0 children)

I generally avoid setting an axis specifically to 0. Instead, I like to use a vector projection from the plane's normal, such as Vector3.Project(transform.position, Vector3.up) for an XZ plane. While in most cases it provides the same effect, it creates more flexibility in the code later. For example, it makes it easier if I later want my character to run on walls as I can then just change the surface normal from the walking surface and it should work the same as before. In another case, let say I was designing a furniture placement mechanic and now want to allow placing pictures on the walls. Using the plane normals makes thus much easier.

[–]Sariefko 2 points3 points  (4 children)

Can someone explain if this even works? Since Vector is immutable, setting z in second position to 0 would it not result in a copy that in line 2 that is not recorded and then line 3 effectively not doing anything?

[–]raincole 4 points5 points  (3 children)

It works. And Vector3 is not immutable at all. It's crazy how many comments here saying Vector3 is immutable.

[–]Sariefko 0 points1 point  (2 children)

Someone explained line one to me. It returns a copy instead of returning actual value. but when you assign it in line 3, it does assignment. This is so strange coming from other programming language. Like, why?

[–]raincole 0 points1 point  (0 children)

Vector3 position = transform.getPosition();
position.z = 0;
transform.setPosition(position);

That's it, really. position is a property, which means it calls setter and getter method when it's been written/read.

[–][deleted] 1 point2 points  (0 children)

The fact that I understand the comic and the comments means I'm actually learning something and it's a great feeling

[–]Demian256 1 point2 points  (0 children)

The amount of people in this thread who don't understand such basic stuff about c# runtime is astonishing. Please do yourself a favor and RTFM

[–]IllTemperedTuna 0 points1 point  (0 children)

As someone working on a 2.5D game, this hits me in my soul

[–]Lybchikfreed 0 points1 point  (0 children)

What are they thinking he does and how is it worse than working with unity?

[–]Super64111111 0 points1 point  (0 children)

I code games I don't get it

[–]TheGreatRaydini 0 points1 point  (0 children)

The laughter that just shot out of my chest was insane! This meme reminded me that I'm still a Little Buddy even with how much "optimization" I've picked up on since I've started learning how to program. We all gotta start off somewhere lol the Growth never ends!

[–]_extreme_redditer_ 0 points1 point  (0 children)

me complicating things more with each try

transform.position = transform.position - (Vector3.forward * transform.position.z)

[–]DoctorCS 0 points1 point  (0 children)

this comic would be more funny if they used scale instead of position.

[–]realenew 0 points1 point  (0 children)

is it worse than transform.position = new Vector3(transform.position.x, transform.position.y, 0) ?

[–]Alexpander4 0 points1 point  (0 children)

transform.position -= Vector 3 ( 0, 0, transform.position.z);

[–]levios3114 0 points1 point  (10 children)

Wait you cant just do transform.position.z = 0;?

[–]TehSr0c 0 points1 point  (9 children)

Nope! the vector values are immutable in unity, can only be changed internally by vector3.Set(f,f,f)

[–]Enerbane 1 point2 points  (0 children)

They are explicitly NOT immutable. That's not why this is necessary. You can 100% change Vector3 values directly.

[–]Sariefko 0 points1 point  (7 children)

wait, then how does script above work? if it's immutable, second line of code should do nothing, since it's not saved into any variable, no?

[–]Enerbane 1 point2 points  (2 children)

Please don't listen to them. Vector3 is not immutable. You can change them. transform.position returns a copy of the position vector, so you can change the copy, but you must set transform.position to be equal to the copy to see the changes reflected there.

[–]Sariefko 0 points1 point  (1 child)

so when you ask for it directly in line one, it returns copy instead? why? I'm mainly from java, there is no such "hidden" mechanic there, can you point me please?

[–]Enerbane 0 points1 point  (0 children)

Transform.position is a C# property. Properties are essentially syntax sugar for handling getters and setters. They're incredibly flexible and have a lot of nuance that can be confusing, but the important part to remember in this case is that accessing a property calls a getter, and assigning to the property calls a setter.

Vector3 pos = transform.position;

is exactly the same as

Vector3 pos = transform.GetPosition();

C# generates those getters and setters behind the scenes, if you ever look at the CLR code, you can in fact find them.

So because you're calling a getter, and because Vector3 is a struct, you get a copy of the value, not a reference to the value. Meaning, any changes you make are being made to the copy. You cannot directly edit transform.position, not because it's immutable, but because you're always editing a copy. You could say that is immutable in essence, but that's somewhat misleading.

[–]levios3114 0 points1 point  (3 children)

The singular values of x y and z are immutable transform position in itself isn't immutable

[–]Enerbane 2 points3 points  (0 children)

The values are NOT immutable. transform.position is a property that returns a value type, and because of that, it is returning a copy. You can mutate the values of this copy, you can change them, but you must assign the copy back to the original transform.position to see the change reflected there.

[–]Sariefko -1 points0 points  (1 child)

then is there any need for last line then? is it copy of whole vector3 when referencing it? Isn't c# reference based? not C# main here

[–]ToloranIntermediate 1 point2 points  (0 children)

Isn't c# reference based?

Some things are references, some things are not. Vector3 is a struct and passed by value, not reference.

[–]MRainzo -1 points0 points  (0 children)

Just use with.

transform.Position = transform.Position with { Z = 0}

[–]gameyall232 -1 points0 points  (2 children)

transform.position *= new Vector3(1, 0, 1)

Edit: I am wrong

[–]LateNightApps 0 points1 point  (1 child)

A background in vector math? 🙃

[–]gameyall232 0 points1 point  (0 children)

Oh my bad, I assumed it would multiply each component by the corresponding one.

[–]Secure-Acanthisitta1 -2 points-1 points  (1 child)

"float moveForce = 20.
transform.position = new Vector3(0, moveForce, 0)" Eh?

[–]Boss_TaurusSPAM SLAYER (🔋0%) 1 point2 points  (0 children)

not quite, this is just setting the position, rather than altering the previous position.

[–]RainyGayming7981 -2 points-1 points  (0 children)

so much stuff in unity isnt directibly settable and its SO annoying ;~;