Tutorial 3 – Roguelike Map Exploration using RogueSharp and MonoGame

This is part 3 in a set of tutorials for using RogueSharp and MonoGame to create a basic Roguelike game. It is meant to serve as an introduction to the RogueSharp library.

If you missed part 2 in the tutorial series you can find it here: https://roguesharp.wordpress.com/2014/05/24/tutorial-2-roguelike-field-of-view-calculation-using-roguesharp-and-monogame/

Working with Input

Before we can start accepting player input and moving around the Map we should create a new class to help us out.

Adding InputState Class

One of my favorite methods for dealing with user input comes from the original XNA game state management tutorial in the form of a class called InputState. MonoGame has recreated this in their sample library here.

I’m not going to spend time talking about this class, as I think there are already good examples of it on the web. I’ll just say that it has helper methods for dealing with input from keyboard, mouse and gamepads for up to 4 players.

You should add my modified version of this class to your project.

Hooking Up InputState

Next we’ll want to add this to Game1.cs by first adding a new private field to the class.

private InputState _inputState;

We should initialize it in the Game1() constructor

_inputState = new InputState();

And last we need to make sure that it gets updated by adding a call to its Update method from the Game’s Update( … ) just below where it says // TODO: Add your update logic here

_inputState.Update();

The code so far

Refactor Exit Game Code

Now that we have InputState we can simplify the line that already exists for exiting the game.

if ( GamePad.GetState( PlayerIndex.One ).Buttons.Back == ButtonState.Pressed
|| Keyboard.GetState().IsKeyDown( Keys.Escape ) )
   Exit();

Changes to…

if ( _inputState.IsExitGame( PlayerIndex.One ) )
{
   Exit();
}

The code so far

Placeholder Method for Handling Input

To our player class we want to create a new method to handle input from the player. For now we’ll leave it empty. Open the Player.cs file and add the following method.

public void HandleInput( InputState inputState )
{
   // Placeholder for input handling code
}

Hooking this up means we need to edit Game1.cs again and by adding the following else block after the exit handling in the Update( … ) method

else
{
   _player.HandleInput( _inputState );
}

The code so far

Responding to Basic Movement Keys

Now that we have the basic input handling wired up with a placeholder, we actually need to handle the key presses from the player. Replace the comment “// Placeholder for input handling code” with the following code in the Player.cs file.

if ( inputState.IsLeft( PlayerIndex.One ) )
{
   X--;
}
else if ( inputState.IsRight( PlayerIndex.One ) )
{
   X++;
}
else if ( inputState.IsUp( PlayerIndex.One ) )
{
   Y--;
}
else if ( inputState.IsDown( PlayerIndex.One ) )
{
   Y++;
}

If you run the game now you’ll see something like this

Player Movement

Player Movement

You should notice a couple of major issues with this

  • The player is able to walk through walls
  • The field-of-view doesn’t update as the player moves

The code so far

Making Sure Cells are Walkable Before Moving

We need to update our HandleInput( … ) method in Player.cs to not move the player if they are attempting to move into a wall.

Code

public bool HandleInput( InputState inputState, IMap map )
{
   if ( inputState.IsLeft( PlayerIndex.One ) )
   {
      int tempX = X - 1;
      if ( map.IsWalkable( tempX, Y ) )
      {
         X = tempX;
         return true;
      }
   }
   else if ( inputState.IsRight( PlayerIndex.One ) )
   {
      int tempX = X + 1;
      if ( map.IsWalkable( tempX, Y ) )
      {
         X = tempX;
         return true;
      }
   }
   else if ( inputState.IsUp( PlayerIndex.One ) )
   {
      int tempY = Y - 1;
      if ( map.IsWalkable( X, tempY ) )
      {
         Y = tempY;
         return true;
      }
   }
   else if ( inputState.IsDown( PlayerIndex.One ) )
   {
      int tempY = Y + 1;
      if ( map.IsWalkable( X, tempY ) )
      {
         Y = tempY;
         return true;
      }
   }
   return false;
}

Explanation

Line 1 – Since we need more information about the map in our HandleInput method, this means we’ll need to change the signature of the method to take an additional parameter of type IMap. We also change the return value to a bool to indicate whether the player actually moved or not.

Lines 5, 14, 23, 32 – We set a temporary variable for the location the player is attempting to move to.

Lines 6, 15, 24, 33 – We use the IsWalkable( … ) method on the map class to check to see if the Player is allowed to move into the Cell. Only if this is true do we update the location of the Player.

Lines 9, 18, 27, 36 – If we updated the location of the player, we return true.

Line 39 – If we get this far, we didn’t move the player, so return false.

The code so far

Updating Field-of-View as the Player Moves

In our Update( … ) method in Game1.cs we need to change the code

_player.HandleInput( _inputState, _map );

to this…

if ( _player.HandleInput( _inputState, _map ) )
{
   UpdatePlayerFieldOfView();
}

