Customizable Navigation Bar

Friday, September 20, 2013

LGUI, My Unity GUI Project

One of the first projects that I took on when we started LoneWolfPack Interactive was creating a new interface system for Unity. We were looking to develop for mobile devices, and after hearing and reading horror stories about the built in Unity GUI system on mobile, we decided that we needed to look at other options.

There were already other GUI options out there, such as EZGUI and the super popular NGUI, but at the end of the day we decided to create our own system. This way we could customize it, add in our own features, and even possibly toss it on the asset store once we got it to where we liked it. As of right now, I am only comfortable with calling it an alpha. There are still a lot of features I would like to add to it, a few bugs here and there that I would like to fix, and a few things that I would like to completely restructure. For now though, it is in a very usable state, and so far it has been used in every one of our games.

What I want to do is show you where we started, where we are now, and what I have planned for it. I'm going to start with the very first iteration of LGUI, which I made a brief post about last year. The horrifying way that I had approached it still gives me shivers, and the second iteration of the system is barely recognizable.

LoneWolfPack Interactive's very first game was called President 3D. We made a mobile version of a popular card game, thinking that it would be a good first project to see how everyone worked together. The first thing I had tackled was an interface system, which was interesting since I didn't really have any ideas in my head as to how it would look or function.

LGUI 1.0 was awful. There were Z-fighting issues everywhere, the editor was ridiculous, and it was missing simple things like scroll bars. There was also the issue of forcing the near clipping plane to a really really small value just so you could have the GUI close to the camera without it going into something within the scene. Doing anything with LGUI was a struggle, even for me, and I was the one who made the thing. This caused our first version of President 3D to have a really ugly interface. We couldn't do what we wanted with it, but we were just happy to have something functional for the most part.

This was how you chose the textures for the buttons and such. You had to click and drag a rectangle to where the texture was on the altas...

This screen was used to position the GUI on the screen. Not only do you have no idea which object is in front of another, but the GUI is aspect ratio locked, so everything would be distorted on different screen sizes.

This is a really really early version of President, but it gives you the idea on how simple the interface for the game looked and what we were stuck with.

So once we released President 3D to the Google Play Store, the first thing I wanted to do was completely scrap LGUI and start from scratch while Jermaine, our other programmer, started on our next project CuBlitz.

The one thing I really wanted to do was create a system that could support both 2D and 3D GUI. I wanted to be able to have a interface like you would see anywhere else, where you have options buttons and a general HUD overlay on the screen, but at the same time have that same system be able to be used for things like buttons on a door's keypad within the level. So that is what I set out to do.

My first obstacle was to figure out a way to have the 2D overlay always draw over top of everything else. I had a few ideas floating around, but what I ended up doing was creating a second camera that would only draw the 2D interface. In order to do this I needed to create a separate layer for just the 2D interface, and make sure that only the LGUI camera would draw that layer. It was a simple bit of code really; since the layers are flags within Unity, I could use simple bit operations to make sure cameras would skip the LGUI layer. Like this:

foreach (Camera c in GameObject.FindObjectsOfType (typeof(Camera)))
    if (c.gameObject.layer != LayerMask.NameToLayer ("LGUI"))
        c.cullingMask = c.cullingMask & (c.cullingMask + (1 << LayerMask.NameToLayer ("LGUI")));

Once that was done I then tried to see what and how much code I could scavenge from the deceased and now rotting LGUI 1.0. After fiddling around with positioning 2D objects in a perspective camera, it dawned on me that I am an idiot, and the entire 2D interface made more sense with an orthographic view. Not only could I skip the trigonometric functions to figure out where objects were supposed to go, it completely solved the Z-fighting issues I had near the edges of the screen.

The next idea I had was to set up the interface in a way that could be stretched, similar to what Unity does with their buttons and scroll bars. The buttons in the built in Unity GUI system use a very small texture that gets stretched out. To do this I created a completely different mesh, where I could stretch the middle and edges separately.

The left mesh is what is used for basic textures that would be used for icons etc. The mesh on the right is for buttons and anything that can be stretched out

