Dungeon gaming stands as one of the most ancient categories in the realm of gaming, guiding players through various levels set in dungeon-like environments. Players engage in battles with foes, collect items, solve puzzles, and ultimately aim to defeat a final boss or successfully escape the dungeon. This genre seamlessly combines elements of exploration and strategic gameplay, complemented by the need for effective resource management.
In this tutorial, we are going to explore the development of a fundamental dungeon game using C++. The tutorial will cover various aspects such as setting up the game environment, controlling the player's movements, incorporating adversaries, and integrating game elements like health points and inventory management. Upon completion, you will have a basic text-driven dungeon game created in C++.
Overview of the Game
In this dungeon adventure, a procedurally generated maze awaits players as their initial location. The main goal is to navigate through the maze, steer clear of traps, engage in battles with foes, and ultimately locate the hidden treasure for victory. The programming implementation will be in C++ following object-oriented programming (OOP) concepts.
Main features of this dungeon game:
- Random dungeon generation
- Player movement (up, down, left, right)
- Enemies that move and attack
- Player health points and enemies' health points
- Finding treasure for the win
Dungeon Setup
A dungeon represents a grid containing different elements such as walls, empty spaces, enemies, and the player. To visualize this grid, we utilize a 2D array . In C++, a 2D array can be initialized as:
const int width = 10;
const int height = 10;
char dungeon[height][width];
Each element of this array will represent a part of the dungeon. For example:
- 'P' for the player
- 'E' for enemies
- 'T' for the treasure
- '#' for walls
- '.' for empty space
We are going to generate a randomly structured dungeon featuring walls and open areas. Both players and treasures will be positioned at random locations across the map.
Definition of the Player Class
We create the Player class to handle all aspects related to players. Typically, the player possesses characteristics such as health status, current location, and items in possession.
#include <iostream>
#include <cstdlib>
#include <ctime>
const int height = 10; // Define dungeon height
const int width = 10;
class Player {
public:
int x, y; // Player's position in the dungeon
int health; // Player's health points
Player(int startX, int startY) {
x = startX;
y = startY;
health = 100; // Starting health
}
void move(char direction) {
switch(direction) {
case 'w': // Move up
y--;
break;
case 's': // Move down
y++;
break;
case 'a': // Move left
x--;
break;
case 'd': // Move right
x++;
break;
}
}
void takeDamage(int damage) {
health -= damage;
}
bool isAlive() {
return health > 0;
}
};
It represents a class enabling the user to navigate a virtual environment using the WASD keys, sustain damage from adversaries or hazards, and assess the player's survival status by examining their health points.
Define a class Enemy:
Now, we are going to establish an Enemy class that will embody adversaries found within the dungeon. Enemies will possess similar characteristics to the player, albeit with distinct actions. These foes will wander randomly within the dungeon and engage in combat with the player if they come within close proximity.
class Enemy {
public:
int x, y; // Enemy's position
int health; // Enemy's health
Enemy(int startX, int startY) {
x = startX;
y = startY;
health = 50; // Enemies have less health than the player
}
void moveRandom() {
int direction = rand() % 4;
switch(direction) {
case 0: x--; break; // Move left
case 1: x++; break; // Move right
case 2: y--; break; // Move up
case 3: y++; break; // Move down
}
}
void takeDamage(int damage) {
health -= damage;
}
bool isAlive() {
return health > 0;
}
};
The adversaries will roam in a haphazard manner. Upon colliding with a player, they will initiate an attack. This particular behavior of the adversary can be further developed to enhance their intelligence. Nevertheless, in this instance, we maintain simplicity by allowing them to wander aimlessly.
Generating the dungeon
Now, we will design a basic dungeon layout, featuring walls, open spaces, adversaries, and valuable rewards. Initially, we will position the player at a specific location. As the player and foes navigate through the dungeon, we will modify the layout accordingly.
void generateDungeon(char dungeon[height][width], Player &player, Enemy &enemy) {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
dungeon[i][j] = (rand() % 10 < 2) ? '#' : '.'; // 20% chance of a wall
}
}
dungeon[player.y][player.x] = 'P';
dungeon[enemy.y][enemy.x] = 'E';
int treasureX = rand() % width;
int treasureY = rand() % height;
dungeon[treasureY][treasureX] = 'T';
}
This function creates a randomized dungeon layout, positions the player and enemy at designated locations, and then randomly assigns a location for the treasure.
Game Loop
The game loop controls the movement of the player, manages enemy actions, and refreshes the game state during each iteration. It handles player inputs and adjusts the game state accordingly.
void gameLoop(char dungeon[height][width], Player &player, Enemy &enemy) {
bool gameRunning = true;
while (gameRunning) {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
std::cout << dungeon[i][j] << " ";
}
std::cout << std::endl;
}
char move;
std::cin >> move;
dungeon[player.y][player.x] = '.';
player.move(move);
dungeon[player.y][player.x] = 'P';
if (dungeon[player.y][player.x] == 'T') {
std::cout << "You found the treasure! You win!" << std::endl;
gameRunning = false;
break;
}
dungeon[enemy.y][enemy.x] = '.';
enemy.moveRandom();
dungeon[enemy.y][enemy.x] = 'E';
if (player.x == enemy.x && player.y == enemy.y) {
player.takeDamage(10); // Player takes damage
enemy.takeDamage(10); // Enemy takes damage
std::cout << "You fought an enemy!" << std::endl;
if (!player.isAlive()) {
std::cout << "You died. Game over!" << std::endl;
gameRunning = false;
}
if (!enemy.isAlive()) {
std::cout << "You defeated the enemy!" << std::endl;
dungeon[enemy.y][enemy.x] = '.';
}
}
}
}
int main() {
srand(time(0)); // Seed for random number generation
char dungeon[height][width];
Player player(1, 1); // Starting position of the player
Enemy enemy(2, 2); // Starting position of the enemy
generateDungeon(dungeon, player, enemy);
gameLoop(dungeon, player, enemy);
return 0;
}
Output: