UI and Menu Editor
Motivation
Since we had developed our own editor for game projects we wanted to do everything in it. In the previous project the UI and Menus had mostly been implemented by a programmer so to alleviate some of that workload from programmers and give artists more freedom and control over their work, I wanted to make this for them.
Goal
The main goal was to make it possible for anyone in the team to set up UI or a menu easily through the editor interface. There were four things I knew from the start I wanted to implement; 2D sprites, text, buttons and sliders.
2D Sprites
-
Transforming: Position,
rotation and scale -
Easily change sprite
-
Change render order
-
Change color
Text
-
Edit text
-
Center text
-
Change font and size
-
Change color
-
Move text in relation to object it is attached to
-
Scriptable
Buttons
-
Set sprites for 3 states
-
Standars, hover and clicked​
-
View and edit clickable space
-
Scriptable
Sliders
-
Set sprites for 3 states
-
Standars, hover and clicked​
-
View and edit clickable space
-
Scriptable
Process
Since I had already set up a system for these, the first thing that needed to be done was to move them into the ECS. In our previous project these classes had been set up outside of it but now I had to clean up and edit a lot of that. First thing I did was change the various classes to system classes and remove a lot of the functionality as it would be controlled very differently from before.
The UISystem is better described as a SpriteSystem and it was the first one i set up as both the button- and the slider-system would be very similar and based on the same structure in many ways.
​
Here you can see that the system can set render order in form of priority which can be sorted and changed as sprites are added and removed. This was an essential addition to it because when saving sprites in a scene the render order could be somewhat randomized when loaded in.
Each system holds and runs the update function for its entities and if they need other functions to handle the entities as a whole it is dealt with in the system. The entities in the system holds components relevant to it, like the UISystem only holds entities that have a Sprite2D component and the ButtonSystem holds entities that have a Button component.
For example the button component saves the paths to the textures of the various states it can be in. When the button is loaded in the paths are converted to an index system. The indexes are not saved to the json file as they can differ from each time the component is loaded in.
The active area offset and the active area pivot makes it possible to change the size and position of the clickable space without moving the button around. These two variables are then added to the collision calculation between the cursor and button.
Clickable Area Test
The sliders are similar to the buttons in many ways as they save several paths. One for the frame, one for the slider and one for the backdrop. The backdrop can be removed and you can see this in the crosshair that was implemented in Solar Uprising. The crosshair itself is the frame of the slider and the slider is the blue bar under it and no backdrop.
Here you can see me testing out the slider component, I have already added it to the entity and am checking that the bar offset and max size works as intended as well as the backdrop settings.
Scripting
Sliders and buttons also need to be able to connect to various data and that's where scripting aspect came in. The reason for wanting to implement scripting was to make it easy for anyone to add functionality to buttons and sliders without having to go into code to set it up. This part was maybe one of the more challenging parts of this projects in terms of planning. I am not used to scripting myself so planning out what nodes that needed to be added to our scripting system was difficult.
Here you see an example of a script implemented on the buttons. With the Button Activation node you can set whether the button is to be activated on release or when clicked. This is here as some buttons such as the "play game" button should be activated when the mouse is released while the buttons to change audio levels should be activated when held.
The PopStates node takes a number of pops of the statestack that it should do, meaning that it can go back one state. This is used for the Resume game button and the Back buttons in the menus. It also reads whether it should load a level or not as some states should only pop. Like the Resume game button where you only want to pop the state, while if we want to go from in game to main menu you also want to load the menu level.
At the end of the project it was possible to setup a menu system with buttons and sliders almost completely through the editor. The only thing that ended up having to lean on a programmer was setting up the various states that the scripts were using.
Improvments
With the time frame allowed for this project I did set the scope a little bit too high as I ended up not having time to polish the tool. This means there some user friendly features ended up lacking. like highlight for selected UI element or gizmos for them.
​
Improvements to the tool:
-
Highlighting selected object
-
2D Gizmos for the 2D elements
Improvements to the UI system:
-
Rotation of buttons and their clickable space
-
Sliders where you can set the size by clicking on it (for settings sliders, like volume)
-
Adding a button to sliders so you can pull the button around (for settings sliders, like volume)
-
As seen in Spite: Divine Descent​
-
I am not sure that the implementation of the statestack in this project was the best. We used an ECS that was tied to the scenes and it became tricky separating the UI from the scenes. This meant that when adding a sprite it ended up in the scene's ECS so if you wanted to push a state without loading a new level and add a menu on top of that it ended up connected to the scene. When the state was poped the UI was still visible while in previous projects the UI was always local to the state rather than the scene so when the state was poped the UI was removed at the same time. It seems like the UI would have been better off in its own system free from the scenes to avoid this issue with the menus.
This project was a lot of fun and I have always loved to work closely with graphics and the implementation of it. So getting to develop a tool that makes the UI pipeline faster as it is directly in engine was really rewarding. Due to the time constraint I think it would have been a good idea to focus on a smaller aspect of the editor to give me more time to polish and really make it into what I wanted. This project posed some new challenges and I would have loved to spend more time on this engine and editor to get it to a more polished state. I am still happy with what I have accomplished but I can't help but see the potential of what it could have been. It does make me more motivated to work on engine and editor tools in a professional setting and I hope to be able to make future tools better!