My first roguelike development blog - [game programming blog ~ part 3]

in #blog7 years ago (edited)

My roguelike development blog ~ part 3


In my previous two tutorials I have showed an example on how to write a main runtime loop and your game loop. Now it's time to incorporate a customizable menu.


The menu function

We need to declare a function to hold our menu logic, so we add another function into our header definition. Add a newline under the function int Program(), and write:

int Menu();


We also add the rought mockup in our implementation. Use the same process as when we created int Program() in tutorial number 2, and add the following code to GameManager.cpp:

int GameManager::Menu () {
  
} // END Menu


After that we need six controller variables; one to handle if the program is done looping and another one to store which menu option was selected to handle changing between menu screen. We also define a char (single character letter) to represent our cursor pointer. Finally we define the menus position on the x-axis and y-axis. Lets call them quit, select, choice, cursor, xmin and ymin.

To check if the loop is finished we need a boolean data type (either true or false), and to store which option was selected and which choice our menu-function returns we need two integers (numbers without decimals).

Maneuver back to your GameManager.h-file and under the access specifier private; add a variables called bool quit, one called int select and one called int choice.

Then we store the cursor in a variable called char cursor and set the cursors default starting position with int xmin and int ymin.

Your code should look like:

private:
  bool quit;
  int select;
  int choice;
  char cursor;
  int xmin;
  int ymin;


Implementing the menu

It's time to use these variables, so go back to the implementation (GameManager.cpp) and lets begin writing the menu loop.

C++ has a loop called a do-while loop. The benefit of this loop type is that it runs (atleast) once before exiting. A do-while loop is declared like this:

do {
  
} while (quit != true);


You could read this as do the code logic inside the loop body (the content inside the {} brackets) while end condition(s) isn't met.

To flesh this function out we use already existing libraries. We need to include these libraries into our code by adding them into our header-file.

Include <conio.h>, <vector> and <Windows.h> leaving your GameManager.h looking like:

#ifndef GAMEMANAGER_H
#define GAMEMANAGER_H
#include <iostream>
#include <conio.h>
#include <vector>
#include <Windows.h>
using namespace std;
class GameManager {   public:     GameManager();     int Program();     int Menu();   private:     bool quit;     int select;     int choice;     char cursor;     int xmin;     int ymin; }; // END GameManager
#endif


Lets keep coding to tie this all together. We begin by declaring a vector of strings (text). First we declare a storage for the menu options like this:

int GameManager::Menu () {
  vector<string> menuopt;
} // END Menu


Then we use a function called push_back(), found in the vector library, to add the options into the menuopt storage. To narrow the scope of this tutorial we only add the most nessecary options:

menuopt.push_back("New game");
menuopt.push_back("Quit");


Calling our Menu()-function should print the options on the screen, which is done with a for-loop:

for (int menuitem = 0; menuitem < menuopt.size(); menuitem++) {
  printf("  %s\n", menuopt[menuitem].c_str());
}


Read this out as for every menuitem stored in our vector of strings, as long as menuitem is less than the number of menuopts stored in the vector, print out a formatted textline and a newline, then increment the menuopt to the next position.

Note that we add two spaces to the beginning of every menu option to leave room for the cursor. Also note that you can get the size of the vector (number of elements) by calling the helper function size() located in the vector header, and that you need to use another helper function called c_str() to read an entire string to assign to the %s inside the printf-statement.

Our Menu() will now print out the options to screen, but how do we select an option?

Again we need a loop, and again a do-while is suitable.

We write a do-while loop and begin by adding our cursor to the screen.

do {
  printf("%c", cursor);
} while (!select && choice == 0);


The ! before the variable select is a relational operator and means not equal to. The double equal sign after the variable choice is an comparison operator and checks if the left value equals the right value. A single equal sign is called an assignment operator, and assigns a value to the variable.

If you compile this code you will notice that the cursor is positioned below the mockup text and the menu (and that the program loops uncontrollably). We need to tell our program to place the cursor at the correct spot on the screen.

The Windows.h library contains a predefined function to do just that, so lets utilize it.

The function is called SetConsoleCursorPostition() and it needs a handle to the console window and a X and a Y coordinate to know where to place terminal cursor. Go to GameManager.h and add the following under private:

COORD point;


You need to initialize your custom variables select and choice, define the cursor position and initialize the cursor.

Navigate back to GameManager.cpp and add the these lines under your vector of menu options:

select = false;
choice = 0;
xmin = 0;
ymin = 0;
point = {xmin, ymin};
cursor = '>';


Then add the code to position the cursor inside the do-while()-loop, just before printing the cursor:

SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), point);


Your updated for-loop should look like this:

do {
  SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), point);
  printf("%c", cursor);
} while (!select && choice == 0);


Now it's time to add the code to select which option we chose. This is done by using a switch-statement. Place the switch after positioning the cursor like this:

switch(select) {
  case 1: {
    choice = 1;
    break;
  }
  case 2: {
    choice = 2;
    break;
  }
  default: {
  }
}


Finally we need to be able to move the cursor to make our selection. Add a newline after the switch-statement and the following line:

char ch = _getch();


You need this codeline to flush the input buffer for excess keyboard input, to avoid having a value stored in the buffer when reading the next input (especially when using the arrow key, alt and ctrl).

