Grabbing procedurally generated values

Hello everyone!

So I’m trying to build a simple game where the user inputs some values and a procedurally generated grid is built out of procedurally generated cubes of random x, y, and z sizes between a min and max value. I’ve set up a Cube prefab that’s Instantiated when the user presses space and that works fine. It’s built correctly and with the correct x, y, and z. The issues I’m having is:

When I go to build the grid I’m unable to get the random value that I’ve assigned to x, y, and z. I’m trying to get the X and Z values of the previous cube in the grid so I know where to place the next cubes using an offset that gets put into a Vector3(x, 0, z) and then used to instantiate the mext cube at that position. What ends up happening is I just get the prefabs X or Z value on the first call of the function and all the cubes end up being placed wrong.

My current “solution” is to have the cubes be a child of an empty parent object (I call it CubeFolder), when space is pressed I instantiate the first cube and then I use transform.GetChild(x) to hopefully grab the previous boxSize.x value then add it to xOffset. Run some for loops and build the grid. Obviously this hasn’t worked. The code below ends up creating all the cubes on top of one another at the parent’s transform.position. This is because xOffset == 0 and since the cube isn’t generated yet boxSize.x == 0 in the prefab as well.

Any ideas?

_gameObject is the procedurally generated cube prefab.

I hope this makes sense. The idea is very simple, but I might’ve explained it badly.

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

public class BuildGrid : MonoBehaviour
{
    [SerializeField] private int width;             // Number of cubes on the X and Z
    public GameObject _gameObject;

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            BuildSquareGrid();
        }

    }

    public void BuildSquareGrid()
    {
        // Because the grid is a square, the width acts as both the width and height
        if (_gameObject.transform.tag == "Cube")
        {
            // Build grid by deciding where to place the first cube and then use that to run the loops to place the rest relative to the first

            float xOffset = 0;
            float zOffset = 0;

            if(width > 1)
            {
                for (int z = 0; z < width; z++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        Instantiate(_gameObject, new Vector3(xOffset, 0, zOffset), Quaternion.identity, transform);
                        xOffset += transform.GetChild(x).GetComponent<CreateSingleCube>().boxSize.x;
                    }

                    zOffset += transform.GetChild(z).GetComponent<CreateSingleCube>().boxSize.z;
                    xOffset = 0;
                }
            }
        }
    }
}

It looks like this is going to be dependent on one major question, which is, when does your spawned object get a random X or Z? If it’s during Start(), for example, that would not be getting executed in between lines 36 (when it’s instantiated) and 37 (when the values are checked).

Typically in procedural generation, you’d have a function on the child (say, GenerateMe) which is called by BuildSquareGrid explicitly, so that you can control the timing of all this functionality precisely.

Also, Instantiate returns a reference to the object it spawns - you don’t need to do the whole transform.GetChild thing. That’s going to cause big issues when you’re doing multiple rows, as it’s going to just be finding an object in the first row.
(And getting the row itself as on line 40 is also just going to get objects from the first row.)

1 Like

You’re right - I was assigning the random value in Start(). Would a better place be when I call GenerateMe?

I’m struggling to understand how I would instantiate the prefab using this. In my generation script I do have a GenerateMe function that builds a single cube, but when I tried to call it from BuildSquareGrid I kept getting a null reference error. If you could point me to a good example of this kind of thing that would be awesome. This is how I tried:

...
CreateSingleCube csc = new CreateSingleCube();
csc.GenerateMe();
...

How do you get the values in it? Would I create an array of GameObjects to store the references and do

_gameObjects[x] = Instantiate(...);

Or am I not understanding?

You could create another variable for reference within your CreateSingleCube class, and then assign it right after you call GenerateMe()

Well, you can’t use “new” on a MonoBehaviour, you are right to use GetComponent() - it’s just that what you call GetComponent on should be different.

What I’m thinking would look something like:

GameObject thisCube = Instantiate(...);
CreateSingleCube csc = thisCube.GetComponent<CreateSingleCube>();
csc.GenerateMe();
xOffset += csc.boxSize.x;
//if you need to keep references to your generated cubes you can:
cscList.Add(csc);
// and outside of your function:
private List<CreateSingleCube> cscList = new List<CreateSingleCube>();

The zOffset will be trickier, because I assume each cube will have a different Z size, so which one do you increase zOffset by? For example, if it’s the largest of all the cubes in that row, you can keep track of which one that is, offset the zOffset at the end of the row, then reset that to 0.

1 Like

Awesome thank you! I was on the right track, but just needed a better understanding of Unity to get it to work. When I get home from work I’ll try this out. I’ll probably keep references for the time being, but I can’t see myself needing them yet. You never know though!

As far as zOffset, I’m thinking of making it uniform to just make it easier for the time being, essentially making X and Y the only variable sizes. It’ll give a nicer look and as long as I make xOffset perfect to the cubes size it should create a tight grid (no holes between cubes) with varied height and width.

Your idea is also something I thought about. I’ll have to play around with it and see what I like the best.

@StarManta btw it worked like a charm! Thank you very much. I’m currently dealing with zOffset in the way you described as well and it does look nice. Not sure if I’ll keep it or if I want to change it at all, but for now it’ll work.