Creating a scrolling menu V.2 [1D/2D]

Discussion in 'Tutorials' started by PlayerOne, Nov 17, 2018.

  1. PlayerOne

    PlayerOne Member

    Joined:
    Mar 14, 2018
    Posts:
    284
    GM Version: GMS2 (unconfirmed if it works with GMS1)
    Version: 2.1
    Links: None
    Downloads [GMS2]: https://drive.google.com/open?id=1EBv7k8nBeuvNkbr7RecvaxizQ4ikv0Ru

    Change Log:
    v2.0: Release
    v2.1: Added image, Fixed some text

    NOTICE:
    Due to a bug I had to rewrite this tutorial. If you find any additional bugs please quote me in the thread so as to alert me to these problems.

    MenuExample.png



    This tutorial will teach you to make menus using keyboard inputs using 1D Arrays and 2D Arrays In addition, this tutorial will also show you how to make a “scrolling effect” when a cursor / highlighter reaches the bottom. Something I rarely find in any tutorial videos on YouTube or looking through google.


    Code:

    Requirements:
    Code:
    1. Knowing GML.
    2. Having a sprite of 1pixel by 1 pixel in size loaded into your project.
    
    Now to understand what is written here I will explain everything at the top and have the code posted at the bottom. Since most of it is the same say for a few lines of code and the different arrays used. There is an example project in the link above if you want to see it in action.

    Explanation breakdown:
    1) Each of the methods uses an array to store strings. Then use array_length_1d() or array_length_2d() to find the maximum amount of entries that will be used. This number is then stored in _max and is used to keep the cursor from going out of bounds when scrolling through the menu. Using the min function keeps the cursor from going out of bounds when the player switches to a new menu within the system.



    2) The scrolling in these examples work in conjunction with the following object variables: amount, _max, and page_down. In the draw gui event the for loop only draws what is displayed based on what is dictated in the amount variable and when page_down gets added the for loop cycles through the array until the marker reaches the maximum amount in _max. Page_down is crucial if you want to scroll through your menus as it is tied to the for loop.

    If you wish to not scroll through your menus you can just axe the page_down variable from the events and just have the cursor for your menu be capped by the len variable.

    3) The object variables amount and display has its own use in how many options you wish to display on screen and how far the cursor can go when maneuvering. It can be 3, 5, or more depending on what you dictated in the array. The amount you want displayed on screen is up to you. Be warned that if you do not use the scroll effect and you have more options in the array than your screen is able to display the text will be cut off at the bottom. It will function normally but you won’t see it.

    4) The object variable cursor_id uses 2 addition variables to get the correct total: cursor and cursor_pos. In the step event when vk_down or vk_up is pressed it checks to see if the cursor variable is larger than the amount variable and will then switch to cursor_pos to continue adding to cursor_id. Doing this will get the correct total when used for the switch statement.

    In addition, when it comes to multiple menus you will have to nest switch statements to ensure what menu effect goes with what string.

    If you need to switch menus say from the start menu to the options you can use a 2d array and have 1 object variable - current_menu in this example - to dictate what menu type is displayed. Be it the options menu, extras, or cheats menu this one variable will dictate everything.

    That said when programming your menu function in the switch statement make sure you set reset to true so that the cursor is set back to zero.


    Code:
    //Controls
    
    enter=keyboard_check_pressed(vk_enter);
    back=keyboard_check_pressed(vk_backspace);
    up = keyboard_check_pressed(vk_up);
    down = keyboard_check_pressed(vk_down);
    
    cursor_id=cursor+cursor_pos; // <<< Calculates cursor position
    
    //These two var's are used to assist in preventing the menu system from crashing.
    //If one menu catagory is larger or smaller than the nest, the "min" function
    //will assist in prevent the for loop in the draw gui event form going out of bounds.
    
    var len = array_length_2d(_menu,current_menu) // <<< Get the 2D array length
    amount=min(len,display);
    _max=min(len,len);
    
    
    
            if reset==true // <<< When changing a menu this will reset the cursor.
            {          
            page_down=0;
            cursor=0;
            cursor_pos=0;
            reset=false;
            }
    
    
            //This will move the cursor up or down.
            if (up && cursor_id!=0)
            {   
                   if cursor!=0{cursor--;}    
                   else{page_down--; cursor_pos--};
            }
    
        
            if (down && cursor_id!=(_max-1))
            {    
                   if cursor<(amount-1){cursor++;}    
                   else{page_down++; cursor_pos++};
            }
    
    
    if enter!=0
    {
    
    
            switch(current_menu)
            {
    
                
    
            case 0: // Root Menu
    
                   switch(cursor_id)
                   {                  
                           case 0: current_menu=1; reset=true; break;
                           case 1: current_menu=2; reset=true; break;
                           case 2: current_menu=3; reset=true; break;
                           case 3: current_menu=4; reset=true; break;
                           case 4: current_menu=5; reset=true; break;
                           case 5: game_end(); break;  
                   }
    
            break;
    
        
    
            }
    
    
    }
    

    When you need to switch menus - by pressing vk_enter in this example - the cursor, cursor_pos, and page_down variables is reset to zero when the object variable reset is set to true. This is essential when going to the next/previous menu in the array.

    Code:
          if reset==true // <<< When changing a menu this will reset the cursor.
          {        
          page_down=0;
          cursor=0;
          cursor_pos=0;
          reset=false;
          }
    
    5) Finally the gui event itself. I said the 1 x 1 sprite is required and now I will tell you why. This sprite is our cursor. A single white pixel stretched under the text indicating where it is highlighted. You have to stretch it by the hundreds to achieve proper results using draw_sprite_ext().

    The object variable cursor moves the cursor sprite up and down on screen. The reality is that once the cursor equals to the amount variable it will stop moving. The scrolling effect used is just an illusion because once you hit the maximum amount stored in the amount variable the for loop in the gui event will cycle through the strings stored in the array and will continue as such until the cursor_id equals _max.

    The var pos_x and var pos_y dictates the entire menu and where it is positioned within the games window. From the cursor to the text on the screen. var _space dictates the space between text options. The smaller it is the more cramped it will look but the more space you have, and you will end up having a menu the will be cut off by your games window.


    Example of a menu using a 2D array:

    Code:
    
    //CREATE:
    
    _menu[0,0]="Start Game";
    _menu[0,1]="Load Game";
    _menu[0,2]="Tutorial";
    _menu[0,3]="Extras";
    _menu[0,4]="Options";
    _menu[0,5]="Exit to Desktop";
    
    _menu[1,0]="Very Easy"
    _menu[1,1]="Easy"
    _menu[1,2]="Medium 1"
    _menu[1,3]="Medium 2"
    _menu[1,4]="Hard"
    _menu[1,5]="Hard Core"
    _menu[1,6]="Expert"
    _menu[1,6]="Back"
    
    _menu[2,0]="No save file found!"
    _menu[2,1]="Back"
    
    _menu[3,0]="Error. Unable to load tutorial."
    _menu[3,1]="Back"
    
    
    _menu[4,0]="Concept Art"
    _menu[4,1]="Audio Logs"
    _menu[4,2]="Cheats"
    _menu[4,3]="Model Viewer"
    _menu[4,4]="Intel"
    _menu[4,5]="Movie Trailer"
    _menu[4,6]="Back"
    
    _menu[5,0]="Level 0 "
    _menu[5,1]="Level 1 "
    _menu[5,2]="Level 2 "
    _menu[5,3]="Level 3 "
    _menu[5,4]="Level 4 "
    _menu[5,5]="Level 5 "
    _menu[5,6]="Level 6 "
    _menu[5,7]="Level 7 "
    _menu[5,8]="Level 8 "
    _menu[5,9]="Level 9 "
    _menu[5,10]="Level 10 "
    _menu[5,11]="Level 11 "
    _menu[5,12]="Level 12 "
    _menu[5,13]="Level 13 "
    _menu[5,14]="Level 14 "
    _menu[5,15]="Level 15 "
    _menu[5,16]="Level 16 "
    _menu[5,17]="Level 17 "
    _menu[5,17]="Back "
    
    _menu[6,0]="No save game found."
    _menu[6,1]="Back"
    
    
    //Object VAR's
    current_menu=0;
    display=7
    reset=false;
    
    cursor=0;
    cursor_pos=0;
    cursor_id=0;
    page_down=0;
    
    
    Code:
    
    //STEP:
    
    //Controls
    enter=keyboard_check_pressed(vk_enter);
    back=keyboard_check_pressed(vk_backspace);
    up = keyboard_check_pressed(vk_up);
    down = keyboard_check_pressed(vk_down);
    
    cursor_id=cursor+cursor_pos
    
    
    var len = array_length_2d(_menu,current_menu);
    amount=min(len,display);
    _max=min(len,len);
    
                    if reset==true
                    {                          
                    page_down=0;
                    cursor=0;
                    cursor_pos=0;
                    reset=false;
                    }
    
    
                    if (up && cursor_id!=0)
                    {          
                         if cursor!=0{cursor--;}
                         else{page_down--; cursor_pos--};
                    }
    
                
    
     
    
                    if (down && cursor_id!=(_max-1))
                    {          
                           if cursor<(amount-1){cursor++;}
                           else{page_down++; cursor_pos++};
                    }
    
                
    
     
    // <<< Nesting switch statements will allow you to add functions to your menus.
    // Just make sure you add the current_menu number then the switch statement with the cursor_id           
    
    if enter!=0
    {
                      // to execute the proper effect.
    
    
                    switch(current_menu)
                    {                 
    
                    case 0: // Root Menu
                                    switch(cursor_id)
                                    {                                          
                                                    case 0: current_menu=1; reset=true; break;
                                                    case 1: current_menu=2; reset=true; break;
                                                    case 2: current_menu=3; reset=true; break;
                                                    case 3: current_menu=4; reset=true; break;
                                                    case 4: current_menu=5; reset=true; break;
                                                    case 5: game_end(); break;      
    
                                    }
    
                    break;
    
                                
                    case 1:
    
                                    switch(cursor_id)
    
                                    {                                          
                                                    case 6: current_menu=0; reset=true; break;    
                                    }
    
                    break;
    
     
                    case 2:
    
                                    switch(cursor_id)
    
                                    {                                          
    
                                    case 1: current_menu=0; reset=true; break;    
    
                                    }          
    
                    break;
    
                
    
                    case 3:
    
                                    switch(cursor_id)
    
                                    {                                          
    
                                     case 1: current_menu=0; reset=true; break;    
    
                                    }
    
                    break;
    
                
    
                
    
                    case 4:
    
                                    switch(cursor_id)
    
                                    {                                          
    
                                    case 6: current_menu=0; reset=true; break;
    
                                    }
    
                    break;
    
                
    
                
    
                    case 5:
    
                                    switch(cursor_id)
    
                                    {                                          
    
                                   case 10: current_menu=0; reset=true; break;
    
                                    }
    
                    break;
    
                
    
                    case 6:
    
                                    switch(cursor_id)
    
                                    {                                          
    
                                    case 1: current_menu=0; reset=true; break;
    
                                    }
    
                    break;
    
                
    
                    }
    
    
    
    }
    
    
    Code:
    
    //Draw Gui
    
    //Draw Menu
    var i=0;
    var ii=0
    var pos_x = (128+256); // <<< Position x on screen for everything seen here
    var pos_y = (140+32); // <<< Position y on screen for everything seen here
    var _space = 32;
    draw_sprite_ext(spr_pixel,0,pos_x-15,pos_y+(cursor*_space-3),256+64,32,0,c_ltgray,1)
    
    
    
    
    
    
    
    
    
                    draw_set_color(c_white)
    
    
    
                    for(i=page_down; i<(amount+page_down); i++)
                    {
    
    
    
                     if ((cursor_id!=_max) || i>_max) && (reset==false)
                     {
                     draw_text(pos_x, pos_y + ((ii)*_space),string(_menu[current_menu,i]));
                     ii++;
                     }
    
                     }
    
    
     
    Last edited: Nov 30, 2018

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice