Tag Archives: MonoGame

Porting RLNET V3 Tutorial to SadConsole and MonoGame

Porting the Sample RogueSharp V3 Game to SadConsole

A few weeks ago I spent some time porting the RogueSharp sample game from RLNET to SadConsole.

SadConsolePort

SadConsole Port

Source Code

The full source code for this can be found on BitBucket:
https://bitbucket.org/FaronBracy/roguesharpsadconsolesamples/src

Notes from the porting process

Because it was a direct port I’m sure I did some things that were undesirable. What follows are some of my notes from the porting process:

RLNET has a concept of a root console. You could draw directly to the root console or you could also draw to sub consoles and then Blit them to a specific locations of the root.

For SadConsole the examples from ThakraAndy show making your own console classes that inherit from SadConsole’s Console class. I didn’t do that however. I just got rid of the RLNET root console and made each of the sub consoles a new SadConsole.Console in my main Game class. This then means that I pass around references of those consoles to each of my object’s draw methods. For example the Player class’s draw method has a reference to both the map console and the stats console and draws directly to them. It doesn’t seem like this is the intended way to work with consoles however.

I also saw that SadConsole has the idea of an Entity which from what I can tell is independent of all of the regular console cells. It seems like the proper thing to do would be to have the player, monsters, and anything that moves around on the map be an entity, so that I wouldn’t have to keep track of what is under the entity and draw it back after the entity moves.

RLNET didn’t have the same entity concept and since I was doing a straight up conversion with as few changes as possible I actually did not use the SadConsole.Entity class. Basically I keep track of when anything changes (due to player input or monsters moving on their own) and then clear all the consoles cells completely and redraw them based on the current state of the game and game objects. Most likely a bad idea, but it worked.

I couldn’t figure out how to change the background color of a range of cells (health bars for the monsters, and refresh bars for abilities) so I made my own extension method which went through the cells one at a time and changed the background color. I think there must be a better way to do that, but I couldn’t find it right off.

Additionally SadConsole has it’s own way of handling input which I completely ignored and just used standard MonoGame input handling. This was due to time constraints and me being lazy. I plan to look over how SadConsole handles input more closely in the future.

Overall the port went quickly and required far fewer changes than I expected.

Tutorial 6 – Roguelike Combat using RogueSharp and MonoGame

Edit: This tutorial was written using RogueSharp 1.1 so if you want to follow it exactly make sure you are using that version. After RogueSharp version 2.0 released there were some changes to the way that Dice work. See the updates here to get the required code changes to make this work with version 2.0 of RogueSharp.

This is part 6 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 5 in the tutorial you can find it here: https://roguesharp.wordpress.com/2014/07/13/tutorial-5-creating-a-2d-camera-with-pan-and-zoom-in-monogame/

Goal

In this tutorial we will add the ability for the Player to fight monsters and take damage. Up until this point we created a monster that would chase the player around the map there wasn’t really anything they could do except follow the player.

Enemy Awareness

When we introduced pathfinding we also created an aggressive enemy that had an all knowing awareness of where the player was at all times. We’re going to start by changing that. Enemies will only be aware of the player once they enter field-of-view.

Open up AggressiveEnemy.cs and update the constructor to also take in a parameter of type IMap. This will give the enemy access to field-of-view. This is a not great as it is coupling our Enemy class to the Map class, but it gets the job done.

// Add two private member variables
private readonly IMap _map;
private bool _isAwareOfPlayer;

// Update constructor to also take in an IMap
 public AggressiveEnemy( IMap map, PathToPlayer path )
{
  _map = map;
  ...
}

Next find the Update() method in AggressiveEnemy.cs and add the following code to create behavior for enemy awareness of the player.

public void Update()
{
  if ( !_isAwareOfPlayer )
  {
    // When the enemy is not aware of the player
    // check the map to see if they are in field-of-view
    if ( _map.IsInFov( X, Y ) )
    {
      _isAwareOfPlayer = true;
    }
  }
  // Once the enemy is aware of the player
  // they will never lose track of the player
  // and will pursue relentlessly
  if ( _isAwareOfPlayer )
  {
    _path.CreateFrom( X, Y );
    X = _path.FirstCell.X;
    Y = _path.FirstCell.Y;
  }
}

Don’t forget to also update the LoadContent() method in Game1.cs to also include the map as a parameter when we create a new AggressiveEnemy.

// Add _map parameter when we new up an enemy
_aggressiveEnemy = new AggressiveEnemy( _map, pathFromAggressiveEnemy )

The code so far

Multiple Enemies

Only having one enemy in our game is not very interesting. Lets look at Game1.cs and the ability to have a bunch of enemies.

// Switch the member variable to a list of enemies instead of a single enemy
private List<AggressiveEnemy> _aggressiveEnemies = new List<AggressiveEnemy>();

// Create a new method that will add a bunch of enemies to the game
private void AddAggressiveEnemies( int numberOfEnemies )
{
  for ( int i = 0; i < numberOfEnemies; i++ )
  {
    // Find a new empty cell for each enemy
    Cell enemyCell = GetRandomEmptyCell();
    var pathFromAggressiveEnemy =
      new PathToPlayer( _player, _map, Content.Load<Texture2D>( "White" ) );
    pathFromAggressiveEnemy.CreateFrom( enemyCell.X, enemyCell.Y );
    var enemy = new AggressiveEnemy( _map, pathFromAggressiveEnemy ) {
      X = enemyCell.X,
      Y = enemyCell.Y,
      Sprite = Content.Load<Texture2D>( "Hound" )
    };
    // Add each enemy to list of enemies
    _aggressiveEnemies.Add( enemy );
  }
}

// Replace the code in LoadContent that created an enemy
// with a call to our new AddAggressiveEnemies method
protected override void LoadContent()
{
...
  AddAggressiveEnemies( 10 );
...
}

// Replace the code in the Update method with a foreach
// to make sure each enemy's Update method is called
protected override void Update( GameTime gameTime )
{
...
  foreach ( var enemy in _aggressiveEnemies )
  {
    enemy.Update();
  }
...
}

