8
\$\begingroup\$

I am getting started with game programming. I am designing a game that starts by taking you through a series of menu screens. I am interested in learning how this is typically structured in professional game development.

Currently I handle the controller inputs from the main file and have individual classes for each menu screen. When the user makes a selection, I need to transition to the next menu screen. It seems like I either need some sort of "menu screen manager" for keeping track of the previous selection and transitioning between menu screens, and/or I need to pass the controller input handling down to the menu objects, since I want to use up/down on one menu and left/right on the other menu.

How would a professional game programmer architect/design this?

require "components/first_menu"
require "components/second_menu"

first_menu = FirstMenu.new
second_menu = SecondMenu.new
menu = first_menu
menu.render

on(controller: "up")   { menu.prev_option }
on(controller: "left") { menu.prev_option }

on(controller: "down")  { menu.next_option }
on(controller: "right") { menu.next_option }

on(controller: "buttonA") do
  if menu.is_a? FirstMenu
    menu.hide
    selected_option = menu.select_option
    menu = second_menu
    menu.show(selected_option)
  else
    selected_option = menu.select_option
    launch_game(selected_option)
  end
end
\$\endgroup\$
7
  • \$\begingroup\$ Just out of curiosity, why Ruby? Ruby is an atypical first choice to learn game programming with, you may find many more resources for game development in C# which is a fairly beginner friendly language with free engines like Unity to play around with. \$\endgroup\$
    – user5665
    Jul 2, 2016 at 14:10
  • \$\begingroup\$ @MattJensJensen True. I'm using Ruby because I'm an experienced Ruby web developer and am building a Ruby game engine which uses SDL under the hood. But I'm new to some of the techniques and design patterns in professional game programming. \$\endgroup\$
    – Andrew
    Jul 2, 2016 at 17:56
  • 1
    \$\begingroup\$ Fair enough, I love Ruby for what it's good at, but it might accelerate your learning to take a shot at C++ or C# just because many of the great resources in game software design are primarily targeted at the concepts in those languages and what those languages expose in terms of efficiency and performance. Ruby is beautiful, but it's hard to squeeze performance out of it, at least C++ was designed to make it as beautiful as possible to squeeze the performance out. \$\endgroup\$
    – user5665
    Jul 3, 2016 at 7:40
  • \$\begingroup\$ @Andrew if your objective is to build an engine, I wouldn't bother with making a menu system. Just the ability to organize entities and draw them and the physics system in a GUI is a great place to start. Unlike typical software, menus are not required in a game. \$\endgroup\$
    – Evorlor
    Jul 3, 2016 at 10:26
  • \$\begingroup\$ Creating a game engine is unrelated to my question about creating a menu system. If you know of any resources (in C, C++, C#, etc) which cover menu system design, please let me know. I'm not looking for a Ruby answer. Just trying to understand the techniques and design patterns. \$\endgroup\$
    – Andrew
    Jul 3, 2016 at 14:19

2 Answers 2

4
\$\begingroup\$

If you have nested menus, you can use a stack. I'm not familiar with Ruby, I think there is no 'stack' object. Similar behavious can be created using an Array. You only add items to the end (push), remove the last item (pop) and can interact with the last item (peek).

So the basic idea is: you define a menu class with update() and draw(). This should be a basic menu implementation. Then you create a menumanager that controls what menu gets updated and displayed in the gameloop. This implements the stack: it can add or remove menu's but only displays and updates the top item in the stack. If a nested menuitem should appear, push the new menu class on the Stack. Your controller and display logic should only display the menu that is on top of the stack.

Example:

Base Menu:

  • Game Settings -> push gamesettings menu on stack
  • Graphic Settings -> push graphics menu on stack
  • Audio Settings -> push audio menu on stack
  • Exit -> exit menu

Gamesettings menu:

  • Number of lives [1|3|5]
  • Difficulty [easy|normal|hard]
  • Choose language -> push language menu on stack
  • Exit -> pop self from stack

Language menu:

  • Language [English|Deutch|Nederlands|Français]
  • Subtitles [Off|White|Yellow]

Start with the stack by pushing the base menu onto it.

The menumanager will only update and display the top of the stack (this is a 'peek' at the top of the stack).

If the player picks the game settings menu; we push that menu onto the stack. Since we're only updating and displaying the top of the stack the 'game settings' menu is shown and manipulated. Suppose the menu goes deeper; you can just push the next menu onto the stack and update and display that one.

The neat trick is, that if the top menu is removed ("popped") from the stack, the underlying menu is shown - you simply go down a level. As added bonus, that menu will be in te state the player left it!

Now to expand this idea; you can use a stack for your game states as well. This way a menu can be pushed onto the game in progress and when it is popped the game can continue as if nothing happened.

\$\endgroup\$
0
0
\$\begingroup\$

I am going to give a non-Ruby answer, because I don't know Ruby.

I would have lots of classes for this:

MenuOneController
MenuTwoController
MenuOneView
MenuTwoView
InputNames

The controllers navigate the menu. Pressing up and down changes the selected item, pressing enter selects the item. Selecting the item would call code elsewhere to perform the actual action. All the while, anything the controller does is update the views. (Depending on your menu system, you can have a single controller.)

The views draw the actually menu, showing the currently selected option, providing feedback when an option is changed, etc.

Input names is a file of strings such as:

string up = "Up"

string select = "Enter"

This way, if you decide you want spacebar to select instead of enter, you change the string in one place, instead of all of your controllers. So instead of on(controller: "buttonA"), you would use on(controller: InputNames.select).

So if you start in menu one, MenuOneView would draw what you see. The MenuOneController would be active. Then if you select "Menu Two" from menu one, it would start drawing MenuTwoView instead and listen to MenuOneController instead.

Here is some pseudo-code:

Class InputNames
{
    string up = "Up";
    string down = "Down";
    string select = "A Button";
}

Class MenuOneController
{
    int selectedMenuOption;

    void GameLoop()
    {
        if(ButtonPressed(InputNames.up)
        {
            selectedMenuOption++;
        }
        if(ButtonPressed(InputNames.down)
        {
            selectedMenuOption--;
        }
        if(ButtonPressed(InputNames.select))
        {
            if(selectedMenuOption == 0)
            {
                MenuOneView.ShowButtonBeingPressed();
                MyCodeElsewhere.DoStuffFromMenuOne();
            }
            if(selectedMenuOption == 1)
            {
                MenuOneView.ShowButtonBeingPressed();
                GameLoop.Remove(this);
                GameLoop.Add(MenuTwoController);
            }
        }
        MenuOneView.DrawUI(selectedMenuOption);
    }
}

Class MenuOneView
{
    void DrawUI(int selectedMenuOption)
    {
        //draw the UI for the currently selected menu option
    }

    void ShowButtonBeingPressed(selectedMenuOption)
    {
        //draw the UI such that the button is pressed
    }
}

Class MenuTwoController
{
    int selectedMenuOption;

    void GameLoop()
    {
        if(ButtonPressed(InputNames.up)
        {
            selectedMenuOption++;
        }
        if(ButtonPressed(InputNames.down)
        {
            selectedMenuOption--;
        }
        if(ButtonPressed(InputNames.select))
        {
            if(selectedMenuOption == 0)
            {
                MenuTwoView.ShowButtonBeingPressed();
                MyCodeElsewhere.DoStuffFromMenuTwo();
            }
            if(selectedMenuOption == 1)
            {
                MenuTwoView.ShowButtonBeingPressed();
                GameLoop.Remove(this);
                GameLoop.Add(MenuOneController);
            }
        }
        MenuTwoView.DrawUI(selectedMenuOption);
    }
}

Class MenuTwoView
{
    void DrawUI(int selectedMenuOption)
    {
        //draw the UI for the currently selected menu option
    }

    void ShowButtonBeingPressed(selectedMenuOption)
    {
        //draw the UI such that the button is pressed
    }
}
\$\endgroup\$
9
  • \$\begingroup\$ "Selecting the item would call code elsewhere to perform the actual action." Can you elaborate a little more on how you see this working? Pseudo code would also help. \$\endgroup\$
    – Andrew
    Jul 2, 2016 at 20:14
  • \$\begingroup\$ Are you saying that MenuOne would directly instantiate MenuTwo? Or do you imagine some event system where control is indirectly passed to MenuTwo? If you could elaborate on this too, that would be helpful. \$\endgroup\$
    – Andrew
    Jul 2, 2016 at 20:16
  • \$\begingroup\$ @Andrew That depends. If all controls are the same across menus, you can just use a single controller. If they are different, you could have menu controllers call on other menu controllers to tell them to take over. Or if you want another layer of abstraction, you could have a MenuControllerController. Personally, I think having a controller for controllers is overkill, but I can see an argument for it. \$\endgroup\$
    – Evorlor
    Jul 2, 2016 at 21:29
  • \$\begingroup\$ @Andrew "Selecting the item would call code elsewhere to perform the actual action." Basically, the menu is for selecting what you want to do. Not for doing it. It should be as dumb as possible. So selecting the "Turn off volume" button should not turn off the volume. It should tell your volume manager to turn off the volume. \$\endgroup\$
    – Evorlor
    Jul 2, 2016 at 22:10
  • \$\begingroup\$ Okay, it is becoming a little more clear to me, based on your pseudo code example. Correct me if I'm wrong, but it looks like your approach is to have a global (singleton) GameLoop object that can be accessed from anywhere and manages/coordinates the individual menu controllers. The GameLoop object passes input handling to the active menu controller. The individual controllers know which controllers handle the action selected in the previous menu. Did I understand this correctly? \$\endgroup\$
    – Andrew
    Jul 3, 2016 at 19:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .