I am trying to get my hands on the (not that) new UI system by experimenting a bit.

I tried to do some Sudoku style layout. (cause lets start simple). Well, seems not that simple for me right now.

For the namings: the game field of Sudoko holds 9 (3x3) cells where each cell holds 9 (3x3) fields.

It is basically a Panel (game field) which has a Grid Layout Group with column count fixed to 3. Adding 9 child panels gives me the 3x3 cells. Now each cell gets another Grid Layout Group with column count fixed to 3. Adding another 9 panels (fields) to each cell panel results in the Sudoku style layout with 3x3 cells and each cell has 3x3 fields.

Now if the screen gets resized the cell sizes do not scale. After googling a bit I found (here or on Unity answers) the answer to a different setup to add a little script calculating the cell sizes dynamically. The content of UpdateCellSizes() is based on the other question.

I ended up with this little script:

[RequireComponent(typeof(RectTransform), typeof(GridLayoutGroup))] 
public class UIFlexibleGridController : MonoBehaviour {

     private RectTransform rt;
     private GridLayoutGroup glg;
     private float lastWidth;
     private float lastHeight;
     private float cellAspectRatio;
     public bool scaleOnlyOnStartup = true;
     public bool keepAspectRatio = false;
     public bool keepSpacingZero = true;

     void Start () {
         rt = GetComponent();
         glg = GetComponent();
         if (rt == null || glg == null) {
             Debug.LogError("UIFlexibleGridController couldn't find a RectTransform or a GridLayoutGroup");
         lastWidth = rt.rect.width;
         lastHeight = rt.rect.height;
         cellAspectRatio = rt.rect.width / rt.rect.height;
         if (glg.constraint == GridLayoutGroup.Constraint.FixedColumnCount || glg.constraint == GridLayoutGroup.Constraint.FixedRowCount) {
         } else {
             Debug.LogWarning("GridLayoutGroup contraints do not make this UIFlexibleGridController necessary. Consider removing it.");

     void Update () {
         if (scaleOnlyOnStartup) return;
         if (!HasSizedChanged()) return;
         lastWidth = rt.rect.width;
         lastHeight = rt.rect.height;

     private bool HasSizedChanged() {
         return lastHeight != rt.rect.height || lastWidth != rt.rect.width;

     private string GameObjectPathName(Transform t) {
         if (t.parent == null) {
             return t.name;
         } else {
             return GameObjectPathName(t.parent) + "/" + t.name;

     private void UpdateCellSizes() {
         float w = 0f;
         float h = 0f;
         float sx = 0f;
         float sy = 0f;
         w = (rt.rect.width / (float)glg.constraintCount);
         h = (rt.rect.height / (float)glg.constraintCount);
         if (w == 0 || h == 0) {
             Debug.LogError(string.Format("Invalid width ({0}) or height ({1}) at {2}", w, h, GameObjectPathName(this.transform)));
         if (!keepSpacingZero) {
             w = (int) w;
             h = (int) h;
         if (keepAspectRatio) {
             h = w * cellAspectRatio;
         Vector2 newSize = glg.cellSize;
         newSize.x = w;
         newSize.y = h;
         glg.cellSize = newSize;
         if (!keepSpacingZero && glg.constraintCount != 1) {
             sx = (rt.rect.width - (w * glg.constraintCount)) / (float)(glg.constraintCount-1);
             sy = (rt.rect.height - (w * glg.constraintCount)) / (float)(glg.constraintCount-1);
         Vector2 newSpacing = glg.spacing;
         newSpacing.x = sx;
         newSpacing.y = sy;
         glg.spacing = newSpacing;

But when I ran it it states that width and height are zero for each cell holding the nine fields. I try to extract the scenario from my project where this message appears only on each second start and works on the other starts.

Does anyone know if that should work or it can't work by the UI system design?


2 Answers 2


Myself, I wouldn't use any scripts for this at all. Just the anchors built into the RectTransform.

Add 9 empty game object children to your canvas (or the container within your canvas that should contain the play field).

Set the horizontal anchors for them three at a time:

  • for the left three: min X = 0, max X = 0.3333333
  • for the middle three: min X = 0.3333333, max X = 0.66666666
  • for the right three: min X = 0.6666666, max X = 1

Then set the vertical anchors again three at a time:

  • for the bottom three: min Y = 0, max Y = 0.3333333
  • for the middle three: min Y = 0.3333333, max Y = 0.66666666
  • for the top three: min Y = 0.6666666, max Y = 1

Set their left, right, top, bottom position offsets to zero.

These anchors are positioned as fractions of the parent size, so this ensures we get 3 equal-width columns and equal-height rows filling the parent. (You can enforce a square aspect ratio on just the parent, and the benefit will apply to these without any additional scripting)

Animated example of 9x9 grid rescaling to fit

You can repeat this same trick for the number fields inside each sub-grid container. Again, since the anchors are relative to the parent, this will fractally nest a 3x3 grid inside the 3x3 grid.

  • \$\begingroup\$ He just did what you call a pro gamer move \$\endgroup\$
    – Vlad
    Jul 20, 2020 at 15:23

This line..


You're checking there is a constraint with:

if (glg.constraint ==  GridLayoutGroup.Constraint.FixedColumnCount || 
glg.constraint ==  
GridLayoutGroup.Constraint.FixedRowCount) {

But UpdateCellSizes(); is dividing by glg.constraintCount, which doesn't exist. Try checking which constraint is active and passing to UpdateCellSizes(int constraint).

Thanks for the script, good stuffs, asides from that I think! :)


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .