Scale parent but don't scale children

I have a UI element comprised of a Quad and 4 children buttons to that Quad.

At runtime I instantiate and resize this Quad.

However, I don’t want to change the scale of the attached children buttons.

How can this be accomplished?

.SetParent() can take an optional second argument… I forget what it’s called and what its default is… check docs for .SetParent().

2 Likes

Are you sure the 4 child buttons need to be a child of the Quad? I’d first try making the Quad a 5th child of the same parent. Then resizing the Quad does nothing to the other 4 child objects.

Otherwise I might just try to brute force it. Unparent the children, resize the quad, reparent the children.

2 Likes

A transform’s scale will always inherit from the parent, I don’t think there’s a way around it, what @Kurt-Dekker is referring to keeps the world position (or whatever it does) only on the parent assignment, it doesn’t set a “state”.
Try achieving what you want with by resizing the size delta and not the scale and anchoring/pivoting the elements as you wish.

if you’ll elaborate on what you wanna do we might be able to offer a different approach.

1 Like

If we’re talking of the same thing, I think perhaps I understand it differently? That second argument does make a difference to the scale… here is my demo.

using UnityEngine;

// drop this on any GameObject in your scene to see it run.
// @kurtdekker

public class SetParentTrueFalse : MonoBehaviour
{
    void Start ()
    {
        // make 2 identical half-scaled GameObjects,
        // left and right, slightly rotated on Y
        var left = new GameObject("Left");
        left.transform.position = Vector3.left * 1.5f;
        left.transform.rotation = Quaternion.Euler( 0, 30, 0);
        left.transform.localScale = Vector3.one * 0.5f;

        // identical to above, just 3 world units right(er)
        var right = new GameObject("Right");
        right.transform.position = Vector3.right* 1.5f;
        right.transform.rotation = Quaternion.Euler( 0, 30, 0);
        right.transform.localScale = Vector3.one * 0.5f;

        // make a cube and parent to left, with TRUE
        var cube1 = GameObject.CreatePrimitive( PrimitiveType.Cube);
        cube1.transform.SetParent( left.transform, true);

        // make a cube and parent to right, with FALSE
        var cube2 = GameObject.CreatePrimitive( PrimitiveType.Cube);
        cube2.transform.SetParent( right.transform, false);

        // NOTE left cube does not move or scale or rotate, whereas
        // right cube will move AND scale AND rotate.
    }
}

Yeah yeah, that’s not what I meant though.
what I was saying is that it only applies to when you set the parent, it doesn’t block future changes from scaling/etc.

the docs says “worldPositionStays: If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.”

That’s true, but I think the OP actually builds that prefab in the editor. So he does not attach the buttons at runtime. He just scales the whole thing after he instantiated the prefab.

Also note that if you pass “true” to the “worldPositionStays” parameter you get the same behaviour as just setting the parent property. Namely Unity will try to calculate the proper local scale / position / rotation in order for the child to keep its world scale / position / rotation. Note that I said Unity “tries” to keep those, however that is not always possible. Obviously if the parent has a degenerate scale (scale of 0) you can not fix that by cranking up the childs scale as it would have to be infinity. Likewise non uniform scaling combined with rotations in more than 1 parent can result in a shear operation. This can not be undone with a single TRS matrix / Transform.

So the solution that Joe suggested is the correct way. Don’t scale the parent at all but make the quad a child of an empty parent and just scale the quad.

And expanding on this, with any hierarchy it is always best to never scale or rotate anything but the very tip pieces, and PERHAPS the root piece if you know wtf you’re doing, but generally nothing else in between. You are just asking for trouble and heartache down the line as your prefab / scene grows.

Hm … I should write myself an editor tool that validates “only children have non-identity scales or rotations” in a prefab or scene. Hm.

1 Like

It is for 3D.But I believe it is also work on UI. Just change the code suitable for UI
Try this for reference -

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

public class FreezeScale : MonoBehaviour
{
Vector3 parentScale;
Vector3 selfScale;
Vector3 scale;

private void Start()
{
scale = transform.localScale;
}

private void Update()
{
parentScale = transform.parent.localScale;
selfScale = new Vector3 (scale.x / parentScale.x, scale.y / parentScale.y, scale.z / parentScale.z);
transform.localScale = selfScale;
}
}

3 Likes

///


/// Keeps the child gameObject scaling fixed on scaling the parent.
///

public class ScaleParentButNotChild : MonoBehaviour
{
[SerializeField] GameObject _parentObject;
[SerializeField] GameObject _childObject;
Vector3 _initialChildScale;
private void Start()
{
_initialChildScale = _childObject.transform.localScale;
}
private void Update()
{
ScaleParentObjectButNotChild(_parentObject, _childObject, _initialChildScale);
}
void ScaleParentObjectButNotChild(GameObject parentObject, GameObject childObject, Vector3 initialChildScale)
{
if (!IsValidParentChild(parentObject, childObject))
return;
Vector3 parentScale = parentObject.transform.localScale;
Vector3 newChildScale = new Vector3(initialChildScale.x / parentScale.x, initialChildScale.y / parentScale.y, initialChildScale.z / parentScale.z);
childObject.transform.localScale = newChildScale;
}
bool IsValidParentChild(GameObject parentObject, GameObject childObject)
{
if (childObject.transform.parent == parentObject.transform)
return true;
Debug.LogError(“Objects are not in Parent-Child relationship”);
return false;
}
}

1 Like