This change means that we will inspect the return value of our HandleInput( … ) method and if it is true, meaning the that player moved, we will update the field-of-view.

Running the game should now show something a little better

Update Field Of View

Update Field Of View

The code so far

Tint Explored Cells Fog-of-War Style

It would be nice if Cells that the Player has explored, but are not currently in the field-of-view would still be visible. We could tint those cells a grey color to show that they are not currently visible. Many games implement this feature as Fog-of-War. To achieve the effect we’ll need to first keep track of Cells the player has seen, by marking them as “Explored”. We can do this by updating our private UpdatePlayerFieldOfView() method in Game1.cs.

Code

private void UpdatePlayerFieldOfView()
{
   _map.ComputeFov( _player.X, _player.Y, 30, true );
   foreach ( Cell cell in _map.GetAllCells() )
   {
      if( _map.IsInFov( cell.X, cell.Y ) )
      {
         _map.SetCellProperties( cell.X, cell.Y, cell.IsTransparent, cell.IsWalkable, true );
      }
   }
}

Explanation

Line 4 – We loop through all of the Cells in the map by calling GetAllCells(). We really should only loop through the Cells that are in the field-of-view to make this more efficient. I’ll show how we can do that in a later blog post.

Line 6 – We check to see if the Cell is in the currently calculated field-of-view

Line 8 – When the Cell is in the field-of-view we call SetCellProperties() and update the last parameter to true which indicates that it has been explored.

Updating the Draw Method

Now that we know whether a Cell is explored or not, we need to draw it differently depending on this knowledge. Change the foreach loop in the Draw() method of Game1.cs to look like this:

Code

foreach ( Cell cell in _map.GetAllCells() )
{
   var position = new Vector2( cell.X * sizeOfSprites * scale, cell.Y * sizeOfSprites * scale );
   if ( !cell.IsExplored )
   {
      continue;
   }
   Color tint = Color.White;
   if ( !cell.IsInFov )
   {
      tint = Color.Gray;
   }
   if ( cell.IsWalkable )
   {
      spriteBatch.Draw( _floor, position, null, null, null,
         0.0f, new Vector2( scale, scale ), tint, SpriteEffects.None, 0.8f );
   }
   else
   {
      spriteBatch.Draw( _wall, position, null, null, null,
         0.0f, new Vector2( scale, scale ), tint, SpriteEffects.None, 0.8f );
   }
}

Explanation

Lines 4 through 7 – If the cell hasn’t been explored, we don’t bother drawing it.

Line 8 – When a sprite is tinted white, it effectively has no tint. It looks exactly like it was created. By default we’ll tint cells white.

Lines 9 through 12 – When a cell is not in the current field-of-view we’ll tint it Gray, overriding the default of white that we set on line 8.

Lines 16 and 21 – The spriteBatch.Draw methods are updated to also take the tint we set as a parameter.

The code so far

If we run the Game now we should see more of the effect I was shooting for. The map updates the field-of-view as the player explores and there is a tinted fog-of-war showing the areas that the player has explored.

Exploring

Exploring

Here is the final code

Additional Fun

  • Is there anything you can do to make some of the code more efficient.
  • Try changing the movement keys from the arrow keys on the keyboard to something else. Maybe “hjkl” of vim and old school Roguelikes
  • Try adding and capturing additional key commands for things like turning on and off the field-of-view and explored area.

Thanks for making it this far! If you have any feedback about things you would like to see, or how you would like me to do these tutorials differently please leave me a comment.

