Reputation: 121
Since your help with my first issue, I have made some great progress with the RPG
I'm making for both educational and recreational purposes. Thank you all for that!
In this post, I will be asking for a solution to 2 problems I've encountered: the first problem is the following:
Now that the movement system is working properly, I've decided to start work on a map for the game. Creating this map is quite a time consuming, but making sure my program prints the map in color is even worse.
This is my method so far:
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.Write(@"^/\/\^^^ ^ /\/\ /\^^/\^/\^^^ ^ ^/\/\^/\/\ /\^^/\ ");
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.Write("ooo~o~o~oo~o~~o~oo~");
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.Write(@"^^/\^/\^/\^^/\ ^/\^^^^ /\ /\/\ /\ ^^ ^/\^/\/\^/\^ ^^");
Console.Write(@"^^/\^/\/\/\^/\^/\^/\/\^ /\ ^ ^ ^^ /\^/\^^/\^^^^^/\ ");
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.Write("oo~~~~~oooo~oo~~o~o");
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("....|....|||....|.|.|.|.|..|.....||..|..||.|.|.");
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.Write(@"^/\/\ ^");
Since my console has a width of 128, this rather large and repetitive chunk of code is to create only two lines of my 32 line map (of which I need to create multiple to create a larger game).
Making the whole map like this would be way too time-consuming (and annoying). Is there a better, more efficient way of creating a map for an RPG
(or any type of ASCII
game for that matter), which a beginner like myself would be able to understand and create?
The second issue I have is the player character. I have not yet found a way to get an '@' symbol to appear on the cursor and to get that @
to move with the cursor when I press a button. This is the code I have for my movement system:
while (true)
{
ConsoleKeyInfo input = Console.ReadKey(true);
PosX = Console.CursorLeft;
PosY = Console.CursorTop;
switch (input.KeyChar)
{
case 'w':
if (PosY > 1)
{
Console.SetCursorPosition(PosX + 0, PosY - 1);
PosX = Console.CursorLeft;
PosY = Console.CursorTop;
}
break;
case 'a':
if (PosX > 1)
{
Console.SetCursorPosition(PosX - 1, PosY + 0);
}
break;
case 's':
if (PosY < 31 )
{
Console.SetCursorPosition(PosX + 0, PosY + 1);
}
break;
case 'd':
if (PosX < 127)
{
Console.SetCursorPosition(PosX + 1, PosY + 0);
}
break;
}
}
As you can see: this is a loop, which constantly checks if a button is pressed and it moves the cursor accordingly.
How would I make sure there is an @
symbol on the cursor at all times, without getting rid of the map character it was on if the @
symbol moves?
Upvotes: 3
Views: 2027
Reputation: 1387
I took a look at your previous question to see the code you were using to create the map and such, also I'm no games programmer so my way probably isn't the best, but hopefully I've helped you learn a little if you can be bothered to read it all.
I highly recommend changing your system to the one I've outlined below, if you continue with the way you're currently creating the map, you might struggle to correctly do some things
So, the way you're currently displaying the map is pretty inefficient, you currently have no way to save the map and it's current display. So lets start there.
You can create a two dimensional array which stores the tiles on the map, that way you can save all the map tiles and the player position to the array rather than the current approach. This will allow you to update the console to display the map after the player has moved or anything else similar.
You can also create a MapTile object that specifies the character and the colour of the tile. Below I've shown how to create the map and the map tiles.
// 32 rows and 128 columns
public static MapTile[,] map = new MapTile[32, 128];
// Holds the character to be displayed and the colour to display it in
public class MapTile
{
public char character {get; set;}
public ConsoleColor colour { get; set; }
}
Now you've got a map which is made up of characters and colours. However, there is nothing in the map, we haven't yet put any MapTiles into it, so lets create a method to fill it.
// Fills the map with the tile passed in
public static void FillMap(MapTile tile)
{
// For all rows
for (int row = 0; row < 32; row++)
{
// For all columns
for (int col = 0; col < 128; col++)
{
// Make that position the same as the tile passed in
map[row, col] = tile;
}
}
}
We may have created these methods, but we've not yet called it yet, so in your main, something like this will be good.
static void Main(string[] args)
{
// Setup our default map tile
MapTile defaultTile = new MapTile();
defaultTile.character = 'o';
defaultTile.colour = ConsoleColor.Green;
// Fill our map with the default tile
FillMap(defaultTile);
...
}
So now we have a map, with a default tile, but there is no easy way to display that map to the user. So lets make a way to do that.
// Displays the map to the player
public static void DisplayMap()
{
// Clear the console so we can redisplay the map
Console.Clear();
// For all rows
for (int row = 0; row < 32; row++)
{
// For all columns
for (int col = 0; col < 128; col++)
{
// Get the right colour
Console.ForegroundColor = map[row, col].colour;
// Write the character
Console.Write(map[row, col].character);
}
Console.WriteLine();
}
}
Now, every time you call the 'DisplayMap()' method, you can redraw the map when anything happens, such as an enemy moves, or something else.
This will let us move a player around easily now. Here is the full main method after the changes:
static void Main(string[] args)
{
// Setup our default map tile
MapTile defaultTile = new MapTile();
defaultTile.character = 'o';
defaultTile.colour = ConsoleColor.Green;
// Fill our map with the default tile
FillMap(defaultTile);
// Display our map to the user
DisplayMap();
// Create a player tile that shows the player on the map
MapTile playerTile = new MapTile();
playerTile.colour = ConsoleColor.Red;
playerTile.character = 'P';
// Create a Player location, instead of using mouse
// May require using 'System.Drawing;' at the top as Point is from System.Drawing
Point playerLocation = new Point(5, 5);
while (true)
{
// Get user input
ConsoleKeyInfo input = Console.ReadKey(true);
// Update the current player position to the default tile
map[playerLocation.Y, playerLocation.X] = defaultTile;
switch (input.KeyChar)
{
case 'w':
playerLocation = new Point(playerLocation.X + 0, playerLocation.Y - 1);
// Change the players new position to the player tile
map[playerLocation.Y, playerLocation.X] = playerTile;
break;
case 'a':
playerLocation = new Point(playerLocation.X - 1, playerLocation.Y + 0);
// Change the players new position to the player tile
map[playerLocation.Y, playerLocation.X] = playerTile;
break;
case 's':
playerLocation = new Point(playerLocation.X + 0, playerLocation.Y + 1);
// Change the players new position to the player tile
map[playerLocation.Y, playerLocation.X] = playerTile;
break;
case 'd':
playerLocation = new Point(playerLocation.X + 1, playerLocation.Y + 0);
// Change the players new position to the player tile
map[playerLocation.Y, playerLocation.X] = playerTile;
break;
}
DisplayMap();
}
}
This should now allow your player to move around and you've successfully created a map that updates (admittedly slowly, but we're only beginners), I'm feeling particularly generous, so I'm giving you another method
// Create rectangle on the map with the maptile
public static void CreateRectangle(Point startPoint, int width, int height, MapTile tile)
{
// Starting from the point we specified, create a rectangle of the given map tile
for (int row = startPoint.X; row < startPoint.X + width; row++)
{
// For all columns
for (int col = startPoint.Y; col < startPoint.Y + height; col++)
{
// Make that position the same as the tile passed in
map[row, col] = tile;
}
}
}
This allows you to create extra stuff on your map, such as rivers, pathways, buildings and so on.
For example, putting this right after our 'FillMap()' method will create a lake where the player starts.
// Set up our water tile
MapTile waterTile = new MapTile();
waterTile.character = '~';
waterTile.colour = ConsoleColor.Blue;
// Create a little lake
CreateRectangle(new Point(10, 10), 4, 3, waterTile);
There is a lot in here that may be new to you, especially the first part. What I've done is basically create an array which is like a database table in a way, and it'll hold certain objects. I've also created a new object called a MapTile, you can do this with anything and hence create some stuff like a player, which has a name, an age, a hair colour, and so on, this is a big part of making games. Obviously if you knew about this stuff, all I've done is insult your intelligence, but otherwise, I hope you've learned some stuff.
Also, sorry, this isn't perfect by any stretch, the console takes a while to update in it's current state. Making the map smaller is a fix, but not a very good one, you'll have to do some research into displaying stuff to the user quickly, but if you don't mind a short wait, then leave it as is.
Upvotes: 4
Reputation: 3072
First, I'm not a game programmer so my solution might not be the best one. Read my comment for explanations.
using System;
namespace ConsoleApp1RPG
{
class Program
{
private static int PosX;
private static int PosY;
private static char LastCharMap; // used to store the last background and needed when we redraw the map when the @ moved
private static char[][] Map = new[] // map object, might store it in a text file
{
new [] { '^', '/', '\\', '/', '\\', '^', '^', '^', ' ', ' ', ' ', '^', ' ', '/', '\\', '/', '\\', ' ', '/', '\\', '^', '^', '/', '\\', '^', '/', '\\', '^', '^', '^', ' ', '^', ' ', ' ', ' ', '^', '/', '\\', '/', '\\', '^', '/', '\\', '/', '\\', ' ', '/', '\\', '^', '^', '/', '\\', ' ' },
new [] { 'o', 'o', 'o', '~', 'o', '~', 'o', '~', 'o', 'o', '~', 'o', '~', '~', 'o', '~', 'o', 'o', '~' },
new [] { '^', '^', '/', '\\', '^', '/', '\\', '^', '/', '\\', '^', '^', '/', '\\', ' ', '^', '/', '\\', '^', '^', '^', '^', ' ', '/', '\\', ' ', '/', '\\', '/', '\\', ' ', '/', '\\', ' ', ' ', '^', '^', ' ', ' ', '^', '/', '\\', '^', '/', '\\', ' ', '/', '\\', '^', '/', '\\', '^', ' ', '^', '^' },
new [] { '^', '^', '/', '\\', '^', '/', '\\', '/', '\\', '/', '\\', '^', '/', '\\', '^', '/', '\\', '^', '/', '\\', '/', '\\', '^', ' ', '/', '\\', ' ', ' ', ' ', '^', ' ', '^', ' ', ' ', '^', '^', ' ', '/', '\\', '^', '/', '\\', '^', '^', '/', '\\', '^', '^', '^', '^', '^', '/', '\\', ' ' },
new [] { 'o', 'o', '~', '~', '~', '~', '~', 'o', 'o', 'o', 'o', '~', 'o', 'o', '~', '~', 'o', '~', 'o' },
new [] { ' ', '.', '.', '.', '.', '|', '.', '.', '.', '.', '|', '|', '|', '.', '.', '.', '.', '|', '.', '|', '.', '|', '.', '|', '.', '|', '.', '.', '|', '.', '.', '.', '.', '.', '|', '|', '.', '.', '|', '.', '.', '|', '|', '.', '|', '.', '|', '.' },
new [] { '^', '/', '\\', '/', '\\', ' ', '^' }
};
static void Main(string[] args)
{
drawMap(); // draw the map
runGame(); // game loop, press Escape to stop playing
Console.Write("Press any key...");
Console.ReadKey(true);
}
// this method draw the @ char and store the background it's entering in
static void drawChar()
{
Console.SetCursorPosition(PosX, PosY); // move cursor position to new position
if ((PosY < Map.Length) && (PosX < Map[PosY].Length)) // check map object if a background exist
LastCharMap = Map[PosY][PosX]; // store it
else
LastCharMap = ' '; // background is not defined in map, use a space
Console.Write("@"); // draw the @ character
Console.SetCursorPosition(PosX, PosY); // move cursor position back because printing @ move it
}
static void runGame()
{
PosX = 0; // starting point
PosY = 0; // starting point
drawChar(); // draw @ char
ConsoleKey key = ConsoleKey.Enter; // initial value
do
{
if (Console.KeyAvailable) // if some key is pressed
{
key = Console.ReadKey(true).Key; // get that key pressed
Console.Write(LastCharMap); // draw last background because @ will move to another location
switch (key) // modify the new location of @
{
case ConsoleKey.W: // press W
if (PosY > 0)
PosY--;
break;
case ConsoleKey.S: // press S
if (PosY < Console.WindowHeight - 1) // better use this because screen height might be different
PosY++;
break;
case ConsoleKey.A: // press A
if (PosX > 0)
PosX--;
break;
case ConsoleKey.D: // press X
if (PosX < Console.WindowWidth - 1) // better use this because screen width might be different
PosX++;
break;
}
drawChar(); // draw @ char
}
} while (key != ConsoleKey.Escape); // stop game loop if Escape is pressed
}
// this method draws the map
static void drawMap()
{
Console.SetCursorPosition(0, 0); // start from top left corner
foreach (var arr in Map)
{
foreach (var c in arr)
Console.Write(c); // draw background
Console.WriteLine(); // move to new line
}
}
}
}
Upvotes: 3