Tutorial 2 – Roguelike Field-of-View Calculation using RogueSharp and MonoGame

This is part 2 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 1 in the tutorial you can find it here: https://roguesharp.wordpress.com/2014/05/18/tutorial-1-roguelike-map-generation-using-roguesharp-and-monogame/

Adding the Player

In the last tutorial we used RogueSharp to help us quickly generate random maps. Now we need to add a player to the map at a starting position.

The Player Sprite

Player Sprite

Player Sprite

Copy the above image (or use your own) into the Content folder of the game. Be sure to set the Properties – Copy to Output Directory to “Copy if newer”

Add Player Sprite

Add Player Sprite

Creating a Player Class

Next we’ll need a class to keep track of the player’s position and properties required for drawing the player to the screen.

Right click on the game project and choose Add -> Class…

Name the class “Player.cs” and add the following code:

Code

public class Player
{
   public int X { get; set; }
   public int Y { get; set; }
   public float Scale { get; set; }
   public Texture2D Sprite { get; set; }
   public void Draw( SpriteBatch spriteBatch )
   {
      float multiplier = Scale * Sprite.Width;
      spriteBatch.Draw( Sprite, new Vector2( X * multiplier, Y * multiplier ),
        null, null, null, 0.0f, new Vector2( Scale, Scale ),
        Color.White, SpriteEffects.None, 0.5f );
   }
}

Explanation

The X and Y properties specify the location of the cell that the player is currently standing on. (0,0) corresponds to the top left corner.

The Scale property will be used to change the size of the sprite. All of the sprites I have provided are 64×64 pixels but in the last tutorial we scaled all the map tiles down to 1/4 of their normal size, so we’ll be doing the same to this player sprite.

The Sprite property is a Texture2D that will be used for drawing the sprite.

The Draw( … ) method takes a spriteBatch in as a parameter and will draw the player to the specified spriteBatch at the position and scale defined by the properties of the class. This code assumes that the player sprite is the same size as the wall and floor sprites we used in the previous tutorial. If you substituted your own sprites you may have to tweak this.

The last parameter in the spriteBatch.Draw method is 0.5f. This represents the layer depth at which the sprite will be drawn. We need it to be drawn higher than the floor sprites or else it won’t show up.

At this point your code should look something like this

Drawing the Player at a Static Position

Add a private Player field to the Game1 class named “_player”

private Player _player;

Add the following code at the bottom of the LoadContent() method

_player = new Player
{
   X = 10,
   Y = 10,
   Scale = 0.25f,
   Sprite = Content.Load<Texture2D>( "Player" )
};

Notice that our scale is 0.25f, which is the same scale we used to draw the wall and floor sprites. Also, we are loading the “Player” sprite that we added to the Content folder previously. Note that the static position we choose to draw the sprite (10,10) has no guarantee that it will be on a floor space. We’ll correct this later.

Now to draw our player we’ll need to modify code in the Draw( … ) method of Game1.cs

Code

spriteBatch.Begin( SpriteSortMode.BackToFront, BlendState.AlphaBlend );

int sizeOfSprites = 64;
float scale = .25f;
foreach ( Cell cell in _map.GetAllCells() )
{
   var position = new Vector2( cell.X * sizeOfSprites * scale, cell.Y * sizeOfSprites * scale );
   if ( cell.IsWalkable )
   {
      spriteBatch.Draw( _floor, position,
        null, null, null, 0.0f, new Vector2( scale, scale ),
        Color.White, SpriteEffects.None, 0.8f );
   }
   else
   {
      spriteBatch.Draw( _wall, position,
         null, null, null, 0.0f, new Vector2( scale, scale ),
         Color.White, SpriteEffects.None, 0.8f );
   }
}

_player.Draw( spriteBatch );

spriteBatch.End();

Explanation

Most of the code is unchanged, but the important bits are:

Line 16 and Line 22 – You’ll see 0.8f is now specified on each of the spriteBatch.Draw calls. This tells it to render the floor and wall sprites below the 0.5f layer depth that our Player sprite is being rendered at.

Line 26 – Because we put a Draw method in our Player class, all we need to do to render it is call it here and pass in the spriteBatch we are currently using.

The code so far

Make Sure the Player Starts on a Floor Cell

Instead of hard coding the player’s position to (10,10) lets create a method that will randomly pick Cells from the map until it finds one that is a floor cell.

Code

