Morgemil Part 12 – I can show you the world

Some years ago I played around with XNA. That never really went anywhere and XNA is now dead. So I’m going to be using Monogame.

I’ve created a new console project that I’ve named “View” and I’ve added the Monogame DX Nuget package. I’ve changed the output type to “Windows Application”.
monogame_2

I’m on new ground so I’m following this tutorial using F# and Monogame. This will get me started. My requirements will diverge from the tutorial quickly though.

The first piece is to create the minimum “Game” necessary to open a window

namespace Morgemil.View
 
open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Graphics
 
type GameView() as this =
  inherit Game()
  let graphics = new GraphicsDeviceManager(this)
 
  override this.Initialize() =
    base.Initialize()
    base.Window.Title <- "Morgemil"
 
  override this.LoadContent() = ()
  override this.Update(gameTime) = ()
  override this.Draw(gameTime) = this.GraphicsDevice.Clear Color.CornflowerBlue

monogame_3
There you go! The entire game is done! Time to label it as early access, make more false promises than a politician, and rake in the cash.

I’ll do that next time. Right now let’s actually make a game. And every game needs a way to draw things. This first prototype will be super basic. No need for actual textures, just draw some colors.

type GameView() as this =
  inherit Game()
  let graphics = new GraphicsDeviceManager(this)
  let mutable spriteBatch = Unchecked.defaultof<SpriteBatch> //null
  let mutable spriteTexture = Unchecked.defaultof<Texture2D> //null
 
  override this.Initialize() =
    base.Initialize()
    base.Window.Title <- "Morgemil"
 
  override this.LoadContent() =
    spriteBatch <- new SpriteBatch(this.GraphicsDevice)
    spriteTexture <- new Texture2D(this.GraphicsDevice, 1, 1) //Only need 1 color
    spriteTexture.SetData([| Color.White |]) //255,255,255
 
  override this.Update(gameTime) = ()
  override this.Draw(gameTime) =
    this.GraphicsDevice.Clear Color.Black
    spriteBatch.Begin()
    //Draw things here
    spriteBatch.End()

The first thing to draw will be the dungeon, which we already have. Colors for the map tiles will be chosen very simply: can the player walk there?

  let ChooseColor(tileDef : Morgemil.Map.TileDefinition) =
    match tileDef.BlocksMovement with
    | true -> Color.Black
    | false -> Color.White

Each tile will take up two pixels square to start with. Notice that the argument “ChooseColor tileDef” in spriteBatch.Draw is not the actual color to draw. It is a mask to be applied to the texture. The texture is pure white (255,255,255). The mask is some color such as red (255,0,0). By applying the mask to the white color: red is chosen (255 & 255, 255 & 0, 255 & 0) = (255,0,0)

  let tileSize = 2
 
  let DrawTile(pos : Morgemil.Math.Vector2i, tileDef : Morgemil.Map.TileDefinition) =
    let drawArea = Rectangle(pos.X * tileSize, pos.Y * tileSize, tileSize, tileSize)
    spriteBatch.Draw(spriteTexture, drawArea, ChooseColor tileDef)

Load a ready made dungeon chunk

  let chunk = Morgemil.Map.DungeonGeneration.Generate 216798

Draw it to the screen

  override this.Draw(gameTime) =
    this.GraphicsDevice.Clear Color.Black
    spriteBatch.Begin()
    chunk.TileCoordinates |> Seq.iter (DrawTile)
    spriteBatch.End()

Make sure that the entire map is shown on the screen. By modifying the buffer on the graphics device manager earlier.

  let graphics = new GraphicsDeviceManager(this)
  do graphics.PreferredBackBufferWidth <- chunk.Area.Width * tileSize
  do graphics.PreferredBackBufferHeight <- chunk.Area.Height * tileSize
  do graphics.ApplyChanges()

Here is the result
monogame_4
And here is the full code. Cleaning this up and accepting user input could very well be a good choice for next week’s post.

namespace Morgemil.View
 
open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Graphics
 
type GameView() as this =
  inherit Game()
  let chunk = Morgemil.Map.DungeonGeneration.Generate 216798
  let tileSize = 2
  let graphics = new GraphicsDeviceManager(this)
  do graphics.PreferredBackBufferWidth <- chunk.Area.Width * tileSize
  do graphics.PreferredBackBufferHeight <- chunk.Area.Height * tileSize
  do graphics.ApplyChanges()
  let mutable spriteBatch = Unchecked.defaultof<SpriteBatch> //null
  let mutable spriteTexture = Unchecked.defaultof<Texture2D> //null
 
  let ChooseColor(tileDef : Morgemil.Map.TileDefinition) =
    match tileDef.BlocksMovement with
    | true -> Color.Black
    | false -> Color.White
 
  let DrawTile(pos : Morgemil.Math.Vector2i, tileDef : Morgemil.Map.TileDefinition) =
    let drawArea = Rectangle(pos.X * tileSize, pos.Y * tileSize, tileSize, tileSize)
    spriteBatch.Draw(spriteTexture, drawArea, ChooseColor tileDef)
 
  override this.Initialize() =
    base.Initialize()
    base.Window.Title <- "Morgemil"
 
  override this.LoadContent() =
    spriteBatch <- new SpriteBatch(this.GraphicsDevice)
    spriteTexture <- new Texture2D(this.GraphicsDevice, 1, 1) //Only need 1 color
    spriteTexture.SetData([| Color.White |]) //255,255,255
 
  override this.Update(gameTime) = ()
  override this.Draw(gameTime) =
    this.GraphicsDevice.Clear Color.Black
    spriteBatch.Begin()
    chunk.TileCoordinates |> Seq.iter (DrawTile)
    spriteBatch.End()