Next Tutorial Post – Simple Room Generation
Previous Tutorial Post – Player
Goal
During this tutorial we’ll handle keyboard input and allow moving the Player around the screen. We’ll update the field-of-view as the Player moves and make sure that walls cannot be walked through.
Small Refactoring
Before starting on new code I noticed two things that should have been cleaned up in the last tutorial. The first was that in our OnRootConsoleUpdate() method in Game.cs we were still setting the background color and printing a message to _mapConsole. Immediately after doing this we overwrote all that work and drew our real map over it. Basically we were doing extra work that we didn’t need to do so we can delete those lines.
The second thing is that all of the code from OnRootConsoleUpdate() for setting the background color and printing a message to each of the other three consoles is being called on each update. This is also a waste as currently the contents of those consoles never changes. All of that code can be moved to Main() so it will only be executed once when we first start our game.
Although these refactorings are optional and the code will still work without doing them, I find that it is nice to continuously look at what you have written and try to clean it up. Some people call this the “boy scout rule” which is to always leave the code cleaner than you found it.
Creating a Direction Enumeration
Before we start handling the keyboard input it could be nice to create an enumeration of all the possible directions that a player could move. Create a new Enum called Direction.cs in the Core folder of the project. Direction.cs should have the following code in it:
namespace RogueSharpV3Tutorial.Core { // Direction values correspond to numpad numbers public enum Direction { None = 0, DownLeft = 1, Down = 2, DownRight = 3, Left = 4, Center = 5, Right = 6, UpLeft = 7, Up = 8, UpRight = 9 } }
You’ll notice that the enum supports diagonals however in this tutorial game we will only allow the player to move up, down, left and right. At this time RogueSharp is missing diagonal support in it’s pathfinder but that is something that we’re looking into adding.
Setting Actor Positions
Since our DungeonMap class is responsible for knowing which Cells are walkable and it will also keep track of the positions of all of our Actors it seems like a good place to put a method that will move an Actor. Note that currently the Player is defined in the Game class and it is an exception as we want the Player to exist outside of the context of the DungeonMap so that we can regenerate the DungeonMap when moving down into deeper levels
Open DungeonMap.cs and add the following methods to it:
// Returns true when able to place the Actor on the cell or false otherwise public bool SetActorPosition( Actor actor, int x, int y ) { // Only allow actor placement if the cell is walkable if ( GetCell( x, y ).IsWalkable ) { // The cell the actor was previously on is now walkable SetIsWalkable( actor.X, actor.Y, true ); // Update the actor's position actor.X = x; actor.Y = y; // The new cell the actor is on is now not walkable SetIsWalkable( actor.X, actor.Y, false ); // Don't forget to update the field of view if we just repositioned the player if ( actor is Player ) { UpdatePlayerFieldOfView(); } return true; } return false; } // A helper method for setting the IsWalkable property on a Cell public void SetIsWalkable( int x, int y, bool isWalkable ) { Cell cell = GetCell( x, y ); SetCellProperties( cell.X, cell.Y, cell.IsTransparent, isWalkable, cell.IsExplored ); }
The SetActorPosition() method runs through a set of steps to make sure that the old cell that the actor was previously on is now walkable, and the new cell the actor is moved to is not walkable. It also updates the player field-of-view if the actor that was moved was the player. The return status here is also important. It returns false if the actor could not be moved. This is necessary when moving the player in case we try to move into a wall or other impassible cell.
The SetIsWalkable() method is really just a helper function so that we don’t have to call SetCellProperties() with so many parameters. In the future I think these helpers might be added directly to the RogueSharp Map class.
Command System
Make a new class in the Systems folder called CommandSystem.cs. The responsibility of this class will be to carry out the commands that the Player executes.
Add the following code to CommandSystem.cs:
public class CommandSystem { // Return value is true if the player was able to move // false when the player couldn't move, such as trying to move into a wall public bool MovePlayer( Direction direction ) { int x = Game.Player.X; int y = Game.Player.Y; switch ( direction ) { case Direction.Up: { y = Game.Player.Y - 1; break; } case Direction.Down: { y = Game.Player.Y + 1; break; } case Direction.Left: { x = Game.Player.X - 1; break; } case Direction.Right: { x = Game.Player.X + 1; break; } default: { return false; } } if ( Game.DungeonMap.SetActorPosition( Game.Player, x, y ) ) { return true; } return false; } }
The only method in this class for now is the MovePlayer() method which takes a Direction as a parameter. Based on the direction that is input we add or subtract to the X or Y position of the player. Then we call SetActorPosition() on the player to move them to the new position and return the result. The result will be true if the player is able to move and false otherwise.
Capturing Key Presses
The last class that we’ll need to modify is Game.cs to get everything hooked up.
First of all we need to add a new static member _renderRequired and a new property CommandSystem to the top of the class.
private static bool _renderRequired = true; public static CommandSystem CommandSystem { get; private set; }
Then in our Main() method we need to instantiate a new CommandSystem
CommandSystem = new CommandSystem();
Next we need to capture the last key pressed and handle it in OnRootConsoleUpdate()
// Event handler for RLNET's Update event private static void OnRootConsoleUpdate( object sender, UpdateEventArgs e ) { bool didPlayerAct = false; RLKeyPress keyPress = _rootConsole.Keyboard.GetKeyPress(); if ( keyPress != null ) { if ( keyPress.Key == RLKey.Up ) { didPlayerAct = CommandSystem.MovePlayer( Direction.Up ); } else if ( keyPress.Key == RLKey.Down ) { didPlayerAct = CommandSystem.MovePlayer( Direction.Down ); } else if ( keyPress.Key == RLKey.Left ) { didPlayerAct = CommandSystem.MovePlayer( Direction.Left ); } else if ( keyPress.Key == RLKey.Right ) { didPlayerAct = CommandSystem.MovePlayer( Direction.Right ); } else if ( keyPress.Key == RLKey.Escape ) { _rootConsole.Close(); } } if ( didPlayerAct ) { _renderRequired = true; } }
RLNET allows us to get the last key pressed by calling Keyboard.GetKeyPress() on the root console. The _renderRequired private member is a bit of an optimization. There is no need to redraw our map if no action was performed and the player didn’t move. Other than that all we are doing is checking if one of the direction keys was pressed and handing it off to the CommandSystem.MovePlayer() method to handle the movement. We also hooked up the Escape key to close the game.
The last step is to wrap our drawing code in OnRootConsoleRender() in an if statement checking _renderRequired so that we don’t redraw all the consoles when nothing has changed.
// Event handler for RLNET's Render event private static void OnRootConsoleRender( object sender, UpdateEventArgs e ) { // Don't bother redrawing all of the consoles if nothing has changed. if ( _renderRequired ) { // ... previous drawing code remains here _renderRequired = false; } }
As always the complete code can be found on bitbucket:
https://bitbucket.org/FaronBracy/roguesharpv3tutorial/commits/tag/05PlayerInput
Pingback: RogueSharp V3 Tutorial – Player | Creating a Roguelike Game in C#
Pingback: RogueSharp V3 Tutorial – Simple Room Generation | Creating a Roguelike Game in C#
Having yet again an issue. When i start the program it shows the fov but not the “@” then when i move it shows this “#” where the player is.
Hi, sorry for the late reply. Did you get this figured out? If not please post a link to the code in question.
DungeonMap.cs and CommandSystem.cs would probably be the most likely candidates.
Tried running though the code and the tutorial and comparing the finished project you shared to mine didn’t find anything. here is the link to DungeonMap.cs and CommandSystem.cs: https://gist.github.com/kalsamies/6dd3c3f42af9bfa177908d4fa46688f7
Sorry to say this but please can you give me some sort of a reply
Sorry about that I missed your first reply. I looked over the two files that you put in the Gist and didn’t see any problems right off. Are you using version control with Github or Bitbucket? A link to the entire project would be really useful so that I could clone it locally and give it a try.
I could share to code via email or do you know a better way. Thank you for helping me out!
well here is a dropbox link https://www.dropbox.com/s/0nvw55fq4xo76h4/Roguelike.zip?dl=0
I was able to look at your code. The OnRootConsoleRender() method in Game.cs has a few things out of order. You need to call Player.Draw after calling DungeonMap.Draw but before doing the .Blit calls. It should look more like this -> http://bit.ly/2jCWOsM
Here’s a screenshot of your code after I reordered a few things and got it working.

I’d highly recommend learning about using a version control system like Git. You can then host the Git repository for free on either Github or Bitbucket. This will allow you to track changes to your code, and have a backup if you mess something up. It will also allow you to send out links to it so that other people can see the code and help you out if you get stuck.
Thank you!
Hey!
Apologies, I’ve followed your code to the letter, but now my console is coming up completely blank. I’ve tried rearranging some things, and I’ve checked every class in this part multiple times over (it worked when the player was just an immovable object). Any idea what that could be?
Thanks!
Sorry to hear that you’re running into issues. Without seeing your code I won’t be able to help. Please upload your source to either Github or Bitbucket and put a link back here and I will be happy to take a look at it.
Hey, thanks for the tutorials!
What is the reason behind explicitly assigning each and every number in the ‘Directions’ enum?
Thanks!
At the time that this post was written RougeSharp didn’t support diagonals so really the diagonal directions shouldn’t have been in the enum. Generally it is better to delete unused code than to include it even if you think you might use it in the future. The latest version of RogueSharp **does** support diagonals in the pathfinding algorithms now in case you were interested in trying that out.
I just realized that I could have interpreted your question incorrectly. Instead of wondering why I used more values in the enum than were necessary you might have instead wondered why I didn’t just let the members of the enumeration get their default values. So to answer that question I prefer to always explicitly define enums. Defining a default value for 0 is even an FxCop rule which will warn you if you have those turned on – https://msdn.microsoft.com/en-us/library/ms182149.aspx. An enum will always be initialized with a value of 0 even if that isn’t in your enum, so better to add it explicitly.
Secondly let’s say that we are serializing the enum for a saved game perhaps. If we were not explicit in defining each of our enum members, and then later we add a new member to enum it could screw up our serialization. Whether you set the value of each member of an enum yourself or not it will still have a value. I prefer to set them than to guess what they might be. Other’s may disagree. It’s a programming preference and really up to each person to decide what they like best.
Hey, thank you for the tutorial! For some reason my player doesn’t move when I press keys (esc works ok) and I’m not sure why. Can you help me to figure it out? Here’s my project: https://github.com/ten2zen/RogueSharpTut/tree/unconfirmed Thank you and sorry for taking your time!
Glad you are enjoying the tutorials. I left a comment on your code here https://github.com/ten2zen/RogueSharpTut/pull/1/commits/d789904260cbd7ae8ccc97d366617c55e204277a
You’re just missing an if statement to set `_renderRequired = true` after the player moves.
Hope that helps!
Thank you! It works now. My bad it was so obvious!
Hello, not sure if you’re still following this but I have an issue with the numb pad. I feel like everything is right, and the keyboard directions work fine but the numb pad input does not, any suggestions? Here’s a link to a GitHub for my Game.cs and Directions.cs.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TestRogue
hosted with ❤ by GitHub
I don’t have a numpad so I can’t test this, but according to the RLNet source code you should be using `RLKey.Keypad2` etc.
https://bitbucket.org/clarktravism/rlnet/src/ed8b92dee259333bdad2e8f5bb79263034a5bb0d/RLNET/RLKey.cs?at=master&fileviewer=file-view-default#RLKey.cs-343
So your if statements need to be something like `if (keyPress.Key == RLKey.Up || keyPress.Key == RLKey.Keypad8)` if you want to allow both arrow keys and the numeric keypad for movement.
Thanks for visiting. Hope that works.
Hi Faron,
I have given you read access to my repo
https://bitbucket.org/SnipeBucket01/gameprojects/commits/173b8a92f9a147dbeaa4ce367cb6c59d923a69a1
Everything was compiling up until I added this tutorial section. I double checked everything but something I am missing. I’m getting an error on the helper method, particularly the “GetCell” part. Error is cannot implicitly convert type RogueSharp.ICell to RogueSharp.Cell.
Hello,
Sorry to hear you are running into issues.
This should be `ICell cell = GetCell(x, y);`
The tutorial was written for RogueSharp v3.0 and you are using v4.1 which is fine, but expect there to be some differences.
Please see the breaking changes in https://roguesharp.wordpress.com/2018/08/19/roguesharp-v4-1-0-released/
You Beauty! Thank you. I am still a noob at programming so some things I miss easily, I was suspecting something wasn’t pointing in the right direction, didn’t think it was that simple. I just corrected the two locations, all tested and working! Thanks again for the help.
Should I also be changing all my Cell references to ICell, I notice the other ones in MapGenerator.cs are still working. I take it if I use the new 4.1 features, they will need to be changed?
I also wondered what its like to fix bugs, now I have an idea what programmers go through lol.
Big problem! is really tiny, but it’s a compiler error so i cant go on, the problem is that “RogueSharp.ICell” can’t convert to “RogueSharp.Cell” on this line: public void SetIsWalkable(int x, int y, bool isWalkable)
{
Cell cell = GetCell(x, y); // <— Problem
SetCellProperties(cell.X, cell.Y, cell.IsTransparent, isWalkable, cell.IsExplored);
}
}
}
i really want to go on, so if you could answer i'd thank you so much!
Nevermind, i saw you fixed it, thanks for being so caring of the people reading this! even after 2 years… Have a great day!
Hi I posted this on the previous tutorial but I thought I would also post it here as I have technically complete this portion of the tutorial as well. I accidentally did this section first, which caused confusion, but ultimately everything works except for the field of view. I can control the character freely, but the diamond shape of the field of view is not drawn, and there are no compiler errors. What should I look for to be causing this? I tried comparing our code but I’m sure I missed stuff obviously.
I realized that I should supply code from now on if I have questions to ask, so here is a link with all the updated files for the project. https://github.com/Boosheroo/LearningToRogulelike.git
Thank you for any help
You need to delete lines 75 and 76 in Game.cs
https://github.com/Boosheroo/LearningToRogulelike/blob/master/Game.cs#L75
That was just a placeholder for drawing some text where the map would go. Now that the map is being drawn for real, we don’t want to draw over it with the placeholder color and text.
Here is Game.cs from the tutorial for reference.
https://bitbucket.org/FaronBracy/roguesharpv3tutorial/src/43b3f30067d3ef238b3a3674fa083fc2233cfb59/RogueSharpV3Tutorial/Game.cs?at=05PlayerInput&fileviewer=file-view-default
Hope this helps!
Thank you for the speedy response! I just deleted those lines and when I ran the program again, it didn’t break anything but I still am unable to see the field of view diamond. Is there anything else that could be causing the error?
I figured it out! Thank you so much for your help! In MapGenerator.cs I had set a boolean to false instead of true by accident earlier on, and that was what was causing the issue! Sorry for bothering you, and thank you so much for your amazing tutorial! This is so fun!
Hey !
Thank you for these blog posts.
I ran into a weird bug: when my OnRootConsoleRender looked life this, The renderer seemed 1 render behind, starting with no map, then updating 1 input late.
“`
private static void OnRootConsoleRender(object sender, UpdateEventArgs e)
{
if( _renderRequired )
{
RLConsole.Blit(_mapConsole, 0, 0, _mapWidth, _mapHeight, _rootConsole,
0, _inventoryHeight);
RLConsole.Blit(_statConsole, 0, 0, _statWidth, _statHeight, _rootConsole,
_mapWidth, 0);
RLConsole.Blit(_messageConsole, 0, 0, _messageWidth, _messageHeight, _rootConsole,
0, _screenHeight – _messageHeight);
RLConsole.Blit(_inventoryConsole, 0, 0, _inventoryWidth, _inventoryHeight, _rootConsole,
0, 0);
DungeonMap.Draw(_mapConsole);
Player.Draw(_mapConsole, DungeonMap);
_rootConsole.Draw();
_renderRequired = false;
}
}
“`
By putting both Draw methods first, I manage to fix this bug, but I don’t understand why. Anyone as an explanation ?
I imagine you have figured this out in the last three years. But for anyone else that has encountered this issue.
Not having looked into the docs, I imagine RLConsole.Blit takes the output of mapConsole and paints it in so to speak. So when you Draw both the Dungeon and the Player after the mapConsole has been blitted, the rootConsole will not have this new output.
So to make it work you draw the Dungeon and Player first on the map and then put the output of the map together with all the other consoles.