private Cell GetRandomEmptyCell()
{
   IRandom random = new DotNetRandom();

   while( true )
   {
      int x = random.Next( 49 );
      int y = random.Next( 29 );
      if ( _map.IsWalkable( x, y ) )
      {
         return _map.GetCell( x, y );
      }
   }
}

Explanation

Line 3 – The IRandom interface comes from RogueSharp and so you’ll need to make sure you add a “using RogueSharp.Random” to the top of the Game1.cs class. In this case we are just using DotNetRandom() which is just a wrapper for the System.Random pseudo-random number generator that comes with .NET.  I’ll talk more about the different pseudo-random number generators that come with RogueSharp in a future blog post.

Lines 5 through 13 – We generate random numbers for the height and width of our map and check to see if that (x , y) Cell is walkable or not. If it is not walkable, we generate new random numbers and try again. If it is walkable we return that Cell.

Last we need to update the LoadContent() method to use our new private method.

Cell startingCell = GetRandomEmptyCell();
_player = new Player
{
   X = startingCell.X,
   Y = startingCell.Y,
   Scale = 0.25f,
   Sprite = Content.Load<Texture2D>( "Player" )
};

Run the game and you should see something like this

Map With Player

Map With Player

The code so far

Calculating and Drawing Field-of-View

Ok you’ve gotten this far but this tutorial was supposed to be about field-of-view and so far we’ve spent all our time just drawing a Player sprite! We’ve hardly used RogueSharp at all. This last part is actually the easiest because RogueSharp will do all the work for us.

Let’s start with another private method in Game1.cs

private void UpdatePlayerFieldOfView()
{
   _map.ComputeFov( _player.X, _player.Y, 30, true );
}

The first two parameters in the _map.ComputeFov() method are the X and Y coordinates from which we want to calculate the field-of-view. In this case we want it to be the current location of the Player. The third parameter 30 is the distance that the Player can see. Feel free to adjust this value and see what happens. A value of 5 or 6 could represent the player being in a dark dungeon and holding a torch or lamp. The last value true represents whether walls should be included in the field-of-view calculation or not. If they are not included they will not be “lit”.

Next we’ll need to call the UpdatePlayerFieldOfView() private method we just created at the bottom of our LoadContent() method.

UpdatePlayerFieldOfView();

Last but not least in our Draw( … ) method when we are looping through all the Cells in the map and drawing them, we’ll want to add a bit of code which will skip drawing the Cell if it is not in the field-of-view.

if ( !cell.IsInFov )
{
   continue;
}

Also in the draw method we can change the background color to black to make it look a little better by changing GraphicsDevice.Clear( Color.CornflowerBlue ); to GraphicsDevice.Clear( Color.Black );

If all is well when you run your Game it should look something like this:

Map With Field-of-View

Map With Field-of-View

Code for field-of-view calculating and drawing field-of-view

All of the code for tutorials 1 and 2

Additional Fun

  • Try replacing the sprites I provided with your own or get some fancy sprites from someone like Oryx (http://oryxdesignlab.com/sprites/)
  • Try adding code to move the player around when keys are pressed. Update the field-of-view as the player moves by calling our UpdatePlayerFieldOfView() method.
  • Refactor, refactor, refactor. Right now the Game1.cs file and class is a kitchen sink for all of our methods. Can you think of anything that could be broken out into it’s own class? Ideally each class should have a single purpose and each method should do one thing.
  • Try changing the size of the window, and re-scaling the sprites.
  • The layer depths 0.5f and 0.8f used throughout our code are magic numbers that aren’t very meaningful. Try making a class for these that has descriptive names or come up with a better solution.
  • Think of a different way to pick the player’s starting Cell other than randomly looking for floor Cells like we did. Try drawing a stairs out of the dungeon at this space as well.
Advertisements

4 thoughts on “Tutorial 2 – Roguelike Field-of-View Calculation using RogueSharp and MonoGame

  1. Pingback: Tutorial 1 – Roguelike Map Generation using RogueSharp and MonoGame | Creating a Roguelike Game in C#

  2. Pingback: Tutorial 3 – Roguelike Map Exploration using RogueSharp and MonoGame | Creating a Roguelike Game in C#

  3. Jason Ewton

    Just a quick heads up. In the section above where you modify Draw() to include “_player.Draw( spriteBatch );”, you prematurely included the check for field of view. If you try to run the code here before the UpdatePlayerFieldOfView() method is added (in a later step), you will just get a blue screen 🙂

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s