Sunday, November 18, 2018

Auto Setup Level Selection Unity


This example is created using Unity 2018.2.14f. 

In Unity, when you have created a lot of scenes in different .unity files, sometimes it is tough to setup the level selection scene. For example, you create a lot of buttons to load the scene. In the button’s OnClick method, you will have to write the level name. This takes a lot of effort if you have more than 20 scenes and I’m super lazy to do that. So I want to setup all the button using script. When I click the button, it has to load the corresponding scene based on the parameter (ex: level2, level3, etc).



In order to do that, I created LevelSelectEditor.cs and LevelSelect.cs. LevelSelectEditor is a script that show a button to trigger the function where I want to generate all the buttons. Here is the LevelSelectEditor.cs:
using UnityEngine; 

#if UNITY_EDITOR
using UnityEditor;

// This script will copy one button in the level select screen,
// then paste its attribute from the button.
// Until the last button
[CustomEditor(typeof(LevelSelect))]
public class LevelSelectEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();

LevelSelect levelSelect = (LevelSelect)target;

      if (GUILayout.Button("Generate Level Text"))
{
         levelSelect.generateLevelText();
}

if (GUI.changed)
{
EditorUtility.SetDirty(levelSelect);
}
}
}

#endif

And this is the LevelSelect.cs
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.Assertions;
using UnityEditor.Events;

// This class is used to create as many button as possible depends
// on the number of level in the game
public class LevelSelect : MonoBehaviour
{
   public int numLevel = 10;
   public GameObject button; 
   public GameObjectManager goManager;
   
   public void generateLevelText()
   {
      // first we need to make sure that Button game object is not null
      Assert.IsNotNull(button);   

      // hold the information of game object button
      string buttonName = button.name;
      Text levelText = button.gameObject.GetComponentInChildren<Text>();   

      // Button must have level text
      Assert.IsNotNull(levelText); // level text must not null   

      // delete children except first child
      while (transform.childCount > 1)
      {
         // delete it
         Object.DestroyImmediate(transform.GetChild(1).gameObject);
      }

   
      // we iterate from 2 because the first button is already created
      for (int i = 2; i <= numLevel; ++i)
      {
         // we want to change the parameter to match the level name of the scene       
         levelText.text = "level" + i;

         // we create new button and set the parent to the same as the first button object
         // the new button that we created is a copy of a button so it still has the same property
         // however, we need to change the onClick parameter to match the level name
         GameObject objButton = Object.Instantiate(button);
         objButton.transform.SetParent(this.transform);
         objButton.transform.localScale = Vector3.one;
   
         // rename the button
         objButton.name = buttonName;

         // we need the component of the Button
         var btnComponent = objButton.GetComponent<Button>();

         // now we remove the existing levelbuttonpressed method with wrong parameter
         UnityEventTools.RemovePersistentListener<string>(btnComponent.onClick, goManager.loadLevel);

         // then we change it to the new one
         UnityAction<string> action = new UnityAction<string>(goManager.loadLevel);
         UnityEventTools.AddStringPersistentListener(btnComponent.onClick, action, levelText.text);
      } 
   
      // set if back to default text
      levelText.text = "level1";
   }
}

You need to hook up the LevelSelect script to the content gameObject, set the properties (number of level, the button instance, the game object that has callback method), and you're done. The button onClick method will change to level1, level2, level3, etc automatically.