1

I'm attempting to make a level select that requires as little upkeep as possible as I intend on adding updates to add more levels (Unity Scenes) in unity.

To account for this I'm attempting to get the level select to create buttons for each level in the Unity Build settings, and then create a template object from a prefab in which it can map buttons it creates onto.

I have it mostly working, but for some reason, it's mapping the buttons in the wrong order, I'm trying to go down to up and Unity appears to be grabbing the Gameobjects is a random order.

Here is my code:

    private Scene[] levels;
    private int currentButtonId = 1;   

    public Transform buttonsHolder;
    public GameObject buttonPrefab;
    public GameObject buttonSlotsPrefab;

    private GameObject[] levelButtonSlots;
    private int currentLevelSlot = 0;
    private int numSlotsToMove = 0;

 private void Start()
    {            
        var sceneCount = SceneManager.sceneCountInBuildSettings;
        levels = new Scene[sceneCount];
        for (var i = 0; i < sceneCount; i++) 
        {
            // Beginning Setup
            levels[i] = SceneManager.GetSceneByBuildIndex(i);

            // Look for Level Placement Slots
            levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot");

            // If there aren't enough Level Placement Slots make more by creating a template
            if(levelButtonSlots.Length < levels.Length)
            {
                GameObject buttonSlots = Instantiate(buttonSlotsPrefab);
                buttonSlots.transform.position = new Vector2(0, 10 * numSlotsToMove);
                numSlotsToMove++;
            }
            // Go get those new placement slots
            levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot");

            // Create Button
            GameObject currentButton = Instantiate(buttonPrefab, buttonsHolder);
            // Move it to the next slot
            currentButton.transform.position = levelButtonSlots[currentLevelSlot].transform.position;
            currentLevelSlot++;

            // Add Text to a new button
            TextMeshProUGUI buttonText = currentButton.GetComponentInChildren<TextMeshProUGUI>();
            buttonText.text = (currentButtonId.ToString());

            // Setup what which scene clicking a button will do
            ButtonManager buttonScript = currentButton.GetComponentInChildren<ButtonManager>();
            buttonScript.sceneToLoad = currentButtonId;

            currentButtonId++;
        }             
    }

The buttonsHolder variable is set in the editor and is the canvas. The buttonPrefab is a TextMeshPro button prefab I set in the editor, it has the level Buttons tag and a simple script that loads the specified scene when clicked. And the buttonSlotsPrefab is a gameobject prefab that I set in the editor, it has the Button Placement Slot tag and it contains 8 other empty gameobjects each with the level slot tag, I use these 8 objects as the guides as to where the buttons should be placed on runtime.

Again, my goal is to place the buttons going from bottom to top, but instead, Unity spits out this on runtime for no apparent reason:

I'm sorry about the naming convention some variables have, I'm tired and stressed as I've been at this 2 two days straight. I will fix those up once I get things working.

After further testing, I have noticed that when I first create buttonSlotPrefab, everything works perfectly, however, after restarting Unity (Not changing any files) when I run the game again after a restart the order gets randomized. Could this be a bug in the Unity Engine?

Jhon Piper
  • 513
  • 2
  • 8
  • 21
  • 1
    sort them by positional value? is it because thats the order in the heirachy? – BugFinder May 19 '20 at 22:34
  • hard to say exactly how to sort but you just need to sort your array of game objects after you call find game object with tag – vasmos May 19 '20 at 22:36
  • @BugFinder You would think so, but I've checked a dozen times to make sure they are in the right order and they are, I've added a section to my OP to explain something new I've encountered which might affect things. – Jhon Piper May 19 '20 at 22:39
  • @vasmos I know you said it's hard to say how you would sort the array after looking for the tags but can you think of any way? It may be a final resort. – Jhon Piper May 19 '20 at 22:41
  • post the code for the script attached to levelButtonSlots – vasmos May 19 '20 at 22:50
  • @vasmos They have no scripts attached, they are just placeholders that say where the buttonPrefab's should be Instantiated – Jhon Piper May 19 '20 at 22:58
  • okay I posted an answer, may need some tweeking but should sort – vasmos May 19 '20 at 23:09
  • Well if it’s 2d you can sort the results by the y .. the oils seem a simplest option – BugFinder May 20 '20 at 07:32
  • @BugFinder Forgive me, as a newer programmer I'm still getting used to arrays, how would you sort the results by their position on the y axis? – Jhon Piper May 20 '20 at 19:20
  • hard to understant your program, is it possible to have a package somewhere to test it? is it this line which causes problem: `currentButton.transform.position = levelButtonSlots[currentLevelSlot].transform.position;` the position of button is not right? – Frenchy May 22 '20 at 06:27

1 Answers1

1

If i understant your problem, you have a problem between number of button and slot: you have slot spaced on axis y and you havent always the levelsolt[0] at the bottom of screen, so

you could order (ascending or descending i dunno) the levelslot on y axis before creating button: i am using Linq so add using System.Linq;

if (levelButtonSlots.Any())//Same thing than levelButtonSlots.Length > 0
{
    levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot").OrderBy(go => go.transform.position.y).ToArray();
}

or

    levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot").OrderByDescending(go => go.transform.position.y).ToArray();

I have added a test if no value in array, but maybe the error doesnt exist, i havent tested that

Frenchy
  • 16,386
  • 3
  • 16
  • 39
  • Hmm... Unity doesn't seem to recognize `OrderBy` or `OrderByDescending`, it throws out this error in both cases: "Assets\Scripts\UI\Level Select\LevelSelectManager.cs(64,80): error CS1061: 'GameObject[]' does not contain a definition for 'OrderBy' and no accessible extension method 'OrderBy' accepting a first argument of type 'GameObject[]' could be found (are you missing a using directive or an assembly reference?)" – Jhon Piper May 22 '20 at 15:41
  • 1
    have you added `using System.Linq;`? – Frenchy May 22 '20 at 15:42
  • That was is, System.Linq was the dependency that was needed, and after testing `OrderBy` it works flawlessly. I can't believe I spent so much time trying so many things to fix the issue only to have one line fix everything. Thank you! I can't give you the bounty for another 6 hours but you'll be getting it when the time comes. – Jhon Piper May 22 '20 at 15:56
  • the difficulty for me (french) was to understand english!!..so happy to resolve your problem – Frenchy May 22 '20 at 15:58
  • i suggest you to test `levelButtonSlots.Length` is not Zero before ordering it – Frenchy May 22 '20 at 16:02
  • Like this? levelButtonSlots = GameObject.FindGameObjectsWithTag("Level Slot"); if(levelButtonSlots.Length > 0) levelButtonSlots.OrderBy(go => go.transform.position.y).ToArray(); – Jhon Piper May 22 '20 at 16:09
  • see my answer modified, you forget to save the result of sort in the variable... – Frenchy May 22 '20 at 16:20
  • Ah, that makes more sense. Thank you again! – Jhon Piper May 22 '20 at 16:31
  • like i say,maybe the test is not useful, i havent tested. you just check if no error with empty array. – Frenchy May 22 '20 at 16:35
  • It's possible the test won't be useful. But I have found that as a general thing that checking always makes debugging easier. Even if I simply add a `else { Debug.Log("I don't see any level slots"); }` – Jhon Piper May 22 '20 at 16:49
  • Ether way, thank you for bringing it up as an option. – Jhon Piper May 22 '20 at 17:04