But this doesn't let you chose a menu option. This is because we haven't coded a way to catch keyboard input. To do so we use Windows.h helper function GetAsyncKeyState().

Implementing the keyboard logic

We need to check if a certain key was pressed, and handle the logic assosiated with it. Lets begin by adding the logic for when the user presses up.

Add the following code after the input buffer flushing:

if (GetAsyncKeyState(VK_UP) != 0 && 0x8000) {
}


Here we check if the up-arrow is pressed (not equal to 0) and if a key is pressed (0x8000). The last check is just a security measurement to ensure a key actually is pressed.

We should check if the cursor is set inside our borders (first and last menu element) before handling any repositioning, so we add the following code inside the if-statement:

if(point.Y > ymin) {
  SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), point);
  printf(" ");
  point.Y = point.Y - 1;
  }
  ch = _getch();


We check if the cursor is within border margin, and if it is, print a space where the cursor used to be then resposition the cursor to be drawn at the new location next time the loop reaches printing the cursor to screen. We also clear the input buffer again.

Now to implement a way to handle down arrow being pressed. This time we write:

else if (GetAsyncKeyState(VK_DOWN) != 0 && 0x8000) {
  if (point.Y < menuopt.size() + (ymin - 1)) {
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), point);
    printf(" ");
    point.Y = point.Y + 1;
  }
  ch = _getch();
}


Again we check if the current key pressed is arrow down, that a key is acctually pressed and that the cursor stays within our borders. If it is we print out a blank space to clear the previous drawn position and reposition the cursor the be redrawn next loop iteration.

Implementing the possibility to select an option using the enter key could look like this:

else if (GetAsyncKeyState(VK_RETURN) != 0 && 0x8000) {
  if (point.Y == ymin) { choice = 1; };
  if (point.Y == ymin + 1) { choice = 2; };
}


Implementing a way to exit the program by pressing escape can be written:

if (GetAsyncKeyState(VK_ESCAPE) != 0 && 0x8000) {
  exit(0);
}


One last thing, to avoid the menu looping at full speed all the time we add a Sleep()-interval between iterations after the last if-statement.

Sleep(16);


We choose 16ms sleep interval because (one second) 1000 / 60 (60 frames per second) is 16.67ms per iteration.

Finally we return the choice we made after the ending bracket from the do-while()-statement:

return choice;


The finishing touch

It's time to test our Menu()-function. Delete the existing output statement inside your function Program() and in your main-function:

cout << "This output is from our new function Program()";
printf("Game programming tutorial");


Instead add the function call to your new Menu()-function in Program(). We implement this with the following code:

select = Menu();


When we have made our selection in the menu, the Menu()-function returns our selection (a number). This returned number is used to call other functions based on our selection. We add this into our code with this (Warning: rough mockup code ahead!):

switch (select) {
  case 1: {
    printf("New game was selected!");
    break;
  }
  case 2: {
    printf("Quit was selected!");
    break;
  }
}


Now recompile your program and test the output by making a selection in our new menu.

There you have it! Now you know how to create a simple menu function and switching between menu states using your keyboard. Next time I'll demonstrate how to separate your cursor positioning into an external function and how to add color to your program.


To see more - upvote this post and follow me @jonrhythmic

Sort:  

Did you mean to skip over naming the things you were including? you have 4 lines of #include with nothing named after them. from later context one is likely for Vector and one for Windows.

Quite a good write up and easy to follow what you've done.

I'm sure I meant to name the includes, but think it might be HTML markup error. I can't find any includes without a library after, so could you help me out by copying the lines so I can search for it?

There is an include section where iostream, conio.h, vector and Windows.h are included, but from my computer it's readable.

Oddly it's readable on mine also now. No idea what caused them to not show up before, but maybe some hiccup in the markup

It's probably been a delay in the update, as I published this post without HTML markup for < and >. Thanks for pointing this out, and for taking the time to proof read my stuff.

Welcome to Steemit! Looking forward to learning more about your very interesting life!

@jonrhythmic

@thaokhanh Thank you! You are more than welcome to follow me.

I'll add you to my following as well ;)

Wow! That was one of the most detailed breakdowns I have seen on writing up code. Seriously sick content! Thanks for posting @jonrhythmic!

Do you plan on teaching coding for other niches?

@ironmanmatt Thank you for reading this and commenting! I currently have no plans for any other tutorial series, but maybe sometime in the future.

Where did you learn how to program? I want to start programing too, but IDK how to :/

I started programming when I was around 16, but gave it for a few years shortly after. I then took a bachelor in IT and relearned programming. Now I'm programming on and off when I feel like it.

I recommend buying a programming book on the language you prefer, and follow it from start to finish. Learning by doing ;)

I'm 15 currently, but I don't want a future in the programming world. I just want to use it for recreational purposes. Did you start off on your 16th with a programming book?

Thanks anyways :)

Perfect age to begin. I started learning from a book (since modem internet would block the housephone and mobile phones didn't have 4G) and any open source project I could find, which mostly was unencrypted files found in programs I had installed on my first Pentium 133MHz, 32Mb RAM computer.

Coin Marketplace

STEEM 0.20
TRX 0.15
JST 0.030
BTC 65269.02
ETH 2653.11
USDT 1.00
SBD 2.84