// Replace code in the Draw method with a foreach
// to make sure each enemy's Draw method is called
protected override void Draw( GameTime gameTime )
{
...
  foreach ( var enemy in _aggressiveEnemies )
  {
    _aggressiveEnemy.Draw( spriteBatch );
    if ( Global.GameState == GameStates.Debugging || _map.IsInFov( enemy.X, enemy.Y ) )
    {
      enemy.Draw( spriteBatch );
    }
  }
...
}

If we run the game now we should see multiple hounds chasing our poor player around.

Multiple Enemies

Multiple Enemies

The code so far

Extract Base Class

We are going to need to add combat stats to both the player and the enemies. Before we do that, lets do a little refactoring and extract out a base class from both Player.cs and AggressiveEnemey.cs. I’m going to name this class Figure since I play a lot of tabletop games and I like to think of all of our monsters and the player as miniature figures on a map.

// The new figure class has common properties shared
// by both enemy and player classes
public class Figure
{
  public int X { get; set; }
  public int Y { get; set; }
  public Texture2D Sprite { get; set; }
}

// Have our enemy class inherit from Figure and remove
// X, Y and Sprite properties
public class AggressiveEnemy : Figure
...

// Do the same thing with the Player class
public class Player : Figure
...

The code so far

Add Combat Stats to Figures

There are tons of different ways combat could be handled. I’m going to use an approach familiar to table top D20 roleplaying games. In this system the attacker will roll a 20-sided die and add their attack bonus. If this result meets or exceeds the opponents armor class, the it is a hit. Otherwise it is a miss. Lets go ahead and add these properties to Figure.cs.

// Roll a 20-sided die and add this value when making an attack
public int AttackBonus { get; set; }
// An attack must meet or exceed this value to hit
public int ArmorClass { get; set; }
// Roll these dice to determine how much damage was dealt after a hit
public Dice Damage { get; set; }
// How many points of damage the figure can withstand before dieing
public int Health { get; set; }
// The name of the figure, used for attack messages
public string Name { get; set; }

Now we need to revisit Game1.cs and make sure that when we create the player and enemies that we also provide values for these properties

// In the LoadContent method update this code
_player = new Player
{
  X = startingCell.X,
  Y = startingCell.Y,
  Sprite = Content.Load<Texture2D>( "Player" ),
  // With a 15 armor class if the enemy has no attack bonus
  // the player will be hit 25% of the time
  ArmorClass = 15,
  AttackBonus = 1,
  // The player will roll 2D4 for damage or 2 x 4 sided Die
  // We can use the Dice class in RogueSharp for this
  Damage = new Dice( new List<IDie> {
    new Die( Global.Random, 4 ),
    new Die( Global.Random, 4 ) } ),
  // The player can take 50 points of damage before dying
  Health = 50,
  Name = "Mr. Rogue"
};

// In the AddAggressiveEnemies method update this code
var enemy = new AggressiveEnemy( _map, pathFromAggressiveEnemy )
{
  X = enemyCell.X,
  Y = enemyCell.Y,
  Sprite = Content.Load<Texture2D>( "Hound" ),
  // Hounds will get hit 50% of the time with no attack bonus
  ArmorClass = 10,
  AttackBonus = 0,
  // Hounds roll one 3 sided Die for damage
  Damage = new Dice( new List<IDie> {
    new Die( Global.Random, 3 ) } ),
  Health = 10,
  Name = "Hunting Hound"
};

The Dice and Die classes are part of the RogueSharp library. They can be a convenient abstraction on top of random number generators. I did realize while doing this tutorial that they are currently a bit cumbersome to create. I plan on updating RogueSharp soon to use System.Random by default if no random number generator is specified. I also plan on allowing you to create a new batch of dice by providing a string parameter to the constructor in dice notation from tabletop gaming. “2D20” or “3d6” for example.

The code so far

Introducing a Combat Manager

It would be useful to have a class that is responsible for handling combat. It will have a method for figures to call to make an attack and it will also have some utility functions for looking up which figures are on different map cells. Make a new class called CombatManager.

public class CombatManager
{
  private readonly Player _player;
  private readonly List<AggressiveEnemy> _aggressiveEnemies;

  // When we construct the CombatManager class we want to pass in references
  // to the player and the list of enemies.
  public CombatManager( Player player, List<AggressiveEnemy> aggressiveEnemies )
  {
    _player = player;
    _aggressiveEnemies = aggressiveEnemies;
  }

  // Use this method to resolve attacks between Figures
  public void Attack( Figure attacker, Figure defender )
  {
    // First create a twenty-sided die
    var attackDie = new Die( Global.Random, 20 );
    // Roll the die, add the attack bonus, and compare to the defender's armor class
    if ( attackDie.Roll() + attacker.AttackBonus >= defender.ArmorClass )
    {
      // Roll damage dice and sum them up
      int damage = attacker.Damage.Roll().Sum();
      // Lower the defender's health by the amount of damage
      defender.Health -= damage;
      // Write a combat message to the debug log.
      // Later we'll add this to the game UI
      Debug.WriteLine( "{0} hit {1} for {2} and he has {3} health remaining.",
        attacker.Name, defender.Name, damage, defender.Health );
      if ( defender.Health <= 0 )
      {
        if ( defender is AggressiveEnemy )
        {
          var enemy = defender as AggressiveEnemy;
          // When an enemies health dropped below 0 they died
          // Remove that enemy from the game
          _aggressiveEnemies.Remove( enemy );
        }
        // Later we'll want to display this kill message in the UI
        Debug.WriteLine( "{0} killed {1}", attacker.Name, defender.Name );
      }
    }
    else
    {
      // Show the miss message in the Debug log for now
      Debug.WriteLine( "{0} missed {1}", attacker.Name, defender.Name );
    }
 }

 // Helper method which returns the figure at a certain map cell
 public Figure FigureAt( int x, int y )
 {
   if ( IsPlayerAt( x, y ) )
   {
     return _player;
   }
   return EnemyAt( x, y );
 }

 // Helper method for checking if the player is at a map cell
 public bool IsPlayerAt( int x, int y )
 {
   return ( _player.X == x && _player.Y == y );
 }

 // Helper method for getting an enemy at a map cell
 public AggressiveEnemy EnemyAt( int x, int y )
 {
   foreach ( var enemy in _aggressiveEnemies )
   {
     if ( enemy.X == x && enemy.Y == y )
     {
       return enemy;
     }
   }
   return null;
 }

