Set a RectTranform's pivot without changing its position

Is there a way to change a RectTransform’s pivot point without changing its position? If not, does someone know the math I would have to do to simulate this behavior? Thanks in advance!

5 Answers

5

Well, I managed to figure this one out on my own. Implemented as an extension method of the RectTransform class. Currently only works properly for XY axis-aligned objects.

    public static void SetPivot(this RectTransform rectTransform, Vector2 pivot)
    {
        if (rectTransform == null) return;

        Vector2 size = rectTransform.rect.size;
        Vector2 deltaPivot = rectTransform.pivot - pivot;
        Vector3 deltaPosition = new Vector3(deltaPivot.x * size.x, deltaPivot.y * size.y);
        rectTransform.pivot = pivot;
        rectTransform.localPosition -= deltaPosition;
    }

Thanks man! This does not make sense AT ALL! :D But it works. I have spent whole week trying to assign x,y position of RectTransform, also anchoredPosition and it never came to my mind that it was this simple :)

Awesome, I needed to do just this and was able to drop in your solution with no problems. Thanks!

Note: If you're changing scale at all, you'll need to incorporate that as well: var scale = rectTransform.localScale; ... var deltaPosition = new Vector3( deltaPivot.x * size.x * scale.x, deltaPivot.y * size.y * scale.y);

Do you know how to implement this method when the RectTransform has been rotated as well? It seems to be a bit more complex and I can't figure it out.

+Props to you dude, solid solution and awesome of you to put it in a method for people to copy paste.

Here is a cleaned up version of previous commenters’ extension function, which supports scaling & rotation.

/// <summary>
/// Set pivot without changing the position of the element
/// </summary>
public static void SetPivot(this RectTransform rectTransform, Vector2 pivot)
{
    Vector3 deltaPosition = rectTransform.pivot - pivot;    // get change in pivot
    deltaPosition.Scale(rectTransform.rect.size);           // apply sizing
    deltaPosition.Scale(rectTransform.localScale);          // apply scaling
    deltaPosition = rectTransform.rotation * deltaPosition; // apply rotation
    
    rectTransform.pivot = pivot;                            // change the pivot
    rectTransform.localPosition -= deltaPosition;           // reverse the position change
}

@ jeffreyschoch - This is great! Thanks for sharing.

To get this to work I had to replace rectTransform.rotatation with rectTransform.transform.localRotation

public static void SetPivot(RectTransform target, Vector2 pivot)
{
if (!target) return;

        var offset=pivot - target.pivot;
        offset.Scale(target.rect.size);
        var wordlPos= target.position + target.TransformVector(offset);

        target.pivot = pivot;
        target.position = wordlPos;
    }

Thanks! That was the only solution that worked in my case. Not sure why the previous solutions caused problems in my setup...

To expand on this some, rotation and scale can be accounted for on a single line:

public static void SetPivot(this RectTransform rectTransform, Vector2 pivot)
{
	if (rectTransform == null) return;
	
	Vector2 size = rectTransform.rect.size;
	Vector2 deltaPivot = rectTransform.pivot - pivot;
	Vector3 deltaPosition = rectTransform.rotation * new Vector3((deltaPivot.x * size.x) * rt.localScale.x, (deltaPivot.y * size.y) * rt.localScale.y);
	rectTransform.pivot = pivot;
	rectTransform.localPosition -= deltaPosition;
}

Both the answers by @wck19870829 and @LiterallyJeff (with the rectTransform.rotation change by @jomalomal) work.

I made it a little easier to get the Vector2 position via an enum.

public static void SetPivot(this RectTransform target, PivotPresets preset)
        {
            target.SetPivot(GetVector2FromPivot(preset));
        }
        
        public static void SetPivot2(this RectTransform target, PivotPresets preset)
        {
            target.SetPivot2(GetVector2FromPivot(preset));
        }
        /// <summary>
        /// Point of this is to change the pivot, without moving the object. Method 1
        /// </summary>
        public static void SetPivot(this RectTransform target, Vector2 pivot)
        {
            if (target == null) return;
            var offset=pivot - target.pivot;
            offset.Scale(target.rect.size);
            var wordlPos= target.position + target.TransformVector(offset);
            target.pivot = pivot;
            target.position = wordlPos;
        }
        /// <summary>
        /// Point of this is to change the pivot, without moving the object. Method 2
        /// </summary>
        public static void SetPivot2(this RectTransform rect, Vector2 pivot)
        {
            if (rect == null) return;
            Vector3 deltaPosition = rect.pivot - pivot;    // get change in pivot
            deltaPosition.Scale(rect.rect.size);           // apply sizing
            deltaPosition.Scale(rect.localScale);          // apply scaling
            deltaPosition = rect.transform.localRotation * deltaPosition; // apply rotation
     
            rect.pivot = pivot;                            // change the pivot
            rect.localPosition -= deltaPosition;           // reverse the position change
        }

public enum PivotPresets
    {
        TopLeft,
        TopCenter,
        TopRight,
 
        MiddleLeft,
        MiddleCenter,
        MiddleRight,
 
        BottomLeft,
        BottomCenter,
        BottomRight,
    }

public static Vector2 GetVector2FromPivot(PivotPresets preset)
{
switch (preset)
{
case (PivotPresets.TopLeft):
{
return new Vector2(0, 1);
}
case (PivotPresets.TopCenter):
{
return new Vector2(0.5f, 1);
}
case (PivotPresets.TopRight):
{
return new Vector2(1, 1);
}
case (PivotPresets.MiddleLeft):
{
return new Vector2(0, 0.5f);
}
case (PivotPresets.MiddleCenter):
{
return new Vector2(0.5f, 0.5f);
}
case (PivotPresets.MiddleRight):
{
return new Vector2(1, 0.5f);
}
case (PivotPresets.BottomLeft):
{
return new Vector2(0, 0);
}
case (PivotPresets.BottomCenter):
{
return new Vector2(0.5f, 0);
}
case (PivotPresets.BottomRight):
{
return new Vector2(1, 0);
}
}
return default;
}

public static Vector2 GetVector2FromPivot(PivotPresets preset)
{
switch (preset)
{
case (PivotPresets.TopLeft):
{
return new Vector2(0, 1);
}
case (PivotPresets.TopCenter):
{
return new Vector2(0.5f, 1);
}
case (PivotPresets.TopRight):
{
return new Vector2(1, 1);
}
case (PivotPresets.MiddleLeft):
{
return new Vector2(0, 0.5f);
}
case (PivotPresets.MiddleCenter):
{
return new Vector2(0.5f, 0.5f);
}
case (PivotPresets.MiddleRight):
{
return new Vector2(1, 0.5f);
}
case (PivotPresets.BottomLeft):
{
return new Vector2(0, 0);
}
case (PivotPresets.BottomCenter):
{
return new Vector2(0.5f, 0);
}
case (PivotPresets.BottomRight):
{
return new Vector2(1, 0);
}
}
return default;
}