Simplify Working UI Creation Script *SOLVED*

Good evening.

I’ve written a C# script that is supposed to create a UI text object at runtime the same way it would be achieved from the editor.

It does work and there are no errors, but it’s fairly clunky in my opinion. I was wondering if anyone might be able to assist me in condensing it to any beneficial degree. Is there a more concise way to attain this functionality?

I did note that some of these values are the same as the default values that would be used if I neglected to address them, but leaving them out seems like asking for trouble.

Below I’ve rewritten an example of the script in question. Perhaps you can see that the code is (needlessly?) drawn out. Should I ever need to change the names of the variables, it would prove quite tedious (especially if I used such tactics to create several objects of similar or greater complexity).

Thank you in advance for any assistance provided.
Have a pleasant night.C# Code

{
     private GameObject textObject;
     private Text myText;
     private RectTransform rTrans;
     private Font font;
     private string message;

     void Start ()
     {
          font = Resources.FindObjectsOfTypeAll(typeof(Font))[0] as Font;

          textObject = new GameObject("Text");
          textObject.transform.SetParent(this.transform);
          textObject.layer = 5;

          myText = textObject.AddComponent<Text>();

          rTrans = myText.rectTransform;
          rTrans.localPosition = new Vector3(0, -48, 0);
          rTrans. sizeDelta = new Vector2(480, 48);
          rTrans.anchorMin = new Vector2(1, 1);
          rTrans.anchorMax = new Vector2(1, 1);
          rTrans.pivot = new Vector2(1, 1);
          rTrans.localRotation = Quaternion.identity;
          rTrans.localScale = new Vector3(1, 1, 1);

          myText.font = font;
          myText.text = message;
          myText.fontStyle = FontStyle.Bold;
          myText.fontSize = 18;
          myText.lineSpacing = 1;
          myText.supportRichText = false;
          myText.alignment = TextAnchor.MiddleCenter;
          myText.alignByGeometry = false;
          myText.horizontalOverflow = HorizontalWrapMode.Wrap;
          myText.verticalOverflow = VerticalWrapMode.Truncate;
          myText.resizeTextForBestFit = false;
          myText.color = new Color(255, 255, 255, 1);
          myText.raycastTarget = false;
     }
}

You can use layout elements to do all of your positioning for you. It’s my default for UI, and it actually does better on scaling.

1 Like

Oh, I see. I hadn’t considered layout elements. Are you suggesting, then, that I remove the following (the RectTransform instructions)C# Code

          RectTransform rTrans;
          rTrans = myText.rectTransform;
          rTrans.localPosition = new Vector3(0, -48, 0);
          rTrans. sizeDelta = new Vector2(480, 48);
          rTrans.anchorMin = new Vector2(1, 1);
          rTrans.anchorMax = new Vector2(1, 1);
          rTrans.pivot = new Vector2(1, 1);
          rTrans.localRotation = Quaternion.identity;
          rTrans.localScale = new Vector3(1, 1, 1);

and add a layout element component with sizing instructions instead?C# Code

           LayoutElement layout;
           layout = textObject.AddComponent<LayoutElement>();
           layout.ignoreLayout = false;
           layout.minWidth = 0f;
           layout.minHeight = 0f;
           layout.preferredWidth = 0f;
           layout.preferredHeight = 0f;
           layout.flexibleWidth = 0f;
           layout.flexibleHeight = 0f;

(Where the floats would, of course, be larger than 0.)
I saw a demonstration of layout elements and they do seem to scale in a much smoother manner (the alternative looks rather jerky by comparison). Thank you very much for the suggestion!

Of course, it results in the same number of lines in the script I’m hoping to shorten. My goal is to build a UI from one script (a component of the panel upon which the interface will exist). The reason for this is so that all of the elements can be accessed within said script without the need for public variables or serialized fields. Given that the interface will be comprised of several objects (input fields, sliders, buttons, etc), I was wondering if there might be a more concise way to go about it. The only other possibility that had crossed my mind was creating the various objects from the editor as normal and then collecting them in the script (GetComponentsInChildren) as a series of arrays – but that would be highly undesirable as the various objects would be stored by number rather than by name.

I normally put all of the layout stuff on prefabs, then Instantiate those as needed. It’s essentially the same thing as specifying every parameter in code.

1 Like

My own comments about arrays led me to the idea of creating individual UI objects in their own methods with various arguments which could then be called from elsewhere in the script. In this way, the same code would not have to be rewritten over and over (thereby keeping the script relatively short and sweet). Everything would end up with both a name and a number, making things easy to identify. Of course, this could easily result in methods with a large number of arguments (for example: MethodName(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, … )) – very unattractive. Testing this theory, though, I came up with something like this:C# Code

{
    private GameObject[] textObject = new GameObject[1];
    private Text[] myText = new Text[1];
    private RectTransform[] rTrans = new RectTransform[1];
    private string[] message = new string[1];
    private Font font;

    void Start()
    {
        font = Resources.FindObjectsOfTypeAll(typeof(Font))[0] as Font;

        message[0] = "Hello, World.";
        CreateTextObject(0, "Example_Name", 500f, 500f, 1000f, 1000f, 0f, 0f);
    }

    void CreateTextObject(int i, string myName, float mWidth, float mHeight, float pWidth, float pHeight, float fWidth, float fHeight)
    {
        textObject[i] = new GameObject(myName);
        textObject[i].transform.SetParent(this.transform);
        textObject[i].layer = 5;

        myText[i] = textObject[i].AddComponent<Text>();
        myText[i].font = font;
        myText[i].text = message[i];
        myText[i].fontStyle = FontStyle.Bold;
        myText[i].fontSize = 18;
        myText[i].lineSpacing = 1;
        myText[i].supportRichText = false;
        myText[i].alignment = TextAnchor.MiddleCenter;
        myText[i].alignByGeometry = false;
        myText[i].horizontalOverflow = HorizontalWrapMode.Wrap;
        myText[i].verticalOverflow = VerticalWrapMode.Truncate;
        myText[i].resizeTextForBestFit = true;
        myText[i].color = new Color(255, 255, 255, 1);
        myText[i].raycastTarget = false;

        rTrans[i] = myText[i].rectTransform;
        rTrans[i].localPosition = new Vector3(0f, 0f, 0f);                    //Values would be passed in from arguments.
        rTrans[i].sizeDelta = new Vector2(480f, 48f);                    //Values would be passed in from arguments.
        rTrans[i].anchorMin = new Vector2(0.5f, 0.5f);                    //Values would be passed in from arguments.
        rTrans[i].anchorMax = new Vector2(0.5f, 0.5f);                    //Values would be passed in from arguments.
        rTrans[i].pivot = new Vector2(0.5f, 0.5f);                    //Values would be passed in from arguments.
        rTrans[i].localRotation = Quaternion.identity;
        rTrans[i].localScale = new Vector3(1f, 1f, 1f);                    //Values would be passed in from arguments.
    }
}

This actually makes a lot of sense. (What an obvious approach to overlook! Thank you very much for pointing this out.) This idea would work very nicely because any code would only have to address values that were meant to change from the defaults specified in the prefabs (which would shorten the code significantly). It would also allow me to address each specific instance’s properties from the script that instantiated it.

I imagine that the two approaches could be combined to very nice effect.

Thank you for your assistance!

No worries. Check out some of the tutorials in my signature for examples of how I’ve used layout elements to good effect.

I will have a look. I’m curious to see what else you might have pointed out that I had overlooked.
Thanks, again.