 // Helper method for checking if an enemy is at a map cell
 public bool IsEnemyAt( int x, int y )
 {
   return EnemyAt( x, y ) != null;
 }
}

In the Attack(…) method you’ll notice that we are using Debug.WriteLine() to report the results of combat. I know this isn’t very exciting, but we’ll work on improving that in the next tutorial. Don’t forget to instantiate a new CombatManager class in Game1.cs.

// First add a public member to Global.cs for the CombatManager
public class Global
{
  ...
  public static CombatManager CombatManager;
  ...
}

// In Game1.cs change the LoadContent method to instantiate a new CombatManager
protected override void LoadContent()
{
  ...
  Global.CombatManager = new CombatManager( _player, _aggressiveEnemies );
  ...
}

The code so far

The Player and Enemies can Attack

The last bit that we want to accomplish for this tutorial is to change our Player and AggressiveEnemy classes so that they use our CombatManager and make attacks when appropriate.

// Find the Update method in AggressiveEnemy.cs
public void Update()
{
  ...
  if ( _isAwareOfPlayer )
  {
    _path.CreateFrom( X, Y );
    // Use the CombatManager to check if the player located
    // at the cell we are moving into
    if ( Global.CombatManager.IsPlayerAt( _path.FirstCell.X, _path.FirstCell.Y ) )
    {
      // Make an attack against the player
      Global.CombatManager.Attack( this,
        Global.CombatManager.FigureAt( _path.FirstCell.X, _path.FirstCell.Y ) );
    }
    else
    {
      // Since the player wasn't in the cell, just move into as normal
      X = _path.FirstCell.X;
      Y = _path.FirstCell.Y;
    }
  }
}

// Find the HandleInput method in Player.cs
public bool HandleInput( InputState inputState, IMap map )
{
  if ( inputState.IsLeft( PlayerIndex.One ) )
  {
    int tempX = X - 1;
    if ( map.IsWalkable( tempX, Y ) )
    {
      // Check to see if there is an enemy at the location
      // that the player is attempting to move into
      var enemy = Global.CombatManager.EnemyAt( tempX, Y );
      if ( enemy == null )
      {
        // When there is not an enemy, move as normal
        X = tempX;
      }
      else
      {
        // When there is an enemy in the cell, make an
        // attack against them by using the CombatManager
        Global.CombatManager.Attack( this, enemy );
      }
      return true;
    }
    ...
    // The previous code only handles moving to the left.
    // Update the code blocks to handle each of the other
    // movement directions. Right, Up, and Down
  }
}

The code so far

If you run the game now, the player should be able to attack enemies simply by bumping into them, or trying to enter the same map square as an enemy. If you are not seeing the results of combat, you’ll need to open the Output window and choose Show output from: Debug.

Combat Debug

Combat Debug Output

Complete Tutorial 6 Code

Extra Fun

Thanks again for reading the tutorials. My release schedule has slowed down a bit for the summer. If you have suggestions for things you would like to see, please let me know in the comments. Until next time, here are some things to try for extra fun.

  • Make a health bar UI for the player
  • Show damage in the UI when the player or an enemy is hit
  • Add different types of enemies
  • Refactor some of the classes (should we really have a Global static class?)
  • Fix some of the bugs (yes there are bugs!)
    • Enemies should not be able move onto the same square as another enemy
    • The player doesn’t actually die when he reaches 0 or less health
    • Enemies should find paths around other figures to surround the player
  • Watch Jim Shepard code his Roguelike game Dungeonmans on Twitch on Wednesday and Friday afternoons (1:00 PM EST). His game is on Steam and it is created using XNA and C#

Tutorial 5 – Creating a 2D Camera with Pan and Zoom in MonoGame

This is part 5 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 4 of the tutorial you can find it here: https://roguesharp.wordpress.com/2014/06/09/tutorial-4-roguelike-pathfinding-using-roguesharp-and-monogame/

Goal

In this tutorial we will add a Camera class to our game that can be manipulated with the keyboard. Additionally when the Player is moving, the camera should always remain centered on the Player. The camera will also provide the ability to zoom in and out. This post is in response to a comment from Joe asking how to do this.

Refactoring LayerDepth

Before we get started with the camera class, lets fix those magic numbers for the sprite layer depth that we had in all of our SpriteBatch.Draw( … ). We’re going to refactor those into a class so that we don’t have to memorize those numbers.

Add a new class named LayerDepth.cs with the following code:

public static class LayerDepth
{
  public static readonly float Cells = 0.8f;
  public static readonly float Paths = 0.6f;
  public static readonly float Figures = 0.5f;
}

Now everyplace we have a spriteBatch.Draw with float specifying the layer depth like 0.8f, replace it with a static member variable from this class like LayerDepth.Cells

Follow along with all the replacements on the Bitbucket code

Removing Sprite Scaling Code

Next we are going to go through all of our SpriteBatch.Draw( … ) calls and remove all of the hardcoded scale variables. We were scaling all of our Sprites to 1/4 size by specifying a Scale of 0.25f.

You can follow along with these removals on the Bitbucket code but basically it will look like the following code:

// Old code with scaling
spriteBatch.Draw( _wall, position, null, null, null, 0.0f,
   new Vector2( scale, scale ), tint, SpriteEffects.None, LayerDepth.Cells );

// New code with scale set to Vector2.One which means no scaling
spriteBatch.Draw( _wall, position, null, null, null, 0.0f,
   Vector2.One, tint, SpriteEffects.None, LayerDepth.Cells );

Move Map and Sprite Sizes to Global

The last thing we are going to refactor is all of the map and sprite sizes that we have throughout the code. We’ll put those in Global.cs and then reference them from the rest of the game when necessary.

// In Global.cs add the following.
public static readonly int MapWidth = 50;
public static readonly int MapHeight = 30;
public static readonly int SpriteWidth = 64;
public static readonly int SpriteHeight = 64;

Now everywhere in the code where we had these numbers we’ll replace them with these static variables from Global.cs.

Follow along in the code on Bitbucket here

After we are finished with all this work, if we start our game we’ll most likely see a completely black screen. If you hit the “spacebar” we’ll see the non-scaled top left corner of the map something like this:

Top Right Map Corner

Map Without Scaling

Adding New Keys to InputState

