Roguelike Tutorial - Part 2 - Character and Map
In this article we’ll accomplish the following:
- [x] Create objects to represent the player.
- [x] Create map tiles and a map.
- [x] Display a map and player on the screen.
- [x] Use the keyboard to move the player.
- Part 1 - Create the project
- Part 2 - Character and map
In a lot of text engines where you want to move characters around a console, you get some additional overhead. Normally, you print the character on the console. Then, when it moves, you redraw it in the new position. After that, you also have to make sure to restore whatever was under the original spot of the character.
SadConsole can avoid that with the
SadConsole.GameHelpers.GameObject type. This type provides a positionable text object that can also be animated. The
GameObject is used when you have a bunch of moving objects that are layered on top of a background.
We’ll create a class named
EntityBase.cs that represents things placed on the map. Right-click on the project file and choose New > Add Folder and name it
Entities. Right-click on the new folder and choose New > Class… and name the class
EntityBase.cs. In this class we’ll inherit from
SadConsole.GameHelpers.GameObject. This will represent the base class for monsters, the player, things like that.
If you noticed, there is a
MoveBy method on the
EntityBase.cs class. This will adjust the position of the entity by the amount provided. It will be easy to move something X or Y spaces on the map. The
Map class doesn’t exist yet, we’ll fix that soon.
Next, add another class to the
Entities folder named
Player.cs. This class will represent the character the player controls in your game.
Now that we have a player that will be contolled on a map, we need to create a class to hold information about the game map. We also need a class to handle drawing the map.
First, we need to create objects that make up the map. A map is a grid of these objects. Some things on the map will block the player (using our movement checks) and others will represent empty space. Add another folder to the solution named
MapObjects. This folder will have each tile type that is used to make up a map. the types of tiles placed on the map.
Add a class to that folder named
TileBase.cs and add this code. This class inherits from
SadConsole.Cell which is a class that all surfaces use in rendering.
Add two more classes to the
MapObjects folder named
Floor.cs. These classes inherit from our new
TileBase class and sets it up with… well… wall things.
Now that we have tiles, we need a map class to hold them. Add a new class to the root of the project named
I put in code comments to help explain things, but here is some more information. SadConsole can take an array of
Cell types, and use that to create a surface that is drawn on the screen. If you remember, our
TileBase class inherits from
Cell. This new map type has an array of
TileBase objects and we can use that to make up each x,y position in the map. And we can use SadConsole to draw those. We’ll see how this works next.
Now that we have a map data structure and some basic information, we will use a
Screen type to not only display the map, but to handle keyboard input as well.
Screen type is a core concept in SadConsole. It represents something that receives input and can draw. A
Screen can be a parent and/or a child of another
Screen. In the case of our map, we’ll use a single screen to represent drawing the player and their surroundings.
Create another folder in the project named
Screens and add a new class named
Dungeon.cs. This screen will handle linking a drawing surface with the map tiles. The code for this screen is pretty simple; it inherits from
The first three variables in this class handle rendering, nothing exciting there. The next variable,
MapViewPoint, handles determining (starting at the top-left of the map) what part of the map we render. If we draw the map on the physical screen in a size of 60x20, and our map is 100x100 we need to support scrolling to the portion the player is at. This property handles controlling the map for that.
LoadMap method takes a
Map object and creates the
BasicSurface, which is used in drawing.
We can now create the master screen that represents the state the game is in as the player moves through the dungeon. In the
Screen folder, add a new class named
Adventure.cs. This screen will not draw anything directly, but will be used to group all child screens (only the
Dungeon screen for now) together. This new screen will also handle keyboard input.
This class is a little more complex so we’ll build it piece-by-piece.
First start with the main declarations and variables. We’ll declare the player, map, map screen, and expose the
MapViewPoint from the map screen.
Next, the constructor, which sets where on the screen the map screen will draw.
Now add the
LoadMap method, which as the name says, loads a map. This method hooks the
map.Entities.CollectionChanged method. We will use this to ensure that the dungeon screen knows about any entity the map object has. Next, we tell the dungeon screen to load the map for drawing.
public void LoadMap(Map map)
The code for the
Entities_CollectionChanged method is uneventful, it just keeps the dungeon screen’s
Children collection in sync with the entities the map has. When a
GameObject (which the entities are based on) is added to the
Children collection of a screen, it does two things. First, anytime the screen drawing physical screen position changes, the entities will adjust position along with it. And secondly, any time the screen is told to draw, all child objects are also drawn.
private void Entities_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
The last method is the
Update method. Here we will check for keyboard input and move the player accordingly.
public override void Update(TimeSpan timeElapsed)
For reference, here are the methods defined by the class, and what they do:
|MapViewPoint||This property just forwards the map’s
|Entities_CollectionChanged||Triggered when an entity is added or removed from the map. This method makes sure that the (Screen base class)
|SyncEntityOffset||Makes sure that each entity is drawn in the correct position based on the
|Update||Handles game logic for this screen.|
Finally, we need to make sure SadConsole knows about this screen. Back in Program.cs, add a static variable at the top (under
ScreenHeight/Width) to represent the adventuring mode of the game.
public static Screens.Adventure AdventureScreen;
Replace the code for the
Init method with the following code.
private static void Init()
Run the project and you should see something similar to this.
You can move the character around with the arrow keys. However, you can walk right on top of the walls. Let’s fix that.
Open up the
Map.cs file and add a new method to the map class:
public bool IsTileWalkable(int x, int y)
This method checks to see if the map data indicates a specific tile will block movement or not.
Open up the
Entities/EntityBase.cs file and modify the
MoveBy method to call the
public void MoveBy(Point change, Map map)
Now run the game and you’ll see that you can no longer walk through the walls!
In this part of the series we created some map tile objects, a player entity, and a map. We displayed the map through a screen object, and we hooked up input to move our player around.