Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Clean UI Coding

Discussion in 'UGUI & TextMesh Pro' started by Vunpac, Jan 6, 2015.

  1. Vunpac

    Vunpac

    Joined:
    Mar 13, 2012
    Posts:
    34
    Alright, so far I really like the new UI system they have. I have been finding it very easy to use, aside from ONE issue. This is the same issue I ran into when using NGUI. I can't seem to find a medium of coding it cleanly. I have put a lot of thought into how I should be doing it, but nothing nice comes to mind. My two biggest issues are

    1. If I use a UIManager (currently doing) with public variables to drag and drop, it's nice, easy to use, BUT does not carry over to other Scenes nicely (since nothing dragged and dropped is saved in prefabs). I will have well over 100 levels each level having it's own scene. Now that being said, copying the previous scene it all works fine. But as soon as I make 1 change. I need to go into every level and change it. (currently have 2 levels but don't want to go farther until I have this issue solved.)

    2. Find all game objects at runtime. This is easier in the long run when it comes to many scenes, but the issue I have with this one is that you are required to have all gameobjects active at runtime in order to find them. This leaves for a pretty big mess in the editor and quite a pain to add or change things, seeing as you are required to disable many things to see what you are doing. Also if you forget to enable anything then you get errors, and it's just a nuisance. (As I was typing this I just though of a way to avoid the mess but still seems like it shouldn't have to be done. I could just loop through the canvas enabling all disabled children, find them then disable the ones I want disabled)

    So is there any way to have a nice UI in the editor, and have access to all of it via scripting, where any changes made to one in one scene would effect all scenes?
     
  2. Feaver1968

    Feaver1968

    Joined:
    Nov 16, 2014
    Posts:
    70
    In your situation of having many scenes, I would suggest you write an editor script that loads all of your scenes one by one, finds the UIManager GameObject and sets all of its needed referenced GameObjects. A little editor scripting helps in all projects. My current setup involves a single scene, but I do what I mentioned above via a ContextMenu item on the manager GameObject. But I'm also interested in the topic.
     
  3. Sebioff

    Sebioff

    Joined:
    Dec 22, 2013
    Posts:
    218
    An easier solution would be to not make your UIManager a GameObject, but a ScriptableObject (maybe with a Singleton accessor) - these work similarly to GameObjects (-> they have public variables you can fill using drag and drop in the Editor), but they're an asset, so you're not required to have an instance of it living in the scene in order to use it (-> if you change anything on it, these changes will be visible wherever you're us.

    Edit: what kind of variables do you have in your UIManager? I assumed it'd be references to UI element Prefabs that you want to instantiate in your scenes, but after re-reading your post I'm not so sure anymore.
     
    Last edited: Jan 6, 2015
    BMayne likes this.
  4. brendan-vance

    brendan-vance

    Joined:
    Jan 16, 2014
    Posts:
    36
    I suppose the first question to ask is: What sort of information are you passing into this UI manager that can't be written to a prefab?
     
    Kiwasi likes this.
  5. Vunpac

    Vunpac

    Joined:
    Mar 13, 2012
    Posts:
    34
    Sorry guys was away on holidays. I am making an android game, so the UI is used for navigation between scenes as well as pausing and game controls (graphics etc.) The UIManager is used to gather all the elements and setup the onclick listeners and such. It's much easier when all UI elements are in a single area so they can reference each other quite easily. For example, when I press the options button it will hide the menu window and open the options window. Often most UI elements need to communicate with one another. I find it easiest when they all belong to the same script.

    I kind of hacked my way around it and created a queue. At the start I find the canvas, recursively accesses every child and children of the child add their active state to the queue then enable them. Find all the objects I need referenced then hide all the ones that were hidden.

    That sounds a bit weird trying to explain it. So I'll just post it.

    Code (CSharp):
    1. private Queue<bool> isActive = new Queue<bool>();
    2.     private void SetAllUITransformsToActive(Transform trans)
    3.     {
    4.         for (int i = 0; i < trans.childCount; i++)
    5.         {
    6.             Transform child = trans.GetChild(i);
    7.             if (child.childCount > 0)
    8.                 SetAllUITransformsToActive(child);
    9.             isActive.Enqueue(child.gameObject.activeSelf);
    10.             child.gameObject.SetActive(true);
    11.         }
    12.     }
    13.  
    14.     private void RevertAllUITransforms(Transform trans)
    15.     {
    16.         for (int i = 0; i < trans.childCount; i++)
    17.         {
    18.             Transform child = trans.GetChild(i);
    19.             if (child.childCount > 0)
    20.                 RevertAllUITransforms(child);
    21.             child.gameObject.SetActive(isActive.Dequeue());
    22.         }
    23.     }
    Here is why I enjoy having all my UI elements accessable in one script

    Code (CSharp):
    1. #region Options
    2.     private void ChangeCamDistance(float value)
    3.     {
    4.         PlayerPrefs.SetFloat(Saves.CamDistance.ToString(), value);
    5.     }
    6.  
    7.     private void OptionsBack()
    8.     {
    9.         pOptions.SetActive(false);
    10.         pMenu.SetActive(true);
    11.     }
    12.     #endregion
    13.  
    14.     #region end game screen
    15.     private void ESReplay()
    16.     {
    17.         SceneLoader.ReloadScene();
    18.     }
    19.  
    20.     private void ESLevelSelect()
    21.     {
    22.         SceneLoader.LoadLevelSelect();
    23.     }
    24.  
    25.     private void ESNextLevel()
    26.     {
    27.         SceneLoader.LoadNextLevel();
    28.     }
    29.     #endregion
    As you can see the back button is as simple as hiding one and showing another.
     
  6. Vunpac

    Vunpac

    Joined:
    Mar 13, 2012
    Posts:
    34
    Just want to mention singleton objects don't carry over to scenes well. I had a big issues with them a while back. Issue is they object is destroyed but none of the static data is. So once it's recreated, the data is there but doesn't like to be referenced easily. Get a lot of unknown data
     
  7. Sebioff

    Sebioff

    Joined:
    Dec 22, 2013
    Posts:
    218
    Oh, I didn't even expect them to carry over scenes - interesting to know though, thanks!

    So, regarding your problem it sounds like prefabs are the way to go as brendan.vance suggested. I suppose they could be a bit annoying to work with though, so here's an alternative idea: you already have your levels separated into scenes, so what if you put your UI into it's own scene and then load the level in using Application.LoadLevelAdditive()? When loading a new level, destroy everything and load the UI new (+ the next level).
    Alternatively, using DontDestroyOnLoad to keep your UI from being destroyed when loading the next level should work as well.
     
  8. Vunpac

    Vunpac

    Joined:
    Mar 13, 2012
    Posts:
    34
    Ah sorry forgot to specify, when creating prefabs any variable that is populated via drag and drop with objects from the scene doesn't carry over to other scenes. I avoid drag and drop where ever possible when it comes to filling variables from objects that are in the scene.

    @Sebioff I forgot DontDestroyOnLoad even exsisted lol. I'm not sure how I feel about it though. I am not a fan of persisting data when going through so many scenes (I tend to think static data should be used for functionality only eg Math classes funtions, not for storing main data). For now my solution is working just fine. Just "feels" dirty lol. I guess since DontDestroyOnLoad is around that it is probably the intended option at this point. I just wish there was a better way to access the UI objects. Almost like an object. I think I'll do some digging on DontDestroyOnLoad. I'm curious how it actually transfers the data over.

    Edit: I looked into DontDestroyOnLoad and it does in fact make the Object static. I don't like that. Another issue I though of with that is it ONLY makes it static in the scene it was called in. So if I made it static in scene 1 then it will ONLY ever make it to other levels if scene 1 is loaded first. So that could pose many issues when selecting any level.

    Another thing I forgot to mention is you're LoadLevelAdditive. I think that one might actually work, from what I read it seems ideal. I would have to test that one out. I never even heard of that one until you just mentioned it so I thank you for that.
     
    Last edited: Jan 9, 2015