In order for the player to be able to manipulate the camera by hand we need to be able to capture more key presses for controlling the camera. I’m going to use “WASD” keys for panning the camera and “,.” keys for zooming in and out. Feel free to change these to anything you want. The important thing is that we update our InputState.cs class to be able to handle the keys.

public bool IsScrollLeft( PlayerIndex? controllingPlayer )
{
   PlayerIndex playerIndex;
   return IsKeyPressed( Keys.A, controllingPlayer, out playerIndex );
}
public bool IsScrollRight( PlayerIndex? controllingPlayer )
{
   PlayerIndex playerIndex;
   return IsKeyPressed( Keys.D, controllingPlayer, out playerIndex );
}
public bool IsScrollUp( PlayerIndex? controllingPlayer )
{
   PlayerIndex playerIndex;
   return IsKeyPressed( Keys.W, controllingPlayer, out playerIndex );
}
public bool IsScrollDown( PlayerIndex? controllingPlayer )
{
   PlayerIndex playerIndex;
   return IsKeyPressed( Keys.S, controllingPlayer, out playerIndex );
}
public bool IsZoomOut( PlayerIndex? controllingPlayer )
{
   PlayerIndex playerIndex;
   return IsNewKeyPress( Keys.OemPeriod, controllingPlayer, out playerIndex );
}

public bool IsZoomIn( PlayerIndex? controllingPlayer )
{
   PlayerIndex playerIndex;
   return IsNewKeyPress( Keys.OemComma, controllingPlayer, out playerIndex );
}

The code so far

Introducing the Camera Class

Next we need to add a new class to our solution named “Camera.cs” and add the following code below to it. This is a large class and I’m going to try something different by putting comments before the interesting lines instead of describing the code afterwords by referencing line-numbers like I did in previous tutorials. Let me know which approach you like better for future articles.

public class Camera
{
   // Construct a new Camera class with standard zoom (no scaling)
   public Camera()
   {
      Zoom = 1.0f;
   }

   // Centered Position of the Camera in pixels.
   public Vector2 Position { get; private set; }
   // Current Zoom level with 1.0f being standard
   public float Zoom { get; private set; }
   // Current Rotation amount with 0.0f being standard orientation
   public float Rotation { get; private set; }

   // Height and width of the viewport window which we need to adjust
   // any time the player resizes the game window.
   public int ViewportWidth { get; set; }
   public int ViewportHeight { get; set; }

   // Center of the Viewport which does not account for scale
   public Vector2 ViewportCenter
   {
      get
      {
         return new Vector2( ViewportWidth * 0.5f, ViewportHeight * 0.5f );
      }
   }

   // Create a matrix for the camera to offset everything we draw,
   // the map and our objects. since the camera coordinates are where
   // the camera is, we offset everything by the negative of that to simulate
   // a camera moving. We also cast to integers to avoid filtering artifacts.
   public Matrix TranslationMatrix
   {
      get
      {
         return Matrix.CreateTranslation( -(int) Position.X,
            -(int) Position.Y, 0 ) *
            Matrix.CreateRotationZ( Rotation ) *
            Matrix.CreateScale( new Vector3( Zoom, Zoom, 1 ) ) *
            Matrix.CreateTranslation( new Vector3( ViewportCenter, 0 ) );
      }
   }

   // Call this method with negative values to zoom out
   // or positive values to zoom in. It looks at the current zoom
   // and adjusts it by the specified amount. If we were at a 1.0f
   // zoom level and specified -0.5f amount it would leave us with
   // 1.0f - 0.5f = 0.5f so everything would be drawn at half size.
   public void AdjustZoom( float amount )
   {
      Zoom += amount;
      if ( Zoom < 0.25f )
      {
         Zoom = 0.25f;
      }
   }

   // Move the camera in an X and Y amount based on the cameraMovement param.
   // if clampToMap is true the camera will try not to pan outside of the
   // bounds of the map.
   public void MoveCamera( Vector2 cameraMovement, bool clampToMap = false )
   {
      Vector2 newPosition = Position + cameraMovement;

      if ( clampToMap )
      {
         Position = MapClampedPosition( newPosition );
      }
      else
      {
         Position = newPosition;
      }
   }

   public Rectangle ViewportWorldBoundry()
   {
      Vector2 viewPortCorner = ScreenToWorld( new Vector2( 0, 0 ) );
      Vector2 viewPortBottomCorner =
         ScreenToWorld( new Vector2( ViewportWidth, ViewportHeight ) );

      return new Rectangle( (int) viewPortCorner.X,
         (int) viewPortCorner.Y,
         (int) ( viewPortBottomCorner.X - viewPortCorner.X ),
         (int) ( viewPortBottomCorner.Y - viewPortCorner.Y ) );
   }

   // Center the camera on specific pixel coordinates
   public void CenterOn( Vector2 position )
   {
      Position = position;
   }

   // Center the camera on a specific cell in the map
   public void CenterOn( Cell cell )
   {
      Position = CenteredPosition( cell, true );
   }

   private Vector2 CenteredPosition( Cell cell, bool clampToMap = false )
   {
      var cameraPosition = new Vector2( cell.X * Global.SpriteWidth,
         cell.Y * Global.SpriteHeight );
      var cameraCenteredOnTilePosition =
         new Vector2( cameraPosition.X + Global.SpriteWidth / 2,
             cameraPosition.Y + Global.SpriteHeight / 2 );
      if ( clampToMap )
      {
         return MapClampedPosition( cameraCenteredOnTilePosition );
      }

      return cameraCenteredOnTilePosition;
   }

   // Clamp the camera so it never leaves the visible area of the map.
   private Vector2 MapClampedPosition( Vector2 position )
   {
      var cameraMax = new Vector2( Global.MapWidth * Global.SpriteWidth -
          ( ViewportWidth / Zoom / 2 ),
          Global.MapHeight * Global.SpriteHeight -
          ( ViewportHeight / Zoom / 2 ) );

      return Vector2.Clamp( position,
         new Vector2( ViewportWidth / Zoom / 2, ViewportHeight / Zoom / 2 ),
         cameraMax );
   }

   public Vector2 WorldToScreen( Vector2 worldPosition )
   {
      return Vector2.Transform( worldPosition, TranslationMatrix );
   }

