Generated by DocFX

Snake Tutorial Part 2: Sprites and Player Input

In this chapter, we'll go over creating a Sprite and polling player input to control its position.

In exploring sprites and input, we'll also be covering MonoGame's Content Pipeline and MGCB tool, and Ladybug's KeyboardMonitor and ResourceCatalog classes.

At the end of this chapter, your game application will produce a sprite that can be moved across the screen with the arrow keys.

Prerequisites

To complete the steps outlined in this chapter, you will need to have completed the steps in the previous chapter

In addition, this chapter will be introducing the MonoGame Content Builder, which requires the MGCB Tool. For information on installing the MGCB tool, see the Installation article.

For information on using the MGCB editor, click here

Preface: What is Content?

Up until now, our Game instance has not had to render anything to the screen; all output so far has been to the console.

Now, as we start to work with graphical assets, we'll need to review Ladybug's concept of "Content".

In summary, Content is any external resource loaded into a game and used as an asset. These resources are usually image or audio resources, but can also involve data resources like JSON and XML.

The MGCB Editor

MonoGame/XNA, which is the underlying framework for Ladybug, handles Content through a ContentManager class within the game code, and through the MGCB editor outside the code. If you don't have experience using the MGCB editor, it is recommended you review this article which details its basic usage.

Usage of the MGCB editor is essentially identical with Ladybug. The only addition to the process Ladybug adds is the ResourceCatalog class, which is a wrapper over a ContentManager that adds extra convenience.

Step 1: Getting a Texture for our Sprite

To draw a Sprite to the screen, we'll need to have an image file to load its texture from.

We'll be needing a texture for the snake's body segments, ideally something symmetrical and uniform. Some sample assets will be provided for this tutorial, but feel free to create your own if you wish.

You can download the sample assets for this tutorial here.

Adding the Texture to the Game

Once you have a texture created or downloaded, copy the file into core/Content/image/snake-body.png. Although the file is now in the Content folder within our game's structure, it is not quite yet accessible to our game code. To make snake-body.png available in our game, we'll need to add it using the MGCB Editor.

Using the MGCB Editor

Take a look at this article that describes the process for using the MGCB Editor. You will use this editor to open core/Content/Content.mgcb and add the core/Content/image/snake-body.png file using its interface.

Once you've added the snake-body.png file in the tool, save it and close the MGCB Editor -- this is the only time we'll need to use it this chapter.

Clarification: Textures vs. Sprites

In casual conversation, the words texture and sprite are sometimes used interchangeably. However, in Ladybug they are quite different, and the distinction is important:

  • Texture: An image file that is loaded into a Scene as a resource.
  • Sprite: An in-game visual representation of a texture, rendered at a particular location, size, scale, and rotation. Contains a reference to a texture, plus transform information about how/where/what portion that texture will be rendered.

It is possible (and sometimes appropriate) to work directly with textures in Ladybug, but for this project we're definitely interested in the added convenience of a Sprite.

Step 2: Creating and Drawing our Sprite

Now that we have a texture we want to use for the body segments of our snake and we've successfully processed it through the MGCB editor, let's work on creating a Sprite with it and drawing it to the screen.

We're going to have to add some new items to MainScene.cs to facilitate drawing our Sprite. For now, edit your MainScene.cs file and add the new items from the following sample:

// core/scene/MainScene.cs

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; // <-- 1: New using directive
using Ladybug;
using Ladybug.Graphics; // <-- 2: New using directive

public class MainScene : Scene
{
	private Sprite _snakeSprite; // <-- 3: New member variable

	protected override void Initialize()
	{
		Console.WriteLine("Main Scene Initialized!");
	}

	// 4: New method
	protected override void LoadContent()
	{
		Texture2D snakeTexture = ResourceCatalog.LoadResource<Texture2D>("snake", "image/snake-body");
		_snakeSprite = new Sprite(snakeTexture);
	}

	protected override void Update(GameTime gameTime)
	{
		// Our update code will go here
	}

	protected override void Draw(GameTime gameTime)
	{
		// 5: New draw logic
		SpriteBatch.Begin();
		_snakeSprite.Draw(SpriteBatch);
		SpriteBatch.End();
	}
}

MainScene.cs New Updates Summary : Drawing

We added quite a few new items to our MainScene.cs file in the above sample. Let's go over the additions one at a time.

1-2: New Using Directives

We added MonoGame.Xna.Framework and Ladybug.Graphics to our using directives. This is so that we can use Texture2D and Sprite, respectively.

3: New Member Variable

We created a new _snakeSprite variable of type Sprite. This will be the sprite that we draw to the screen.

4 New Override Method: LoadContent

We've added a new override method: LoadContent(), which will be called when the scene is being set up.

In this new method, we load our snake-body.png file into a ResourceCatalog and assign the resulting resource to a local variable snakeTexture. ResourceCatalog's first argument contains a simple name we can use to reference the snake-body.png texture later, while the second argument contains the file path to snake-body.png relative to the core/Content folder, without the file extension.

Now that we have snake-body.png loaded into the texture snakeTexture, we use that to create a Sprite and assign it to _snakeSprite.

6: New Draw Logic