And this is what I ended up with:
All four of these buttons use the same texture. The top left is using the basic mesh, and it shows the unstretched 21x21 texture used. The rest of the buttons use the stretchable mesh, and can be stretched to whichever width and height you want without losing detail.
As you can see it turned out really nice, and you can have sharp looking displayed while using extremely small textures.

The next feature I wanted to make sure that was in was compatibility with LTexturePacker, our texture packing tool. Reducing draw calls is important when it comes to mobile, and using atlases and combining materials helps with that greatly. One of the benefits of LTexturePacker is that it exports an XML file with all of the sprite data, plus it exports an enumerator so that you can easily set the UVs in code with the tool. What I wanted to do was set up the editor so that it would read this XML file and you could choose which texture you wanted from a foldout menu. Like this:
With LTexturePacker integration you can pack your textures into an atlas and easily plop them onto your buttons and textures in LGUI

Once LGUI was running and had these features added to it, we decided that we would make an upgrade to President 3D. We scrapped the old interface and built it from the ground up using LGUI 2.0. The main menu was no longer 2D, it was now set up as a 3D menu, where everything was placed on cards being dealt out to the player. It created a much more dynamic feeling menu, and looked a lot cooler.

We took a similar approach in the CuBlitz menu. Most of the main menu was placed in the 3D world and the camera zoomed around the room to go to different menu locations.

Now that LGUI is in a very usable condition, I can finally bring out a list of things I really want to do with it. I'm no longer trying to figure out a way to make a scroll area, or how to mask objects, I can actually take a step back and say, "this is good, but how do I make it great?".

The one recent update I made was an overhaul to the text system for LGUI. I took advantage of Unity 4's markup text and dynamic fonts on mobile to make LGUI's text much more interesting. Now that dynamic fonts are available for all platforms, you can create boldfaced, italics and regular text using the same font file. On top of that you can change the colour, boldness and italics of individual words and letters making for much more interesting labels. Using the new text mark up also reduced the draw calls for the text because now they can all use the same material and have completely different styles and colours. Unity 4 added a lot of features that I still haven't had a chance to completely try out yet (LoneWolfPack Interactive only upgraded to 4 about a month ago), and I am looking forward to seeing what else we can optimize in LGUI.

I've been looking around at other interface systems and games in general to see what they do, what they can't, and how a different approach to things may help build LGUI into a much more functional, easier to use, and useful tool than it already is. As I said earlier, I am only comfortable calling LGUI an alpha. It has the basic features, but it needs a set of scripts to allow this to become a more artist friendly, and robust tool. Here are my future tasks for LGUI:

  • The ability to rotate 2D GUI
    • This is the biggest shortfall with LGUI so far. Anything you create in 2D cannot be rotated, and the way that the 2D is set up it would basically need a rewrite. As of right now it just checks if the mouse is within it's Rect position on the screen, and you can't take rotation into account if you are doing a check that is so simple.
  • Flipping GUI
    • Right now you can't flip the texture on the LGUI objects, so you need a separate texture for something like a left or right arrow. 
  • Better movement and easing tools
    • There is a really basic utility script that has different lerping functions, but it would be nice if you could just call a function and tell your object to bounce to another part of the screen in the time that you want it to.
  • Keeping size ratio of texture
    • Right now there is a script for 2D objects to keep the size ratio of them the same no matter what the screen resolution and aspect ratio is, which is nice, but it shouldn't be a separate script, and it should also be a part of the 3D objects as well, that way you can scale up the texture to which ever size you want and not have to worry about fiddling around and remove stretching
  • Better audio control
    • Right now if a button uses audio it creates its own AudioSource. If you have hundreds of buttons it can be very inefficient, it would be better to have a static audio source that all LGUI objects use and it moves around and plays when necessary.
  • Using custom handles
    • Unity has something called handles. This is what you use when you drag to scale, move or rotate and object, modify the size of colliders etc. It would be really nice to have handles for LGUI objects that way you can just drag and scale or move them instead of copying and pasting values into the editor.
So while LGUI is still extremely useful, you can see that I still have a long ways to go before it is what I would consider finished. I hope you enjoyed reading about my failures, where we are now and where we want to go. That's all for this week guys, and if you did like it remember to share!