   public Vector2 ScreenToWorld( Vector2 screenPosition )
   {
      return Vector2.Transform( screenPosition,
          Matrix.Invert( TranslationMatrix ) );
   }

   // Move the camera's position based on input
   public void HandleInput( InputState inputState,
      PlayerIndex? controllingPlayer )
   {
      Vector2 cameraMovement = Vector2.Zero;

      if ( inputState.IsScrollLeft( controllingPlayer ) )
      {
         cameraMovement.X = -1;
      }
      else if ( inputState.IsScrollRight( controllingPlayer ) )
      {
         cameraMovement.X = 1;
      }
      if ( inputState.IsScrollUp( controllingPlayer ) )
      {
         cameraMovement.Y = -1;
      }
      else if ( inputState.IsScrollDown( controllingPlayer ) )
      {
         cameraMovement.Y = 1;
      }
      if ( inputState.IsZoomIn( controllingPlayer ) )
      {
         AdjustZoom( 0.25f );
      }
      else if ( inputState.IsZoomOut( controllingPlayer ) )
      {
         AdjustZoom( -0.25f );
      }

      // When using a controller, to match the thumbstick behavior,
      // we need to normalize non-zero vectors in case the user
      // is pressing a diagonal direction.
      if ( cameraMovement != Vector2.Zero )
      {
         cameraMovement.Normalize();
      }

      // scale our movement to move 25 pixels per second
      cameraMovement *= 25f;

      MoveCamera( cameraMovement, true );
   }
}

Whew! That’s a lot of code. We should go ahead and hook it up to see all this work pay off.

And here is the link to the code on Bitbucket

Hooking up the Camera

Lets first add a line to our Global.cs

public static readonly Camera Camera = new Camera();  

This will let us access the Camera from anywhere.

Now we’ll want to update Game1.cs first by setting the Camera Viewport Width and Height in the Intialize() method.

Global.Camera.ViewportWidth = graphics.GraphicsDevice.Viewport.Width;
Global.Camera.ViewportHeight = graphics.GraphicsDevice.Viewport.Height;

We’ll also want to center the Camera on the Player’s starting cell by adding the following code to the LoadContent() method of Game1.cs

Global.Camera.CenterOn( startingCell )

In the Update( … ) method of Game1.cs we want to to call the HandleInput( … ) method on the Camera class to take care of any pan or zoom key presses from the player.

Global.Camera.HandleInput( _inputState, PlayerIndex.One );

Also in the Update( … ) method of Game1.cs in the block where we handle player input, we want to add a bit of code to re-center the Camera on the Player after the Player moves.

Global.Camera.CenterOn( _map.GetCell( _player.X, _player.Y ) );

And the very last bit is the best. When we call the Draw( … ) method of Game1.cs and make our spriteBatch.Begin( … ) call we want to pass in the Camera’s translation matrix. That’s all we have to do to get everything to draw appropriately and at the correct scale.

// The old spriteBatch.Begin call looked like this...
spriteBatch.Begin( SpriteSortMode.BackToFront, BlendState.AlphaBlend );

// Replace it with this call, with the final parameter being the Camera's translation matrix
spriteBatch.Begin( SpriteSortMode.BackToFront, BlendState.AlphaBlend, 
    null, null, null, null, Global.Camera.TranslationMatrix );

If you run the game now you’ll have full camera control.

Controlling the Camera

Controlling the Camera

The complete code

RogueSharp 1.1 Released – Cave-Like Map Creation Using Cellular Automata

RogueSharp Version 1.1

Version 1.1 of RogueSharp was just released. It now includes a new map creation strategy for creating cave-like maps based off of an article on RogueBasin with some custom modifications.

Usage

IMapCreationStrategy<Map> mapCreationStrategy = 
   new CaveMapCreationStrategy<Map>( 50, 30, 45, 4, 2 );
IMap map = Map.Create( mapCreationStrategy );

Parameters

  • width – The width of the Map (in Cells not pixels) to be created
  • height – The height of the Map (in Cells not pixels) to be created</param>
  • fillProbability – Recommend using values between 40 and 60. Percent chance that a given cell will be a floor when randomizing all cells before starting the cellular automata algorithm.
  • totalIterations – Recommend using values between 2 and 5. Number of times to execute the cellular automata algorithm.
  • cutoffOfBigAreaFill – Recommend using value less than 4. The iteration number to switch from the large area fill algorithm to a nearest neighbor algorithm
  • random (optional) – A class implementing IRandom that will be used to generate pseudo-random numbers necessary to create the Map. If this parameter is omitted then a default implementation of .NET’s System.Random will be used.

Tutorial 4 – Roguelike Pathfinding using RogueSharp and MonoGame

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

Goal

In this tutorial we will be adding an aggressive enemy to the game that will hunt the player down. This enemy is a Hound straight out the Baskervilles and it will seek out the player no matter how far away the player is. We will also be adding a debugging state to the game, that when invoked will show us as developers more details about what is going on within the game. These details would normally not be available to a regular player but will assist us with developing the game.

Adding Sprites

We’ll need to add two sprites to our game for this tutorial. First we’ll add the aggressive enemy hound that will be hunting the player. Second we’ll add a solid white 64×64 pixel sprite that we’ll tint different colors and overlay on the map to show paths when debugging. Trust me, it will make more sense once we get there.

Aggressive Hound

Aggressive Hound

White Texture

White Texture

Remember to add sprites you 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“.

The code so far

Game State

Next we are going to add a new class file. Click on the game project and press “Shift + Alt + C“. This is a shortcut for adding a new class. You can also right click and add it if you prefer.

Name the class Global.cs and add the following code to it.

namespace ExampleGame
{
   public enum GameStates
   {
      None = 0,
      PlayerTurn = 1,
      EnemyTurn = 2,
      Debugging = 3
   }
   public class Global
   {
      public static GameStates GameState { get; set; }
   }
}

I’m actually not happy to add this class to our code. We now have a globally accessible game state and have defined a few potential states which will be useful to us, but this isn’t a great way to go about it. You’ll see that because of this choice our code will be littered with “if” statements comparing the current game state to another state before doing logic. Basically this is not a solution that will scale well as our game gets larger. I only choose it with reluctance for ease of understanding and simplicity.

I’d recommend reading Programming Game AI by Example, written by Mat Buckland for some better examples. The chapter on finite state machines is partially available online here http://www.ai-junkie.com/architecture/state_driven/tut_state1.html