Now that we have something to draw, we can do something with our Draw() method.

First, we have to start the draw process for the current frame by calling SpriteBatch.Begin(). Similarly, we'll need to tell the SpriteBatch when we're done drawing, so we call SpriteBatch.End() at the end of the Draw() method.

Between SpriteBatch.Begin() and SpriteBatch.End(), we actually draw our Sprite by calling _snakeSprite.Draw().

Success - Our First Sprite!

With the above adjustments to MainScene.cs, running the game should result in our snake-body.png texture being drawn to the screen. The size and position may not be what we expected, but it's there and that's a great starting point. We will adjust its size and position easily in future steps.

Step 3: Bringing In User Input

Drawing to the screen is a big step -- a vital part of creating a game. However, allowing a user to control what is drawn to the screen is equally important, if not more so.

To get the user's input, we'll be using Ladybug's KeyboardMonitor, which as its name implies, monitors the keyboard for input.

To do so, we'll need to make a few more adjustments to our MainScene.cs file:

// core/scene/MainScene.cs

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input; // <-- 1: New using directive
using Ladybug;
using Ladybug.Graphics; 
using Ladybug.Input; // <-- 2: New using directive

public class MainScene : Scene
{
	private Sprite _snakeSprite; 
	private KeyboardMonitor _keyboard; // <-- 3: New member variable 

	protected override void Initialize()
	{
		Console.WriteLine("Main Scene Initialized!");
		_keyboard = new KeyboardMonitor(); // <-- 4: New initialization item
	}

	protected override void LoadContent()
	{
		Texture2D snakeTexture = ResourceCatalog.LoadResource<Texture2D>("snake", "image/snake-body");
		_snakeSprite = new Sprite(snakeTexture);
	}

	protected override void Update(GameTime gameTime)
	{
		// 5: New update logic
		_keyboard.BeginUpdate(Keyboard.GetState());

		if (_keyboard.CheckButton(Keys.Space, InputState.Released))
		{
			Console.WriteLine("Space Pressed!");
		}

		_keyboard.EndUpdate();
	}

	protected override void Draw(GameTime gameTime)
	{
		SpriteBatch.Begin();
		_snakeSprite.Draw(SpriteBatch);
		SpriteBatch.End();
	}
}

MainScene.cs New Updates Summary : User Input

We've added yet another series of new items and updates to MainScene.cs. Let's go over each change now:

1-2: New Using Directives

We've added two new using directives: Microsoft.Xna.Framework.Input and Ladybug.Input. These allow us to use Keyboard.GetState() and KeyboardMonitor, respectively.

3-4: New Member Variable and Initialization

We've added a new private member variable to store our KeyboardMonitor: _keyboard. We then set this variable to its initial value in Initialize().

5: New Update Logic

Now that we have our KeyboardMonitor initialized, we can poll for user input. Since this is something we'll need to do every frame, Update() is the perfect place for this.

Similar to how SpriteBatch works, we need to tell our KeyboardMonitor when we want to start checking for input, and when we've finished checking for input. This is handled by _keyboard.BeginUpdate() and _keyboardMonitor.EndUpdate().

Finally, to show that our game can see the user's input, we've added an input check on the spacebar that will print "Space Pressed!" to the console every time the spacebar is pressed, then released.

At this point, if you run the game, you will still see the sprite we set up in the previous step, and now if you press and release the spacebar, you should see "Space Pressed" in the console.

It may not look like it quite yet, but we're very close to getting a handle on the most vital parts of making a game!

Step 4: Putting it Together

Now that we've handled drawing to the screen and gathering input, let's combine them!

Lucky for us, this is very easy. Replace the contents of MainScene.cs's Update() method with the following:

protected override void Update(GameTime gameTime)
{
	_keyboard.BeginUpdate(Keyboard.GetState());

	if (_keyboard.CheckButton(Keys.Up, InputState.Down))
	{
		_snakeSprite.Transform.Move(0, -10);
	}

	if (_keyboard.CheckButton(Keys.Left, InputState.Down))
	{
		_snakeSprite.Transform.Move(-10, 0);
	}

	if (_keyboard.CheckButton(Keys.Right, InputState.Down))
	{
		_snakeSprite.Transform.Move(10, 0);
	}

	if (_keyboard.CheckButton(Keys.Down, InputState.Down))
	{
		_snakeSprite.Transform.Move(0, 10);
	}

	_keyboard.EndUpdate();
}

Now, when you press the arrow keys, the sprite will move ten pixels per frame in the direction pressed. InputState.Down means "every frame the button is down", so holding the arrow keys will keep the Sprite moving.

Conclusion

In this chapter, we reviewed the concept of content, how Ladybug and MonoGame handle importing and processing content, and how to load and use content in our game. In addition, we covered basic user input, and illustrated how to use input to affect what is drawn to the screen.

With our MainScene now drawing and updating objects within our game, we've got the foundations settled for our snake game.

Next Steps

Now that we've got a basic game foundation including sprite drawing and user input, it's time to make the snake that the player will be controlling!

When you're ready for the next step, head on over to the next chapter where we'll set up our Snake class and get a controllable snake working.

Complete code for this chapter can be found here.