I just started studying games programming and my first assignment should be a little escape room game, written in C#. I made a "room" with a 2D-Array and I want to move a character (displayed as "H") with the arrow keys on the keyboard.
static void Main(string[] args)
{
ConsoleKeyInfo consoleKey;
int XPositionCursor = 5;
int YPositionCursor = 5;
int MapWidth = 20;
int MapHeight = 20;
int GameOver = 0;
string Character = "H";
do
{
int[,] MapGeneratorArray = new int[MapWidth, MapHeight];
Console.Clear();
for (int i = 0; i < MapWidth; i )
{
Console.SetCursorPosition(i, 0);
Console.Write("I");
}
for (int i = 0; i < MapWidth; i )
{
Console.SetCursorPosition(i, MapHeight);
Console.Write("I");
}
for (int i = 0; i < MapHeight; i )
{
Console.SetCursorPosition(0, i);
Console.Write("I");
}
for (int i = 0; i < MapHeight; i )
{
Console.SetCursorPosition(MapWidth, i);
Console.Write("I");
}
Console.ReadLine();
Console.SetCursorPosition(XPositionCursor, YPositionCursor);
Console.Write(Character);
consoleKey = Console.ReadKey(true);
Console.Clear(); //Deletes the character at the "old" coordinate
switch (consoleKey.Key) //Movement of Character
{
case ConsoleKey.UpArrow:
YPositionCursor--;
Console.SetCursorPosition(XPositionCursor, YPositionCursor);
Console.WriteLine(Character);
break;
case ConsoleKey.DownArrow:
YPositionCursor ;
Console.SetCursorPosition(XPositionCursor, YPositionCursor);
Console.WriteLine(Character);
break;
case ConsoleKey.LeftArrow:
XPositionCursor--;
Console.SetCursorPosition(XPositionCursor, YPositionCursor);
Console.WriteLine(Character);
break;
case ConsoleKey.RightArrow:
XPositionCursor ;
Console.SetCursorPosition(XPositionCursor, YPositionCursor);
Console.WriteLine(Character);
break;
}
} while (GameOver == 0);
}
That's my code so far and it doesn't work quite well. After the room is displayed, the character is displayed inside the room, but as soon as I press an arrow-button, the "H" moves outside the array room and it can't be moved and the number "20" is displayed. I don't know why or what is the problem, can anyone help me?
CodePudding user response:
You are not using the map array at all. Since the map does not change during the game, there is no need to redraw it at each loop iteration. Draw it once before the loop. When you move the cursor first write a blank at the old position and then the H
at the new position. This makes the movements more fluid and the console is not flickering.
We can also detect collisions with the walls. If we have a collision, we restore the old cursor position.
The code gets somewhat easier to read, if we extract some code into methods.
private static void Write(int x, int y, char ch)
{
Console.SetCursorPosition(x, y);
Console.Write(ch);
}
private static void DrawMap(char[,] map)
{
for (int x = 0; x < MapWidth; x ) {
for (int y = 0; y < MapHeight; y ) {
Write(x, y, map[x, y]);
}
}
}
private static char[,] GenerateMap()
{
var map = new char[MapWidth, MapHeight];
for (int i = 0; i < MapWidth; i ) {
map[i, 0] = Wall;
}
for (int i = 0; i < MapWidth; i ) {
map[i, MapHeight - 1] = Wall;
}
for (int i = 0; i < MapHeight; i ) {
map[0, i] = Wall;
}
for (int i = 0; i < MapHeight; i ) {
map[MapWidth - 1, i] = Wall;
}
// Add some inside wall
for (int i = 8; i < MapHeight - 5; i ) {
map[12, i] = Wall;
}
return map;
}
I also added some sound when hitting the wall. The variables xLastCollision
and yLastCollision
are used to avoid a repeated sound when the character is hitting the same place without moving.
The main code becomes:
const int MapWidth = 20;
const int MapHeight = 20;
const char Wall = '█';
static void Main(string[] args)
{
const char Character = '☻';
int xCursor = 5, yCursor = 5;
int xLastCollision = -1, yLastCollision = -1;
bool isGameOver = false;
Console.Clear();
Console.CursorVisible = false; // Make the default console cursor invisible
char[,] map = GenerateMap();
DrawMap(map);
// Set inital cursor position
Console.ForegroundColor = ConsoleColor.Yellow;
Write(xCursor, xCursor, Character);
do {
ConsoleKeyInfo consoleKey = Console.ReadKey(true);
// Clear old cursor position
Write(xCursor, yCursor, ' ');
int oldX = xCursor, oldY = yCursor;
switch (consoleKey.Key) // Movement of Character
{
case ConsoleKey.UpArrow:
yCursor--;
break;
case ConsoleKey.DownArrow:
yCursor ;
break;
case ConsoleKey.LeftArrow:
xCursor--;
break;
case ConsoleKey.RightArrow:
xCursor ;
break;
case ConsoleKey.Escape:
isGameOver = true;
break;
}
//Detect collisions with walls
if (map[xCursor, yCursor] == (char)0) {
xLastCollision = -1;
yLastCollision = -1;
} else { // We hit the wall, restore old position
if (xCursor != xLastCollision && yCursor != yLastCollision) {
Console.Beep(800, 100);
xLastCollision = xCursor;
yLastCollision = yCursor;
}
xCursor = oldX;
yCursor = oldY;
}
Write(xCursor, yCursor, Character);
} while (!isGameOver);
}
By using a block drawing character for the walls '█' and a smiley for the character '☻' and also changing the foreground color to yellow we get a nice console game:
CodePudding user response:
Below are some ideas to get you going.
Note that you had the array for the board, but you were not using it well. Note below several pieces of code were extracted to simplify the core logic. Also, note that the array is updated and the draw method redraws every time. This could be optimized by making a copy for comparison and only redraw what has changed from one iteration to the next, just like the prevX
and prevY
allow only the cursor to be updated in the array. And finally, I changed up the data types make room for char
instead of int
and bool
instead of an int
for GameOver.
using System;
namespace stackoverflow_cli_game
{
internal class Program
{
private const char cursor = 'H';
private const char border = 'I';
private static void Main(string[] args)
{
ConsoleKeyInfo consoleKey;
int XPositionCursor = 5;
int YPositionCursor = 5;
int MapWidth = 20;
int MapHeight = 20;
bool GameOver = false;
char[,] MapGeneratorArray = InitializeMap(MapWidth, MapHeight);
MapGeneratorArray = ApplyCursorToNewPosition(MapGeneratorArray, XPositionCursor, YPositionCursor);
Console.Clear();
DrawMap(MapGeneratorArray);
do
{
consoleKey = Console.ReadKey(true);
switch (consoleKey.Key) //Movement of Character
{
case ConsoleKey.UpArrow:
YPositionCursor--;
break;
case ConsoleKey.DownArrow:
YPositionCursor ;
break;
case ConsoleKey.LeftArrow:
XPositionCursor--;
break;
case ConsoleKey.RightArrow:
XPositionCursor ;
break;
case ConsoleKey.Escape:
GameOver = true;
break;
}
if (YPositionCursor < 1) { YPositionCursor = 1; }
if (XPositionCursor < 1) { XPositionCursor = 1; }
if (YPositionCursor >= MapWidth - 1) { YPositionCursor = MapWidth - 2; }
if (XPositionCursor >= MapHeight - 1) { XPositionCursor = MapHeight - 2; }
MapGeneratorArray = ApplyCursorToNewPosition(MapGeneratorArray, XPositionCursor, YPositionCursor);
DrawMap(MapGeneratorArray);
} while (!GameOver);
}
private static int prevX = -1, prevY = -1;
private static char[,] ApplyCursorToNewPosition(char[,] mapGeneratorArray, int xPositionCursor, int yPositionCursor)
{
if (prevX > -1 && prevY > -1)
{
mapGeneratorArray[prevX, prevY] = ' ';
}
mapGeneratorArray[xPositionCursor, yPositionCursor] = cursor;
prevX = xPositionCursor;
prevY = yPositionCursor;
return mapGeneratorArray;
}
private static void DrawMap(char[,] mapGeneratorArray)
{
for (var i = 0; i < mapGeneratorArray.GetLength(0); i )
{
for (var j = 0; j < mapGeneratorArray.GetLength(1); j )
{
Console.SetCursorPosition(i, j);
Console.Write(mapGeneratorArray[i, j]);
}
}
}
private static char[,] InitializeMap(int MapWidth, int MapHeight)
{
char[,] MapGeneratorArray = new char[MapWidth, MapHeight];
for (int i = 0; i < MapWidth; i )
{
MapGeneratorArray[i, 0] = border;
}
for (int i = 0; i < MapWidth; i )
{
MapGeneratorArray[i, MapWidth - 1] = border;
}
for (int i = 0; i < MapHeight; i )
{
MapGeneratorArray[0, i] = border;
}
for (int i = 0; i < MapHeight; i )
{
MapGeneratorArray[MapHeight - 1, i] = border;
}
return MapGeneratorArray;
}
}
}
CodePudding user response:
You have to be mindful of when and where the console is being cleared. Hope this simplifies the code. Check it out, it runs perfectly.
static void Main(string[] args)
{
ConsoleKeyInfo consoleKey;
int XPositionCursor = 5;
int YPositionCursor = 5;
int MapWidth = 20;
int MapHeight = 20;
int[,] MapGeneratorArray = new int[MapWidth, MapHeight];
int GameOver = 0;
string Character = "H";
do
{
Console.Clear();
for (int i = 0; i < MapWidth; i )
{
Console.SetCursorPosition(i, 0);
Console.Write("I");
}
for (int i = 0; i < MapWidth; i )
{
Console.SetCursorPosition(i, MapHeight);
Console.Write("I");
}
for (int i = 0; i < MapHeight; i )
{
Console.SetCursorPosition(0, i);
Console.Write("I");
}
for (int i = 0; i < MapHeight; i )
{
Console.SetCursorPosition(MapWidth, i);
Console.Write("I");
}
Console.SetCursorPosition(XPositionCursor, YPositionCursor);
Console.Write(Character);
consoleKey = Console.ReadKey(true);
Console.Clear(); //Deletes the character at the "old" coordinate
switch (consoleKey.Key) //Movement of Character
{
case ConsoleKey.UpArrow:
YPositionCursor--;
break;
case ConsoleKey.DownArrow:
YPositionCursor ;
break;
case ConsoleKey.LeftArrow:
XPositionCursor--;
break;
case ConsoleKey.RightArrow:
XPositionCursor ;
break;
}
} while (GameOver == 0);
}
And get familiar with the debugger, it is your friend.