The code so far

Debugging Mode

Now we’ll go ahead and use our new Global.GameState to determine if we are in debugging mode or not. In debugging mode we’ll remove the fog of war so that we can see the entire map. As a key to switch too and from debugging mode we’ll use the spacebar.

Start by adding an “else if” block to the Update( … ) method in Game1.cs just below the existing check for exiting the game.

if ( _inputState.IsExitGame( PlayerIndex.One ) )
{
   Exit();
}
// New code to handle switching modes when spacebar is pressed
else if ( _inputState.IsSpace( PlayerIndex.One ) )
{
   if ( Global.GameState == GameStates.PlayerTurn )
   {
      Global.GameState = GameStates.Debugging;
   }
   else if ( Global.GameState == GameStates.Debugging )
   {
      Global.GameState = GameStates.PlayerTurn;
   }
}

Next we’ll update the Draw( … ) method in Game1.cs by changing the first two if blocks as follows:

if ( !cell.IsExplored && Global.GameState != GameStates.Debugging )
{
   continue;
}
Color tint = Color.White;
if ( !cell.IsInFov && Global.GameState != GameStates.Debugging )
{
   tint = Color.Gray;
}

The changes are on lines 1 and 6 where we check the game state and only skip drawing Cells or tint Cells if we are _not_ currently in Debugging mode.

Now if we run the game and press the spacebar we should see something like this

Debugging Mode

Debugging Mode

The code so far

Introducing an Enemy

It’s time to unleash the hounds! Before we can make our first enemy, we’ll need a class to keep track of it.

Aggressive Enemy Class

Add a new class file by clicking on the game project and press “Shift + Alt + C“. Name the class AggressiveEnemy.cs and add the following code to it.

namespace ExampleGame
{
   public class AggressiveEnemy
   {
      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 );
      }
   }
}

This code should look familiar from the Player.cs class. We use it again here to keep track of the position of the enemy and to be able to draw it to the screen. Because the code appears in two classes, we should really refactor it, but for now I’ll leave it using what I like to call copy and paste inheritance. 🙂

The code so far

Drawing the Enemy in an Empty Cell

Add a private field to the top of the Game1 class

private AggressiveEnemy _aggressiveEnemy;

Add the following code to the LoadContent() method to setup our enemy.

startingCell = GetRandomEmptyCell();
_aggressiveEnemy = new AggressiveEnemy()
{
   X = startingCell.X,
   Y = startingCell.Y,
   Scale = 0.25f,
   Sprite = Content.Load<Texture2D>( "Hound" )
};

Line 1 – Notice that we reuse the GetRandomEmptyCell() method to find a starting cell for the enemy. There are a couple of possible bugs here that we’ll talk about later.
Line 7 – We are going to give this enemy the “Hound” texture that added to the project earlier.

The last thing we need to do to draw the enemy is to update our Draw( … ) method.

_aggressiveEnemy.Draw( spriteBatch );

If we run the game now we should see something like this:

Hound Bug

Hound Bug

The code so far

Fixing Bugs

There are at least two bugs right now. First the way that we implemented GetRandomEmptyCell() previously has a bug if we call it more than once. The bug is that we are initializing a new DotNetRandom() within the method. Because we do not provide a parameter when we construct it, it will use the current system time (ticks) to seed the pseudo-random number generator. Two pseudo-random number generators initialized with the same seed will always generate the same set of numbers in the same order. This is problematic because if we called GetRandomEmptyCell() twice within the same tick, we would get the same numbers generated.

One way to overcome this is to move the initialization of our DotNetRandom() to our Global class and then have GetRandomEmptyCell() use that.

public class Global
{
   public static readonly IRandom Random = new DotNetRandom();
   public static GameStates GameState { get; set; }
}

Line 3 – The only line that we changed. Add the DotNetRandom initalization to our global class and reference this whenever we need a random number generator.

private Cell GetRandomEmptyCell()
{
   while ( true )
   {
      int x = Global.Random.Next( 49 );
      int y = Global.Random.Next( 29 );
      if ( _map.IsWalkable( x, y ) )
      {
         return _map.GetCell( x, y );
      }
   }
}

Lines 5 and 6 – Don’t forget to update GetRandomEmptyCell() to use the static Global Random now.

The code so far

The second bug that you might have noticed is that we draw Hound even if it isn’t currently in the field-of-view for the player. We can fix this by updating our Draw( … ) method in Game1.cs by wrapping an “if” statement around our drawing code for the aggressive enemy.

if ( Global.GameState == GameStates.Debugging
   || _map.IsInFov( _aggressiveEnemy.X, _aggressiveEnemy.Y ) )
{
   _aggressiveEnemy.Draw( spriteBatch );
}

If you run the game now you’ll see that the Hound will only be draw when he is in the player’s Field-of-View.

The code so far

Pathfinding

As usual we had to do a lot of setup to get to the important part of the tutorial. Now we are going to look at RogueSharp’s pathfinding capability and have the Hound start chasing the Player.

Finding a Path to the Player

We can start off by adding a new class that will be able to handle finding and drawing a path to the player. Add a new class file by clicking on the game project and press “Shift + Alt + C“. Name the class PathToPlayer.cs and add the following code to it.

public class PathToPlayer
{
  private readonly Player _player;
  private readonly IMap _map;
  private readonly Texture2D _sprite;
  private readonly PathFinder _pathFinder;
  private IEnumerable<Cell> _cells;

  public PathToPlayer( Player player, IMap map, Texture2D sprite )
  {
    _player = player;
    _map = map;
    _sprite = sprite;
    _pathFinder = new PathFinder( map );
  }
  public Cell FirstCell
  {
    get
    {
      return _cells.First();
    }
  }
  public void CreateFrom( int x, int y )
  {
    _cells = _pathFinder.ShortestPath( _map.GetCell( x, y ),
      _map.GetCell( _player.X, _player.Y ) );
  }
  public void Draw( SpriteBatch spriteBatch )
  {
    if ( _cells != null && Global.GameState == GameStates.Debugging )
    {
      foreach ( Cell cell in _cells )
      {
        if ( cell != null )
        {
          float scale = .25f;
          float multiplier = .25f * _sprite.Width;
          spriteBatch.Draw( _sprite, new Vector2( cell.X * multiplier, cell.Y * multiplier ),
            null, null, null, 0.0f, new Vector2( scale, scale ), Color.Blue * .2f,
            SpriteEffects.None, 0.6f );
        }
      }
    }
  }
}

