Difference in transform.position between a parent gameobject and seperate gameobject?

Hello,
I am seeing a behavior which is not clear to me (or to chat GPT :slight_smile: ).

In script attached to the parent gameobject of a child gameobject when I use the child.transform.position I get the rotated local position of the child gameobject relative to the parent.

In a script attached to a seperate gameobject I get the reference to the same child.transform.position and I then get the world position of the child gameobject.

Is this expected behavior? Why the different behavior?

As per the docs transform.position is always the world position of a game object: Unity - Scripting API: Transform.position

Would need to see more information, code, or an example package to diagnose what’s going on in your case.

My code is complex, thinking how to whittle it down… but here are my debug statements and its output.

External GameObject:
print ("{GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}(): taxi endpoints positions = " + myFlghtOps.parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[myFlghtOps.taxiAnchor_hp_idx].hp_transform.position.ToString() + myFlghtOps.parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[myFlghtOps.taxiAnchorExt_hp_idx].hp_transform.position.ToString()); print ("{GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}(): taxi endpoints localPositions = " + myFlghtOps.parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[myFlghtOps.taxiAnchor_hp_idx].hp_transform.localPosition.ToString() + myFlghtOps.parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[myFlghtOps.taxiAnchorExt_hp_idx].hp_transform.localPosition.ToString());

and its output:

AI_FighterGO::Do_Update(): taxi endpoints positions = (-4622.58, -157.47, 515.93)(-4623.89, -185.58, 523.35)
AI_FighterGO::Do_Update(): taxi endpoints localPositions = (5.80, -3.50, -16.00)(5.80, -3.50, 13.10)

Parent Gameoject
print($“{GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}(): taxi endpoints positions” +
parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[taxiAnchor_hp_idx].hp_transform.position.ToString() +
parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[taxiAnchorExt_hp_idx].hp_transform.position.ToString());

                    print($"{GetType().Name}::{System.Reflection.MethodBase.GetCurrentMethod().Name}(): taxi endpoints localPositions" +
                    parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[taxiAnchor_hp_idx].hp_transform.localPosition.ToString() + 
                    parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[taxiAnchorExt_hp_idx].hp_transform.localPosition.ToString());

Parent Gameobject output:
AI_flightOps::Update(): taxi endpoints positions(-5.96, 16.03, -3.08)(-7.26, -12.08, 4.34)

AI_flightOps::Update(): taxi endpoints localPositions(5.80, -3.50, -16.00)(5.80, -3.50, 13.10)

Hopefully the debug code to show which class/method I am in is not confusing.

Okay that is all incredibly hard to read. Please update that to use code tags.

But I’m not sure what the issue is here. You are getting different results for transform.position as opposed to transform.localPosition, as to be expected for any child transform whose parent is not at world origin.

Sorry…maybe this is a little cleaner. Not sure how to use code tags…

Parent gameobject debug code:

print(“taxi endpoints positions" + parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[taxiAnchor_hp_idx].hp_transform.position.ToString() +
parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[taxiAnchorExt_hp_idx].hp_transform.position.ToString());

print(" taxi endpoints localPositions" + parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[taxiAnchor_hp_idx].hp_transform.localPosition.ToString() + parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[taxiAnchorExt_hp_idx].hp_transform.localPosition.ToString());

Parent gameobject output:
AI_flightOps::Update(): taxi endpoints positions(-5.96, 16.03, -3.08) (-7.26, -12.08, 4.34)
AI_flightOps::Update(): taxi endpoints localPositions(5.80, -3.50, -16.00) (5.80, -3.50, 13.10)

Unrelated GameObject debug code:

print ("taxi endpoints positions = " + myFlghtOps.parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[myFlghtOps.taxiAnchor_hp_idx].hp_transform.position.ToString() + myFlghtOps.parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[myFlghtOps.taxiAnchorExt_hp_idx].hp_transform.position.ToString());

print (“taxi endpoints localPositions = " + myFlghtOps.parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[myFlghtOps.taxiAnchor_hp_idx].hp_transform.localPosition.ToString() + myFlghtOps.parent_GOdetail.ai_mgo.ai_meshGO.HPs_list[myFlghtOps.taxiAnchorExt_hp_idx].hp_transform.localPosition.ToString());

Unrelated gameobject debug output:

AI_FighterGO::Do_Update(): taxi endpoints positions = (-4622.58, -157.47, 515.93) (-4623.89, -185.58, 523.35)
AI_FighterGO::Do_Update(): taxi endpoints localPositions = (5.80, -3.50, -16.00) (5.80, -3.50, 13.10)

In the external gameobject ‘myFlightOps’ is the reference that gets to the same parent_GOdetail class

Just use the tags in the post window:

But again, not sure what the issue is. transform.position is a game object’s world position. It has always worked that way.

While I figure that out… Perhaps you could suggest a way for what I want…

In my external GameObject I would like to get the child’s local position relative to a rotated parent.

I mean… that’s what localPosition already is? It’s effectively an offset applied to the parent’s position. Though this offset is rotated around the parent based on the parents rotation, and then multiplied by the parent’s scale as well, to get the final visual appearance of the game object.

So what’s your end goal here? What result are you trying to actually accomplish?

no unfortunately for a separate gameobject… for a child gameobject localPosition gives the local position with no runtime rotation of the parent applied when I access it from a separate gameobject. Works as you describe from a script attached to the parent gameobject.

I’ve logged the transform name and instance id from the parent gameobject and from the separate gameobject and confirmed they are using the same.

Well yes, but that’s not what I said. Local position is simply an offset from the parent. How that offset is used depends on a few other factors (the parent’s rotation and scale, namely).

If you want the position after any transformations from the parent are applied… what’s what the world position is for, aka transform.position as we’ve already established.

I will ask again: what is your end goal here? What are you actually trying to accomplish in a non-abstract sense?

I am similarly confused - what does “with no runtime rotation of the parent applied” mean?

The parent transform includes orientation, so if you rotate the parent, the child will still have the same localposition offset to the parent’s transform.

If you’re looking for something like the relative position of the child object to the parent object in world space coordinates without having to know / care about rotations, you could do child.transform.position - parent.transform.position, but it’s probably not the best-practice approach

Thanks for the reply guys…

The parent gameobject is going to be rotating during game play, that is what I was trying to express.

I was expecting access to a child transform.position from a script attached to separate gameobject to behave the same as accessing the child transform.position from a script attached to the parent gameobject. It doesn’t. I’ve confirmed I’m using the same transform by logging the transform name and instanceID from the parent and external gameobjects.

From the parent gameobject transform.position unity gives me the local position rotated per parent and for the external game object access to the same transform.position unity gives me the world position.

Was doing a complicated calculation to move another game object toward the child of another gameobject so re-adjusting for this.

This just sounds like a bug somewhere in your code, to be honest.

If you’re writing massive lines of code like this:

Then that’s not surprising. Code like this is incredibly prone to error.