- Crafting a C# Text-Based Dungeon Adventure
- Block 1: Namespace and Class Declaration
- Block 2: Game Class Variables and Enums
- Block 3: ReadUserInput Method
- Block 4: GetStepCounter Method
- Block 5: ProcessUserInput Method
- Block 6: MovementAction Method
- Block 7: Update Method
- Block 8: ValidPlayerMove Method
- Block 9: PrintMapToConsole Method
- Block 10: PrintExtraInfo Method
- Block 11: LoadMapFromFile Method
- Block 12: CloneCharArray2D Method
- Block 13: GetOriginalMap Method
- Block 14: GetCurrentMapState Method
- Block 15: GetPlayerPosition and GetPlayerAction Methods
- Block 16: GameIsRunning Method
- Block 17: InitializeMap Method
- Block 18: Main Method
- Conclusion
In this guide, welcome to the Text-Based Dungeon Game programming project! On this page, we will delve into a comprehensive analysis of a C# codebase for a text-based dungeon game. Whether you are just starting your journey in C# programming or seeking inspiration for your next project, this code offers valuable insights into game development and software architecture. We'll break down the code block by block, discussing each component's role, and provide a clear understanding of how this captivating game is crafted. By the end of this guide, you'll have not only a deeper knowledge of C# programming but also a newfound appreciation for the intricate world of text-based game development.
Crafting a C# Text-Based Dungeon Adventure
Explore creating-a-text-based-dungeon-game-in-csharp to help your C# assignment. This comprehensive guide provides step-by-step insights into game development and software architecture, making it an invaluable resource for aspiring programmers. Delve into the intricacies of coding a captivating text-based dungeon game, learn how to structure game elements, and acquire practical knowledge that can enhance your C# programming skills. Whether you're a beginner seeking hands-on experience or a game development enthusiast, this guide opens the doors to endless creative possibilities in the world of C# game development.
Block 1: Namespace and Class Declaration
```csharp
using System;
using System.Linq;
using System.IO;
using System.Collections.Generic;
using static GameDev.Game;
namespace GameDev
{
// Main class definition for the Dungeon Game Application
public class Game
{
// ...
}
}
```
- This block includes necessary C# `using` directives for the required libraries.
- It defines a `Game` class within the `GameDev` namespace.
Block 2: Game Class Variables and Enums
```csharp
using System;
namespace GameDev
{
public class Game
{
public enum PlayerActions
{
NOTHING,
NORTH,
EAST,
SOUTH,
WEST,
PICKUP,
ATTACK,
DROP,
QUIT
}
private PlayerActions action = PlayerActions.NOTHING;
private int playerX;
private int playerY;
public enum GameState
{
UNKNOWN,
STOP,
RUN,
START,
INIT
}
private GameState status = GameState.INIT;
private char[][] originalMap = new char[0][];
private char[][] workingMap = new char[0][];
private bool advanced = false;
private string currentMap;
private int coins;
private int kills;
public string ReadUserInput()
{
return Console.ReadLine() ?? string.Empty;
}
public int GetStepCounter()
{
return counter;
}
public void ProcessUserInput(string input)
{
var inputSplit = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
var command = inputSplit.FirstOrDefault()?.ToLower().Trim();
var args = inputSplit.Skip(1).Select(x => x.Trim()).ToArray();
switch (command)
{
case "load":
var mapName = args.FirstOrDefault();
LoadMapFromFile(mapName ?? string.Empty);
break;
case "begin":
if (GameIsRunning() == GameState.RUN)
{
action = PlayerActions.NOTHING;
}
else
{
status = GameState.RUN;
counter = 0;
var removedS = false;
foreach (var tilesY in workingMap)
{
for (var i = 0; i < tilesY.Length; i++)
{
var tile = tilesY[i];
if (tile != 'S') continue;
removedS = true;
tilesY[i] = '_';
break;
}
if (removedS) break;
}
var placedPlayer = false;
for (var y = 0; y < workingMap.Length; y++)
{
var tilesY = workingMap[y];
for (var x = 0; x < tilesY.Length; x++)
{
var tile = tilesY[x];
if (tile != 'S') continue;
playerX = x;
playerY = y;
placedPlayer = true;
workingMap[y][x] = '_';
break;
}
if (placedPlayer) break;
}
}
break;
case "w":
MovementAction(PlayerActions.NORTH);
break;
case "a":
MovementAction(PlayerActions.WEST);
break;
case "s":
MovementAction(PlayerActions.SOUTH);
break;
case "d":
MovementAction(PlayerActions.EAST);
break;
case "z":
action = PlayerActions.PICKUP;
break;
case "q":
action = PlayerActions.ATTACK;
break;
}
}
// Other methods and logic for game functionality
public GameState GameIsRunning()
{
return status;
}
private void InitializeMap()
{
status = GameState.START;
action = PlayerActions.NOTHING;
coins = 0;
var placedPlayer = false;
for (var y = 0; y < workingMap.Length; y++)
{
var tilesY = workingMap[y];
for (var x = 0; x < tilesY.Length; x++)
{
var tile = tilesY[x];
if (tile != 'S') continue;
playerX = x;
playerY = y;
placedPlayer = true;
break;
}
if (placedPlayer) break;
}
}
}
}
- In this block, various class variables and enums are defined.
- `PlayerActions` is an enum representing player actions.
- `GameState` is an enum representing game states.
- Several private variables are used to store player and game state information.
Block 3: ReadUserInput Method
```csharp
private string ReadUserInput()
{
// Read user input from the console, returning an empty string if null
return Console.ReadLine() ?? string.Empty;
}
```
- This block defines a private method for reading user input from the console.
Block 4: GetStepCounter Method
```csharp
public int GetStepCounter()
{
return counter;
}
```
- This method returns the number of steps a player has taken on the current map. It increments with each player move.
Block 5: ProcessUserInput Method
```csharp
public void ProcessUserInput(string input)
{
// Split input by space, we get command and args
var inputSplit = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
// Command is processed in lowercase to make things easier
var command = inputSplit.FirstOrDefault()?.ToLower().Trim();
// Arguments are all trimmed
// Also skip the first element as that's the command
var args = inputSplit.Skip(1).Select(x => x.Trim()).ToArray();
switch (command)
{
case "load":
// Load map
var mapName = args.FirstOrDefault();
LoadMapFromFile(mapName ?? string.Empty);
break;
case "begin":
if (GameIsRunning() == GameState.RUN)
{
// Game already running, do nothing
action = PlayerActions.NOTHING;
}
else
{
status = GameState.RUN;
counter = 0;
var removedS = false;
foreach (var tilesY in workingMap)
{
for (var i = 0; i < tilesY.Length; i++)
{
var tile = tilesY[i];
if (tile != 'S') continue;
removedS = true;
tilesY[i] = '_';
break;
}
if (removedS) break;
}
var placedPlayer = false;
for (var y = 0; y < workingMap.Length; y++)
{
var tilesY = workingMap[y];
for (var x = 0; x < tilesY.Length; x++)
{
var tile = tilesY[x];
if (tile != 'S') continue;
playerX = x;
playerY = y;
placedPlayer = true;
workingMap[y][x] = '_';
break;
}
if (placedPlayer) break;
}
}
break;
case "w":
MovementAction(PlayerActions.NORTH);
break;
case "a":
MovementAction(PlayerActions.WEST);
break;
case "s":
MovementAction(PlayerActions.SOUTH);
break;
case "d":
MovementAction(PlayerActions.EAST);
break;
case "z":
action = PlayerActions.PICKUP;
break;
case "q":
action = PlayerActions.ATTACK;
break;
}
}
```
- The `ProcessUserInput` method parses user input, interprets commands, and updates the game state accordingly.
Block 6: MovementAction Method
```csharp
private void MovementAction(PlayerActions playerAction)
{
if (GameIsRunning() != GameState.RUN) return;
action = playerAction;
counter++;
}
```
- This method handles movement-related actions (North, East, South, West).
Block 7: Update Method
```csharp
public bool Update(GameState status)
{
this.status = status;
if (status != GameState.RUN)
{
// If not running, there's no need to update, return false
return false;
}
var pos = GetPlayerPosition();
var currentTile = workingMap[pos[0]][pos[1]];
switch (GetPlayerAction())
{
case PlayerActions.NOTHING:
break;
case PlayerActions.NORTH:
playerY--;
if (!ValidPlayerMove())
playerY++;
break;
case PlayerActions.EAST:
playerX++;
if (!ValidPlayerMove())
playerX--;
break;
case PlayerActions.SOUTH:
playerY++;
if (!ValidPlayerMove())
playerY--;
break;
case PlayerActions.WEST:
playerX--;
if (!ValidPlayerMove())
playerX++;
break;
case PlayerActions.PICKUP:
if (currentTile == 'C')
{
// We can pick up a coin
coins++;
workingMap[pos[0]][pos[1]] = '_';
}
break;
case PlayerActions.ATTACK:
if (currentTile == 'M')
{
// We can slay the monster
kills++;
workingMap[pos[0]][pos[1]] = '_';
}
break;
case PlayerActions.DROP:
// Handle dropping an item
// ...
break;
case PlayerActions.QUIT:
this.status = GameState.STOP;
return false;
default:
throw new ArgumentOutOfRangeException();
}
// The tile could be updated as the player has moved
pos = GetPlayerPosition();
currentTile = workingMap[pos[0]][pos[1]];
if (currentTile == 'D')
{
// Exit, game stops here
this.status = GameState.STOP;
}
return true;
}
```
- The `Update` method updates the game state, handles player actions, and checks game conditions to determine if the game continues.
Block 8: ValidPlayerMove Method
```csharp
private bool ValidPlayerMove()
{
var pos = GetPlayerPosition();
// Check collision
return workingMap[pos[0]][pos[1]] != '#';
}
- This method checks if the player's move is valid by detecting collisions with obstacles in the game map.
Block 9: PrintMapToConsole Method
```csharp
public bool PrintMapToConsole()
{
// Check game status before printing
if (status != GameState.RUN)
{
return false;
}
Console.Clear();
var map = GetCurrentMapState();
foreach (var yTiles in map)
{
foreach (var tile in yTiles)
{
Console.Write(tile);
}
Console.WriteLine();
}
return true;
}
```
- The `PrintMapToConsole` method clears the console and displays the current state of the game map.
Block 10: PrintExtraInfo Method
```csharp
public bool PrintExtraInfo()
{
if (GameIsRunning() != GameState.RUN)
return false;
Console.WriteLine($"Coins: {coins}, moves: {counter}, kills: {kills}");
return true;
}
```
- This method prints additional information, such as the number of coins collected, moves made, and monsters killed.
Block 11: LoadMapFromFile Method
```csharp
public bool LoadMapFromFile(string mapName)
{
var mapPath = Path.Combine(Environment.CurrentDirectory, mapName);
string[] map;
try
{
map = File.ReadAllLines(mapPath);
}
catch (Exception)
{
// Couldn't load map file
return false;
}
var mapHeight = map.Length;
originalMap = new char[mapHeight][];
workingMap = new char[mapHeight][];
for (var y = 0; y < mapHeight; y++)
{
var width = map[y].Length;
originalMap[y] = new char[width];
for (var x = 0; x < width; x++)
{
// Pick the correct tile from the map string
originalMap[y][x] = map[y][x];
}
}
// Copy original to working
CloneCharArray2D(originalMap, workingMap);
InitializeMap();
return true;
}
// Helper method to clone a 2D char array
private static void CloneCharArray2D(char[][] original, char[][] dest)
{
var height = original.Length;
for (var y = 0; y < height; y++)
{
var line = original[y];
var width = line.Length;
dest[y] = new char[width];
Array.Copy(line, dest[y], width);
}
}
```
- This method loads a game map from a file, initializes the game state, and handles map-related operations.
Block 12: CloneCharArray2D Method
```csharp
private static void CloneCharArray2D(char[][] original, char[][] dest)
{
var height = original.Length;
for (var y = 0; y < height; y++)
{
var line = original[y];
var width = line.Length;
dest[y] = new char[width];
Array.Copy(line, dest[y], width);
}
}
```
- This method is a utility function to clone a 2D character array.
Block 13: GetOriginalMap Method
```csharp
public char[][] GetOriginalMap()
{
return originalMap;
}
```
- This method returns the original game map, which doesn't change as the player moves.
Block 14: GetCurrentMapState Method
```csharp
public char[][] GetCurrentMapState()
{
// Don't alter the original, clone it for showing
var returningMap = new char[workingMap.Length][];
CloneCharArray2D(workingMap, returningMap);
// Only place the player in the map if the game is running
if (GameIsRunning() == GameState.RUN)
{
var pos = GetPlayerPosition();
// Show the player's position
returningMap[pos[0]][pos[1]] = '1';
}
return returningMap;
}
- This method returns the current state of the game map with the player's position.
Block 15: GetPlayerPosition and GetPlayerAction Methods
```csharp
public int[] GetPlayerPosition()
{
return new[] { playerY, playerX };
}
public PlayerActions GetPlayerAction()
{
return action;
}
```
- These methods return the player's current position and the next player action, respectively.
Block 16: GameIsRunning Method
```csharp
public GameState GameIsRunning()
{
return status;
}
```
- This method returns the current game state (whether the game is running, stopped, etc.).
Block 17: InitializeMap Method
```csharp
private void InitializeMap()
{
status = GameState.START;
action = PlayerActions.NOTHING;
coins = 0;
// Place the player at a valid empty position
var placedPlayer = false;
for (var y = 0; y < workingMap.Length; y++)
{
var tilesY = workingMap[y];
for (var x = 0; x < tilesY.Length; x++)
{
var tile = tilesY[x];
if (tile != 'S') continue;
// Found the starting position, move the player there
playerX = x;
playerY = y;
placedPlayer = true;
break;
}
if (placedPlayer) break;
}
}
```
- This method initializes the game map and places the player in the starting position.
Block 18: Main Method
```csharp
static void Main(string[] args)
{
Game crawler = new Game();
string input = string.Empty;
Console.WriteLine("Welcome to the Commandline Dungeon!" + Environment.NewLine +
"May your Quest be filled with riches!" + Environment.NewLine);
// Loops through the input and determines when the game should quit
while (crawler.GameIsRunning() != GameState.STOP && crawler.GameIsRunning() != GameState.UNKNOWN)
{
Console.Write("Your Command: ");
input = crawler.ReadUserInput();
Console.WriteLine(Environment.NewLine);
crawler.ProcessUserInput(input);
crawler.Update(crawler.GameIsRunning());
crawler.PrintMapToConsole();
crawler.PrintExtraInfo();
}
Console.WriteLine("See you again" + Environment.NewLine +
"In the CMD Dungeon! ");
}
```
- This is the main entry point of the program, which sets up and runs the game loop, processes user input, and updates the game state based on user commands.
Conclusion
By breaking down the code into these manageable sections, you gain a better understanding of how the text-based dungeon game is structured and how different components interact to create an engaging gameplay experience. This code serves as a valuable reference for anyone interested in game development, C# programming, or software architecture. Furthermore, the knowledge and skills acquired from this guide can be a stepping stone for your game development journey, enabling you to embark on exciting projects, explore advanced game mechanics, and even create your own unique gaming experiences. As you dive deeper into the world of programming and game design, you'll find that the possibilities are virtually limitless. So, roll up your sleeves, unleash your creativity, and start crafting your very own digital adventures today. The dungeon's gates are open—venture forth and code your way to gaming greatness!
Related Samples
Check out our free C# assignment samples to deepen your knowledge and improve your coding skills. Each sample showcases important concepts and best practices in C# programming.
C#
C#
C#
Web Development
Web Development
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#
C#