15 thoughts on “Tutorial 3 – Roguelike Map Exploration using RogueSharp and MonoGame

  1. Joe Byall

    Very nice implementation of the input system. What approach would you take to get the view centered on the player sprite? I was trying a viewport with little success.

    Reply
    1. Faron Bracy Post author

      One approach is to use a camera class. Here is an example of the camera class I’m using for a game I’m working on – https://gist.github.com/FaronBracy/25c77fc572794f7bcfe4
      You can call one of the Center() methods on that camera class to center your camera on exact pixel coordinates or the Cell location.
      Then your SpriteBatch.Begin method can take in the camera translation matrix like this –
      _spriteBatch.Begin( SpriteSortMode.BackToFront, null, SamplerState.PointClamp, null, null, null, Camera.TranslationMatrix );
      I plan on doing a camera tutorial here eventually, but it is probably a few weeks out still. Hopefully this will help you out in the meantime.

      Reply
  2. Pingback: Tutorial 4 – Roguelike Pathfinding using RogueSharp and MonoGame | Creating a Roguelike Game in C#

  3. caneastrale

    Really nice tutorials, they really are helping me out in understanding how a game works and make one from my ideas! I’m having a little trouble in trying to add something like a slide, a transiction between the position from cell A to cell B, I would like to have the movement a little smoother. You got any ideas on how to do that?

    Reply
    1. Faron Bracy Post author

      Hello and thank you for visiting,

      For XNA and MonoGame one of the best animation libraries I have seen is ZoomEngine.Animation. http://blogs.msdn.com/b/brandf/archive/2009/10/28/zoomengine-animation-part-1.aspx

      Yes, the blog is really old but the source code downloads still work and he has a developer talk video where he explains a lot of the concepts. You could use it to animate the player or the creatures from one tile to the next with easing functions for the transition.

      If that doesn’t work for you another good series is on animated and mobile sprites here -> http://www.xnaresources.com/default.asp?page=Tutorial:SpriteEngine:4

      Even though that last series is for XNA the code should be nearly if not exactly the same for MonoGame.

      Hope these might be useful for you.

      Reply
      1. caneastrale

        Thank you very much, I’ll look at them since they seem to be the best explained on the net. One last thing regarding the movement: is there a way to make the player move continuosly in a direction? Using your algorithm if i keep a key pressed the player only moves for one cell; coming from unity 3d I tried to find something like .KeyDown but couldn’t seem to achieve what I was searching.

      2. Faron Bracy Post author

        In MonoGame the keyboard state class has an IsKeyDown method -> https://github.com/mono/MonoGame/blob/develop/MonoGame.Framework/Input/KeyboardState.cs#L184

        In the tutorial the KeyState.cs class wraps that and exposes an IsKeyPressed method. So for example if you wanted to move continuously to the left when the left arrow key is pressed in the IsLeft() method of InputState.cs change the return from “IsNewKeyPress” to “IsKeyPressed”.

        One downside to doing this is that the input will be processed so quickly on a modern computer that your character will fly across the screen and be difficult to control. To make this work better you should also keep track of the elapsed game time since the last time the key was handled. Put in a delay of a few hundred milliseconds to make it so the player doesn’t move so fast.

  4. Jessica

    What would you suggest to only get the cells in the field of view? I see that the map has methods to .GetCellsInArea and .GetCellsInRadius. Could I achieve this with one of them?

    Reply
    1. Faron Bracy Post author

      Hello and thank you for visiting.

      There is a FieldOfView class that has documentation here https://bitbucket.org/FaronBracy/roguesharp/wiki/RogueSharp/FieldOfView/README.md
      When you call ComputeFieldOfView(…) it will return a list of all of the cells in the field-of-view. https://bitbucket.org/FaronBracy/roguesharp/wiki/RogueSharp/FieldOfView/ComputeFov.md
      If you want to get cells in a circular field-of-view the bottom of this post has an example
      https://roguesharp.wordpress.com/2017/04/25/roguesharp-4-0-pre-release/

      Hope this helps.

      Reply
  5. Znab

    Thank you for creating this tutorial. I have replaced the titled movement with smooth title movement using vector.Lerp. This replaced the X and Y variables with a vector function. I have to cast from float to int every time I want to use the map functions. Is it expensive to do these conversions when it is done frequently? If so, do you recommend any other alternative for smooth movements between titles?

    Reply
    1. Faron Bracy Post author

      That’s cool that you added smooth movement! The answer to if it is expensive or not is a difficult question to answer. There is some good information out there about conversion costs for example – https://stackoverflow.com/questions/28668348/how-expensive-is-it-to-convert-between-int-and-double.

      My personal recommendation is not to try to optimize things unless you actually start running into performance problems, or your project is finished and you are just trying to reach as many possible machines as possible. It can be a real time sync looking into this stuff.

      There is a fun xkcd about premature optimization https://www.explainxkcd.com/wiki/index.php/1691:_Optimization

      Reply
      1. Znab

        Thank you very much for sharing the links and your recommendation. It is indeed a fun chart! It will save me a bunch of time for now. I really appreciate it.

        I have another question that I hope you wish and have time to answer. I Have struggled the past few days with making the camera following the player smoothly as the player moves instead of the cell to cell movement. I have managed to make the camera move smoothly to the cell that the player is approaching but it is delayed. I have tired working with the WorldToScreen with no luck. Do you have any hints on how to make the camera follow the player as they move?

      2. Faron Bracy Post author

        What I remember using for smooth animations with XNA (should be compatible with Monogame) was one of the developer’s personal animation libraries called ZoomEngine.Animation. His original blog post is archived here – https://web.archive.org/web/20140428161508/http://blogs.msdn.com/b/brandf/archive/2009/10/28/zoomengine-animation-part-1.aspx

        The links to download his library still worked for me – https://onedrive.live.com/?authkey=%21&id=B687A23C9E1D24FD%21199&cid=B687A23C9E1D24FD

        Using that, my camera class looked like this. I believe I used the PanTo method to follow the player – https://gist.github.com/FaronBracy/47360c1365d5f0a868938a0287c18db9#file-camera-cs-L96

      3. Znab

        Thank you very much for helping me solving this problem and sharing the library. It has helped me a lot!

Leave a reply to Faron Bracy Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.