Line 6 – Instantiate a new PathFinder object which comes from RogueSharp.
Line 7 – IEnumerable<Cell> will hold each Cell in the path from a location to the Player.
Line 9 – The constructor takes a Player, IMap, and Texture2D as parameters. The Player and IMap are both necessary for finding a path through the Map to the Player. The Texture2D is used to draw the path.
Lines 16 to 21 – The FirstCell property will return the starting Cell in the path from a location to the Player. We will use this later to find the first step the Hound should take to advance towards the Player
Line 23 – The CreateFrom() method takes an X and Y coordinate as parameters and calculates the shortest path to the Player.
Lines 25 and 26 – The ShortestPath( … ) method on the pathfinder object takes two Cells as parameters. The first Cell is the source of the path, or where it starts. The second parameter is the destination Cell or end of the path. In this case the destination is always where the Player is, and the source is provided as parameters to this method.
Line 28 – The Draw method will be used to show the path on the screen.
Line 30 – We only want to draw the path when we are in Debugging mode.
Line 39 – Remember we are using a pure white texture for the Path. We’ll tint it to Color.Blue and multiply that by .2f to make it slightly transparent.

The code so far

Updating the Enemy

Next we need to add an instance of our new PathToPlayer class to our AggressiveEnemy class.

public class AggressiveEnemy
{
  private readonly PathToPlayer _path;
  public int X { get; set; }
  public int Y { get; set; }
  public float Scale { get; set; }
  public Texture2D Sprite { get; set; }
  public AggressiveEnemy( PathToPlayer path )
  {
    _path = path;
  }
  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 );
    _path.Draw( spriteBatch );
  }
}

Line 3 – Add an instance of PathToPlayer
Line 8 through 11 – Add a constructor which takes a PathToPlayer as a parameter and sets the private field.
Line 17 – After we draw the enemy, draw the path by calling the Draw( … ) method on our PathToPlayer instance.

Since we changed the signature of the constructor for AggressiveEnemy we need to update our LoadContent() method in Game1.cs by changing the line “_aggressiveEnemy = new AggressiveEnemy();” to the following

var pathFromAggressiveEnemy = new PathToPlayer( _player, _map, Content.Load<Texture2D>( "White" ) );
pathFromAggressiveEnemy.CreateFrom( startingCell.X, startingCell.Y );
_aggressiveEnemy = new AggressiveEnemy( pathFromAggressiveEnemy )

Line 1 – Instantiate a new PathToPlayer and pass in the Player, Map and White texture that we added at the beginning of this tutorial.
Line 2 – Call the CreateFrom( … ) method on our PathToPlayer using the Cell where the Hound started as the source of the path.
Line 3 – Pass in our PathToPlayer instance to the new constructor for AggressiveEnemy.

If you run the game and hit the Spacebar to switch to Debugging mode you should see something like this:

Path To Player

Path To Player

The code so far

Following the Path

Well now that  we have a path, the final task left to us is to have the Hound actually follow it and hunt down our player.

First we’ll add an Update() method to our AggressiveEnemy class

public void Update()
{
  _path.CreateFrom( X, Y );
  X = _path.FirstCell.X;
  Y = _path.FirstCell.Y;
}

Line 3 – Create a path from the Hound’s current X and Y location to the Player by calling CreateFrom( … )
Line 4 and 5 – Set the X and Y location of the Hound to the first Cell in the path we calculated by using the FirstCell property.

Last we add more code to our Update( … ) method in Game1.cs to handle switching between the Player and the Enemy turn. Place the following code in the “else” block overwriting the code that is already there.

if ( Global.GameState == GameStates.PlayerTurn
     && _player.HandleInput( _inputState, _map ) )
{
   UpdatePlayerFieldOfView();
   Global.GameState = GameStates.EnemyTurn;
}
if ( Global.GameState == GameStates.EnemyTurn )
{
   _aggressiveEnemy.Update();
   Global.GameState = GameStates.PlayerTurn;
}

Lines 1 through 6 – If it is the Player’s turn wait for them to move. Then update the field-of-view and change the GameState to the Enemy’s turn.
Lines 7 through 11 – If it is the Enemy’s turn call the Update() method on our aggressive enemy Hound. This will make the Hound move one step toward the player. Then switch the state back to the Player’s turn

If we run our Game now we should be ruthlessly hunted down by this fiendish Hound!

Unleash The Hound

Unleash The Hound

The last commit

The final code for tutorial 4

Extra Fun

Thank you for being patient and making it this far. I expected to have this tutorial out a couple of days ago but it took longer than I expected. Until I get the next tutorial out try some of these challenges:

  • Try making a cowardly enemy that flees from the player
  • Make a system for handling attacks so the player and enemies can fight each other
  • Think about ways to make a loot system
  • Refactor anything that you don’t like

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.

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.

Tutorial 1 – Roguelike Map Generation using RogueSharp and MonoGame

This is part 1 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. This tutorial assumes that you already have Visual Studio installed. I’m using VS 2013.

Preparing the Project

  1. Go and download MonoGame if you don’t already have it. I’m using version 3.2 which can be found here: http://www.monogame.net/2014/04/07/monogame-3-2/
  2. Create a new MonoGame Windows Project.

    Create a new MonoGame Windows Project

    Create a new MonoGame Windows Project

  3. Start the project and make sure that you get a Blue Screen of Success. I know what you were thinking when I said Blue Screen but trust me, this one is good!

    Blue Screen Of Success

    Blue Screen Of Success

  4. Your code at this point should look something like this.
  5. Next we’ll need to add Floor and Wall sprites into the content folder. Be sure to set their Copy to Output Directory properties to “Copy if Newer”

    Add Floor and Wall Sprites

    Add Floor and Wall Sprites

  6. Here are the sprites I’m using but feel free to substitute your own.
    Floor Sprite

    Floor Sprite

    Wall Sprite

    Wall Sprite

  7. Now your code should be like this
  8. Next we are going to create two new Texture2D fields and load our Floor and Wall sprites into these using Content.Load. Lastly we’ll use a SpriteBatch to draw our two textures to the screen. I’m not going to go into great detail as to how this works as there are plenty of resource out there already for this. Here is one example
  9. Code so far
  10. You should now be able to run the project and verify that you are seeing the floor and wall textures on your patented Blue Screen of Success.

    Rendering Floor and Wall Sprites

    Rendering Floor and Wall Sprites

