Solarian Programmer

My programming ramblings

Roguelike game in C++ - Bootstrap

Posted on July 12, 2012 by Paul

The code for this post is on GitHub: https://github.com/sol-prog/roguelike.

I suppose you’ve skimmed through the Introduction to this series of blog posts about implementing a toy roguelike game in C++. That being said, let’s get started!

If you want to follow along with the development, you will need a C++ compiler and the ncurses library. I’m going to use the GNU C++ compiler (specifically g++-4.7.1) for testing and compiling my code. In principle any C++ compiler available on your system should work, just be sure that you can work with the last version of ncurses. For Mac users, installing Xcode and the Command line tools will also install ncurses. On Linux you could use your package manager for installing g++ and ncurses, the procedure is different from distribution to distribution, if you use a Debian derived distro, like Ubuntu, all you have to do is to write in a Terminal:

1 sudo apt-get install g++
2 sudo apt-get install libncurses5-dev

For Windows users, the situation is a bit more complicated. First, you will need to install the last stable version of the GNU C++ compiler from www.equation.com. After that you will need to find a binary version of ncurses. For your convenience I’ve included a 64 bits version of ncurses on the Gihub repository of this blog series. You will need to copy the content of the include folder in the include path of your compiler, same goes for the lib folder. If you already know what Cygwin or Mingw is, you probably don’t need my help to install g++ and ncurses.

Let’s check if we can compile a simple code with ncurses:

 1 #include <ncurses.h>
 2 
 3 int main() {
 4 	// Initialize ncurses
 5 	initscr();
 6 	clear();
 7 
 8 	// Print a string on screen
 9 	printw("Seems that you can use ncurses ...\nPress any key to exit!");
10 
11 	// Wait until the user press a key
12 	getch();
13 
14 	// Clear ncurses data structures
15 	endwin();
16 }

You can compile the above code with:

1 g++ test.cpp -lncurses -o test

If you have no error, congratulations, you can follow along with this post.

@ is usually the symbol used for representing the main character in a roguelike game. We could start our game with an welcome message and present the user with the option to start or quit the game. Once the game starts we will print the main character on the screen, the user will be able to quit at any point by pressing q or Q:

 1 ...
 2 	// Initialize ncurses
 3 	initscr();
 4 	clear();
 5 	noecho();
 6 	cbreak();
 7 	keypad(stdscr, TRUE);
 8 	curs_set(0);
 9 
10 	// Define the main character initial position and symbol:
11 	int row = 10, col = 10;
12 	char main_char = '@';
13 
14 	// Print a welcome message on screen
15 	printw("Welcome to the RR game.\nPress any key to start.\nIf you want to quit press \"q\" or \"Q\"");
16 
17 	// Wait until the user press a key
18 	int ch = getch();
19 
20 	// Clear the screen
21 	clear();
22 
23 	// Start the game loop
24 	while(1) {
25 		if(ch == 'q' || ch == 'Q') {
26 			break;
27 		}
28 		// If the user choses to stay, show the main character at position (row,col)
29 		else {
30 			mvaddch(row, col, main_char);
31 			ch = getch();
32 		}
33 	}
34 ...

A game with no movement is pretty lame if you ask me, so in the next step we are going to let the user to move the character on screen using the arrow keys. Initially I was thinking to use for movement the H,J,K,L keys à la Vim, but I think the arrow keys are more intuitive. If I will ever change my mind I could add this as an option in the game settings.

The screen is an array of rows and columns for ncurses, so if we want to move the @ we will need to change the (row, col) position. For e.g. if you want to move left you will subtract 1 from col. If you want to go right you will add 1 to col and so on … An important observation here, when we move @ from one position to another we will need to clean the old location. We could use an empty C++ character for this if we want to have a clean screen or another symbol if we want to see a trace for the movements of @. For now, I’m going to erase the old position of @ with the # symbol.

We can clean up a bit the main function by moving the ncurses initialization in a separate function, same goes for the game loop:

 1 int main() {
 2 	// Define main character symbol and initial position
 3 	int row = 10, col = 10;
 4 	char main_char = '@';
 5 
 6 	// Start ncurses
 7 	init();
 8 
 9 	// Print a welcome message on screen
10 	printw("Welcome to the RR game.\nPress any key to start.\nIf you want to quit press \"q\" or \"Q\"");
11 
12 	// Wait until the user press a key
13 	int ch = getch();
14 
15 	// Clear the screen
16 	clear();
17 
18 	// Start the game loop
19 	game_loop(main_char, row, col, ch);
20 
21 	// Clear ncurses data structures
22 	endwin();
23 
24 	return 0;
25 }

Currently the the game loop function contains code for moving @ on the screen:

 1 void game_loop(char main_char, int row, int col, int ch) {
 2 	// Check if the user wishes to play the game
 3 	if(ch == 'q' || ch =='Q') return;
 4 
 5 	// Show the main character on the screen
 6 	mvaddch(row, col, main_char);
 7 	refresh();
 8 
 9 	for(;;) {
10 		ch = getch();
11 
12 		if(ch == KEY_LEFT) {
13 			erase(row, col);
14 			col = col - 1;
15 			mvaddch(row, col, main_char);
16 			refresh();
17 		}
18 		else if(ch == KEY_RIGHT) {
19 			erase(row, col);
20 			col = col + 1;
21 			mvaddch(row, col, main_char);
22 			refresh();
23 		}
24 		else if(ch == KEY_UP) {
25 			erase(row, col);
26 			row = row - 1;
27 			mvaddch(row, col, main_char);
28 			refresh();
29 		}
30 		else if(ch == KEY_DOWN) {
31 			erase(row, col);
32 			row = row + 1;
33 			mvaddch(row, col, main_char);
34 			refresh();
35 		}
36 		else if(ch == 'q' || ch == 'Q') {
37 			break;
38 		}
39 	}
40 }

The erase function will simply put a symbol at position (row, col):

1 void erase (int y, int x) {
2 	mvaddch(y, x, '#');
3 }

Let’s play a bit with this game.

Initial screen:

Initial game screen

Game starts and the main character, @, is shown on the screen:

Show the main character

Game state after some movements:

Main character traces on the screen

Not bad for a few lines of code. Next time we are going to create a map for the game and put the @ on this map. We’ll also add functionality for seeing different parts of the map, since in general the map is larger than the screen.

If you have any improvements in mind feel free to drop me a comment.

All posts from this series:

If you are interested in learning more about the new C++11 syntax I would recommend reading The C++ Programming Language by Bjarne Stroustrup.

or, Professional C++ by M. Gregoire, N. A. Solter, S. J. Kleper:


Show Comments