Menu System and UI

For this project, I wanted revenge on making a menu system. In the previous project (Kinzo: God’s Mistake, which I don’t want to showcase for obvious reasons) I felt I had gone about creating the system itself the wrong way and wanted to try doing it right (or rather more right). The first problem with the Kinzo system was that every menu item (buttons and sliders) was hand-placed by me. This obviously took a lot of unnecessary time and so I wanted a more dynamic solution. I did still use a function pointer based system instead of a message based system simply because I didn’t know they existed. Anyhow, this is how the menu manager creates the menus in its Init() function:

myMenus[eGameState::MainMenu].InitMainMenu(); myMenus[eGameState::LevelSelect].InitLevelSelect(); myMenus[eGameState::Options].InitOptions(); myMenus[eGameState::Pause].InitPause(); myMenus[eGameState::Credits].InitCredits(); myMenus[eGameState::HowToPlay].InitHowToPlay();

 
 
 

void Menu::InitMainMenu() {     Delete();     myMenuOptions.clear();     myMenuOptions =     {         new MenuOption("Play", eMenuIcon::Play, &MenuManager::LevelSelect),         new MenuOption("Options", eMenuIcon::Options, &MenuManager::Options),         new MenuOption("Controls", eMenuIcon::Info, &MenuManager::HowToPlay),         new MenuOption("Credits", eMenuIcon::Credits, &MenuManager::Credits),         new MenuOption("Exit", eMenuIcon::Exit, &MenuManager::Exit)     };     PlaceOptions(); }

For example, this is what InitMainMenu() looks like. Delete() is just an assurance that there are no memory leaks since the menu options are new:ed, if by chance the function is called more than once. The first parameter is the name of the item shown in game. The second is an enum mapped to a certain sprite that is rendered in the centre of the wheel to indicate the function of the item. The third is a pointer to the function to call when executed. The dynamic part of this system comes after the menus have been initialized, in the PlaceOptions() function.

 
 
 

The PlaceOptions() function basically calculates, based on the number of items in the menu and a formula, where the item should be. In this case, it’s based on a circle. The SetPosition() function is where the bulk of the magic happens however I won’t show it because it is quite a lot of dull code. It takes the assigned position and rotation and constructs the lines and texts at their desired locations. If I remove, let’s say, the “Controls“ item from the main menu, there won’t be an empty space where that option had been. The rest of the items are spread evenly as if “Controls“ never existed. That is why I claim this system to be dynamic.

The hexagon and line segments are different sprites and are dynamically placed and rotated.

void Menu::PlaceOptions() {     float size = myMenuOptions.size() == 1 ? 4 : myMenuOptions.size();     float mod = 5 - size;     mod = mod == 0 ? 1 : (mod < 0 ? 0 : mod);     float increment = PI / ((size + mod * 2) * PI / 2);     float base = -increment * (((size + mod * 2) - 1) / 2);     float ratio = 9.f / 16;     float r = .5f;     float x, y;     int i = myMenuOptions.size() == 1 ? 4 : mod;     for (MenuItem* item : myMenuOptions)     {         x = .1f + r * cos(base + increment * i) * ratio;         y = 0.5f + r * sin(base + increment * i);         item->SetRotation(base + increment * i);         item->SetPosition({x, y});         i++;     } }

 
 

Thoughts

I know that this obviously isn’t the way of implementing a menu system, and had I had the opportunity today, I would have done it a lot differently. I do however feel like I did a decent job. At the very least, I succeeded in making a more dynamic system that was easier to iterate on, and I had the chance to add a bit more juice to it and make it something truly unique.