Introducing RogueSharp

Hopefully that wasn’t too bad getting here. Now is where the real tutorial begins. We’re going to start using the power of RogueSharp to get moving really quickly on our game.

Add the RogueSharp NuGet Package to the Project

Right click on the project and choose Manage NuGet Packages…

Manage NuGet Packages

Manage NuGet Packages

Search for “RogueSharp” and add it to the project.

RogueSharp NuGet Package

RogueSharp NuGet Package

Creating a Map

In the Game1.cs file we’ll first need to add a using directive for the RogueSharp namespace

using RogueSharp;

Next we want to create a private field of type IMap named _map in the Game1 class

private IMap _map;

In the Initialize() method place the following code just under where it says // TODO: Add your initialization logic here.

Code

IMapCreationStrategy<Map> mapCreationStrategy =
    new RandomRoomsMapCreationStrategy<Map>( 50, 30, 100, 7, 3 );
_map = Map.Create( mapCreationStrategy );

Explanation

There is a static method on the Map class that takes an IMapCreationStrategy as a parameter. Several implementations of this strategy are included with RogueSharp. This also allows anyone to create their own Map generation code by simply creating a class that implements the IMapCreationStrategy interface.

In this example we didn’t make our own class, but instead used the RandomRoomsMapCreationStrategy which is included with the RogueSharp library.

With this strategy a Map will be generated by trying to place rooms up to the maximum number specified in random locations around the Map. Each room will have a height and width between the minimum and maximum room size. If a room would be placed in such a way that it overlaps another room, it will not be placed. Once all rooms have have been placed, or thrown out because they overlap, corridors will be created between rooms in a random manner.

The constructor takes 5 parameters which are as follows:

  • width = The width of the Map to be created
  • height = The height of the Map to be created
  • maxRooms = The maximum number of rooms that will exist in the generated Map
  • roomMaxSize = The maximum width and height of each room that will be generated in the Map
  • roomMinSize = The minimum width and height of each room that will be generated in the Map

With the parameters we provided we said create a Map that is 50 by 30 Cells in size. Try to place up to 100 rooms on this Map (though many of these will be thrown out if they overlap an existing room). And last make sure that each side of the rooms are between 3 and 7 Cells long.

The source code for the RandomRoomsMapCreationStrategy can be found here.

Your code should now look something like this

Drawing the Map

It’s great that we created a Map, but we can’t actually see it yet.

To actually see our map we’re going to need to add some code to the Draw() method in Game1.cs. Place the following code just under where it says // TODO: Add your drawing code here.

Code

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

int sizeOfSprites = 64;
foreach ( Cell cell in _map.GetAllCells() )
{
   if ( cell.IsWalkable )
   {
      var position = new Vector2( cell.X * sizeOfSprites, cell.Y * sizeOfSprites );
      spriteBatch.Draw( _floor, position, Color.White );
   }
   else
   {
      var position = new Vector2( cell.X * sizeOfSprites, cell.Y * sizeOfSprites );
      spriteBatch.Draw( _wall, position, Color.White );
   }
}

spriteBatch.End();

Explanation

Line 3 – We set a variable corresponding to the size of the sprites we are using for our floor and wall Cells. If you used the graphics provided above these are 64 pixels square. This value will be used to calculate where to draw each sprite.

Line 4 – Next we call the GetAllCells() method on our newly created _map. This returns an IEnumerable<Cell> of all Cells in the map that we can iterate through using a foreach loop.

Line 6 – As we iterate through each Cell we check the IsWalkable property. This will determine if we should draw a floor as in Line 9 or a wall as in Line 14

Lines 8 and 13 – We calculate the position of where we should draw the sprite by looking at the corresponding Cell’s X and Y properties and multiplying it by the variable we set in Line 3

Lines 9 and 14 – This is where we make the actual calls to the Draw method on our SpriteBatch and provide either the floor or wall texture and the position we calculated earlier

Your code should now look something like this

The Scaling Problem

If you run the game now you’ll see a problem (at least if you used my 64 x 64 pixel sprites).

Drawing The Map

Drawing The Map

The map is too large to fit on the screen. There are several more elegant ways to handle this using transformation matrices and camera classes but we’ll talk about that in a later tutorial. For now lets just scale all of our sprites down to 1/4 size, 16 x 16 pixels.

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 );
   }
   else
   {
      spriteBatch.Draw( _wall, position, null, null, null, 0.0f, new Vector2( scale, scale ), Color.White );
   }
}

spriteBatch.End();

Explanation

Line 4 – Introduced a new variable “scale” which we will use both when drawing sprites and calculating their positions

Line 7 – Refactored position calculation out of each if / else block and placed it directly in the foreach. Also use scale when calculating the position

Lines 10 and 14 – Used a different overload of the Draw method on SpriteBatch which allows us to set the scale of the sprite to draw

Now if we run the project we should see something better

Map Scaled by 1/4

Map Scaled by 1/4

Your map will look different because each time the program is ran, a different seed is generated for the random number generator used behind the scenes to place all the rooms and corridors. In a later tutorial will look at the different pseudo-random number generators provided with RogueSharp and how you can even create your own.

And here is the final code for creating our map in this tutorial

[edit] Part 2 of the tutorial is now live

Additional Fun

If you are looking for some extra things to try until I post the next tutorial some ideas are

  • Try creating your own class that implements IMapCreationStrategy and use it to create your own unique maps. Look at the source code for RogueSharp for a few simple examples.
  • Use more map features like doors and pits, not just floors and walls
  • Clean up the code. Extract some methods, rename classes, organize what we have. I purposely left it as close to the default MonoGame project as possible, but Game1 is not a very interesting name for a class and who needs all those TODOs
  • Try creating your own camera class that allows you to pan around the Map and zoom in or zoom out.
  • Classic Roguelikes don’t use sprites. Try making it look like the classics by implementing an ASCII display. (or just fake it with sprites of ASCII characters)