11 May 2019

What Even is a Pivot Anyway?

unity

Recently I have been working a lot with Unity’s UI system. The editor itself is a great tool, I’m able to quickly make what I want and have it respond to changes in screen sizes without too much effort. Trying to control it programatically, trying to drive a completely dynamic layout, however is a lot more painful. I can’t wait for the new UIElements system to be usable in-game.

It’s not that it’s buggy - it’s not - it’s that the information in the editor window is not transferable to code. I can’t set the localPosition of an object and expect it to be where it would be if I set the same value in (what looks like) the same places in the editor.

So we sensibly head to the documentation. On the whole, Unity’s manuals and documentation (and all their other resources) are great, but the docs for the components in the UI system can be a little …vague. For instance RectTransform.pivot is documented simply as

The normalized position in this RectTransform that it rotates around

Hmmm. I’m not sure what that’s supposed to mean. This isn’t even complete. A pivot is the origin point from which all transformations take place - including rotation but also scaling and translation. This means the position of an object within its parent relies on its pivot, we can prove this with a little experimentation.

Game objects with different pivots with a position of (0,0)

The image shows 5 game objects with different pivots, each has a x and y set to zero in the editor. As you can see, the pivot how the object is positioned within its parent and in which direction the object scales. It would also describe the point on the object around which it was rotated.

This means you need to take the pivot into account when trying to position an object from code - when building a custom layout component, for instance. As an example, we could take a custom layout that places items in the middle and corners of itself, like in the image above. We could control the pivots from the layout, and it would work out of the box, but we could do better.

In Unity, (0, 0) is the bottom left corner in the UI system. Setting the pivot of an object has the effect of moving the object away from the bottom corner of its parent, it also moves the point from which the width and height of the object extend. By ‘undoing’ these two effects we can then position the object where we want, regardless of the pivot.

public static void SetPosition(this RectTransform rectTransform, float x, float y)
  {
      RectTransform parent = (RectTransform)rectTransform.parent;
      rectTransform.anchoredPosition =
          // Move the object origin to the bottom left
          new Vector2(
            rectTransform.rect.width,
            rectTransform.rect.height
          ) * rectTransform.pivot

          // Move the parent origin to bottom left
          - new Vector2(
            parent.rect.width,
            parent.rect.height
          ) * rectTransform.pivot

          // Apply consistent translation
          + new Vector2(x, y);
  }

This adds an extension method to RectTransform. Calling this on an object with (0, 0) places the object in the bottom left corner of the parent, no matter the pivot value of child. Calling it with (parentWidth-childWidth, parentHeight-childHeight) places it in the top right corner.

Example project can be found on github.