Part 1 - The Player Class
In this walkthrough, create a new Player class and use it to allow the player to control a character on the screen.
Defining the Player Class
Define a Player class that will represent the game component that the user will control.
Setup
There is some essential setup necessary when defining a new class.
- Create a new file named Player.cs in the "src" folder
- At the top of the file, add two
usingstatements that include the proper MonoGame tools:using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; - Under the
usingstatements, create anamespacewrapper to ensure that thePlayerclass will be included in the same namespace as theArcadeFlyerGameclass:namespace ArcadeFlyer2D { } - In the body of the
namespacewrapper, define a new class namedPlayer:class Player { }
Now the Player has been defined! It does not do much of anything yet, but the basic setup is complete.
Fields
The Player class should hold some sort of data so that a Player object can actually be displayed on the screen.
One thing the Player will need is a reference to the game that contains it. This can facilitate communication between the player and the overarching game structure. In the body of the Player class, add a field named root with a type of ArcadeFlyerGame:
private ArcadeFlyerGame root;
Another thing to track is the player's position on the screen. This can be stored in a Vector2 structure in MonoGame. This structure simply holds two float values; an X and a Y. These can be used to represent the player's position. Add the field in the body of the Player class:
private Vector2 position;
Next, the player should have a Texture2D image. Add this field under the position field:
private Texture2D spriteImage;
Another important thing to store will be the width of the sprite as it appears on the screen. The sprite may need to resize the image to make it fit. Add this field under the spriteImage field:
private float spriteWidth;
Now the Player class actually has some data associate with it!
Constructor and Content Loading
Next, add some initialization to the Player class that will build the player object.
- Define a constructor for the
Playerclass that takes in arootparameter of typeArcadeFlyerGame, and apositionparameter of typeVector2:public Player(ArcadeFlyerGame root, Vector2 position) { } - In the body of the constructor, initialize the
rootandpositionfields withthis.rootandthis.position:this.root = root; this.position = position; - Under those statements, set the
spriteWidthfield to128.0f:this.spriteWidth = 128.0f; - Under that, call a method named
LoadContentthat has yet to exist:LoadContent(); - Time to define the method! In the
Playerclass, define a new method namedLoadContentwith no parameters and avoidreturn type:public void LoadContent() { } - In the body of the
LoadContentmethod, use therootfield to load theTexture2D"MainChar" asset - Set the
spriteImageproperty to the loadedTexture2D:this.spriteImage = root.Content.Load<Texture2D>("MainChar");
Now, the class has a way to properly initialize a new Player object!
Properties
Next, there are a couple of calculated properties that will be necessary for the player. Properties in C# behave like fields, but they actually have special accessor methods that can do some interesting things.
SpriteHeight
The SpriteHeight property will calculate the height of the sprite on the screen based on the height of the actual image asset, and the resizing scale from the width.
- In the
Playerclass, create afloatproperty namedSpriteHeight:public float SpriteHeight { } - In the body of the
SpriteHeightproperty, create agetaccessor:get { } - The code in the body of the
getaccessor will determine how the property returns its value; first, define a variable to calculate the scale:float scale = spriteWidth / spriteImage.Width; - Return the adjusted height value:
return spriteImage.Height * scale;
That's it for the SpriteHeight property! This property will only be gettable; it will not be settable.
PositionRectangle
The position rectangle for the player represents the rectangle where they will appear on the screen. This can also be calculated based on the position field, the spriteWidth field, and the SpriteHeight property. Note that the Rectangle object must be created using int values, so the values from the Player class will have to be casted.
- In the
Playerclass, create aRectangleproperty namedPositionRectangle:public Rectangle PositionRectangle { } - In the body of the
PositionRectangleproperty, create agetaccessor:get { } - In the body of the
getaccessor, return a newRectanglewith the properly casted x-coordinate, y-coordinate, width, and height (based on thePlayervalues):return new Rectangle((int)position.X, (int)position.Y, (int)spriteWidth, (int)SpriteHeight);
That's it for the PositionRectangle property! Like the SpriteHeight property, this property will only be gettable; it will not be settable.
Updating and Drawing
Now the Player class has everything it needs to exist in the game. It's time to add functionality to make that happen!
- In the
Playerclass, define a new method namedUpdatewith avoidreturn type and aGameTimeparameter:public void Update(GameTime gameTime) { } - Keep the body of the
Updatemethod empty for now - Under the
Updatemethod, define a new method namedDrawwith avoidreturn type, aGameTimeparameter, and aSpriteBatchparameter:public void Draw(GameTime gameTime, SpriteBatch spriteBatch) - In the body of the
Drawmethod, use thespriteBatchparameter to draw thespriteImagewith thePositionRectangleand a white color mask:spriteBatch.Draw(spriteImage, PositionRectangle, Color.White);
That's it! The Player class can now update and draw a player.
Player Code
At this point, the code in the Player.cs file should look like this:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace ArcadeFlyer2D
{
class Player
{
private ArcadeFlyerGame root;
private Vector2 position;
private Texture2D spriteImage;
private float spriteWidth;
public float SpriteHeight
{
get
{
float scale = spriteWidth / spriteImage.Width;
return spriteImage.Height * scale;
}
}
public Rectangle PositionRectangle
{
get
{
return new Rectangle((int)position.X, (int)position.Y, (int)spriteWidth, (int)SpriteHeight);
}
}
public Player(ArcadeFlyerGame root, Vector2 position)
{
this.root = root;
this.spriteWidth = 128.0f;
this.position = position;
LoadContent();
}
public void LoadContent()
{
this.spriteImage = root.Content.Load<Texture2D>("MainChar");
}
public void Update(GameTime gameTime)
{
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
spriteBatch.Draw(spriteImage, PositionRectangle, Color.White);
}
}
}
Using the Player Class
Now that the Player class has been defined, it's time to start using it in the ArcadeFlyerGame.cs file!
- Remove the
Texture2D playerImagefield from theArcadeFlyerGameclass, and add aPlayerfield:private Player player; - At the end of the
ArcadeFlyerGameconstructor, initialize theplayerfield. Set it to a newPlayerobject, passing inthisfor the root, and a(0, 0)vector for the position:player = new Player(this, new Vector2(0.0f, 0.0f)); - In the
LoadContentmethod, remove the statement that loads the"MainChar"image. This happens in thePlayerclass now, so it is not necessary here - In the
ArcadeFlyerGameclassUpdatemethod, call thePlayerclassUpdatemethod:player.Update(gameTime); - In the
ArcadeFlyerGameclassDrawmethod, remove the current drawing code betweenspriteBatch.Begin();andspriteBatch.End(); - Replace the previous drawing code with a call to the
PlayerclassDrawmethod, passing in thegameTimeandspriteBatch:player.Draw(gameTime, spriteBatch);
Run the game to verify that the player sprite appears on the screen! It may seem like this was a lot of work for very little payoff, but creating the Player class will help tremendously with the organization of the code as development continues.
Handling Input
Currently, the Update method on the Player class does nothing. It's time fix that and make the Player object controllable!
Use the KeyboardState structure to make this happen. This allows developers to detect which keys are pressed. For more information, check out this page. Make the changes in the Player.cs file.
- Add a new
floatproperty to thePlayerclass namedmovementSpeed, and set it equal to4.0f:private float movementSpeed = 4.0f; - Define a new method on the
Playerclass. The method should have:- An access modifier of
private - A return type of
void - A name of
HandleInput - A parameter named
currentKeyboardStateof typeKeyboardStateprivate void HandleInput(KeyboardState currentKeyboardState) { }
- An access modifier of
- In the body of the
HandleInputmethod, use thecurrentKeyboardState.IsKeyDownmethod to check if theKeys.Upkey is pressed. Store the result in aboolvariable:bool upKeyPressed = currentKeyboardState.IsKeyDown(Keys.Up); - Create an
ifstatement withupKeyPressedas the condition:if (upKeyPressed) { } - In the body of the
ifstatement, since the up key is pressed, decrement theYvalue of the position to make the player move up:position.Y -= movementSpeed; - Repeat the steps above for the Down, Left, and Right keys. Make sure the player moves in the proper direction for each key press
- In the body of the
Updatemethod, useKeyboard.GetState()to get the currentKeyboardStatefor the game and store it in a variable:KeyboardState currentKeyboardState = Keyboard.GetState(); - Under that line, call the
HandleInputmethod, passing in thecurrentKeyboardStatevariable:HandleInput(currentKeyboardState);
The HandleInput Method
The code for the HandleInput method should look something like this:
private void HandleInput(KeyboardState currentKeyboardState)
{
bool upKeyPressed = currentKeyboardState.IsKeyDown(Keys.Up);
bool downKeyPressed = currentKeyboardState.IsKeyDown(Keys.Down);
bool leftKeyPressed = currentKeyboardState.IsKeyDown(Keys.Left);
bool rightKeyPressed = currentKeyboardState.IsKeyDown(Keys.Right);
if (upKeyPressed)
{
position.Y -= movementSpeed;
}
if (downKeyPressed)
{
position.Y += movementSpeed;
}
if (leftKeyPressed)
{
position.X -= movementSpeed;
}
if (rightKeyPressed)
{
position.X += movementSpeed;
}
}
The Update method should look something like this:
public void Update(GameTime gameTime)
{
KeyboardState currentKeyboardState = Keyboard.GetState();
HandleInput(currentKeyboardState);
}
Run the program and test out the game! The main character should move based on they keyboard state!
Final Code
The final code for this walkthrough is available on GitHub.