C/C++ week
Save up to 80 % on our C and C++ e-learning courses. Only this week!
Get up to 80 % extra points for free! More info

Lesson 4 - Sounds, Music, Keyboard, and Mouse Input in MonoGame

In the previous lesson, Drawing and Writing in MonoGame, we showed how to render sprites and text.

Today, we're going to take a look at music, sound effects, and processing keyboard input.

Loading Content

Just like the sprites from previous lesson, sounds and music must be loaded first as well. Sound effects are of the SoundEffect type, while music is of the Song type. We'll add two new fields into our game class:

private SoundEffect soundRow;
public Song music_zardax;

Let's move on to the LoadContent() method and load the sounds from the content:

soundRow = Content.Load<SoundEffect>(@"Sounds\sound_row");
music_zardax = Content.Load<Song>(@"Sounds\music_zardax");

Music

Music playback is provided by the MediaPlayer static class. We are mainly interested in the Play() method, which takes a Song object (the music we want to play) as its parameter. We'll call the Play() method at the end of the LoadContent() method, that is when the music is loaded.

MediaPlayer.Play(music_zardax);

Let's try :)

For further development, we'll create public ChangeMusic() method that will take a Song as a parameter. If this song isn't playing, it starts playing it. If the is already playing, the method does nothing. With this check we can avoid situations in which, for example, we'd come to a location where the music that is already playing is supposed to be played. Without the check, the music would start playing again from the beginning. The songs that are currently playing can be found in the Query.ActiveSong property. As you can see, the player has some kind of playlist, but we won't be using it in our course. The method will look like this:

public void ChangeMusic(Song song)
{
    // Isn't the same song already playing?
    if (MediaPlayer.Queue.ActiveSong != song)
        MediaPlayer.Play(song);
}

We'll change our code to play music using this method. In addition, to play the music repeatedly, we have to set the IsRepeating property. The bottom of the LoadContent() method now looks like this:

MediaPlayer.IsRepeating = true;
ChangeMusic(music_zardax);

To keep yourself sane, we recommend you to comment the ChangeMusic() method for development purposes, otherwise you may start hating the music after few days of programming and testing the game :)

Sound Effects

Furthermore, we have sound effects, which are pretty simple. Whenever we need to play these, we only have to call their Play() method. Let's try to play some sound effect at the end of the LoadContent() method:

soundRow.Play();

Let's try it.

Keyboard

Of course, the player will control the game in some way. MonoGame provides us with broad support for controllers, such as keyboard, mouse, joystick or gamepad. We'll now focus on the keyboard.

The Keyboard class is available for us to work with keyboard input. For us, the most important is the GetState() static method, which returns the currently pressed key states. The state instance then keeps the information about the particular key. The keyboard key state instance is of the KeyboardState type. We'll add a public variable of this type into our game class:

public KeyboardState keyboardState;

We already know that we'll be using the Update() method to check the key states. Let's move to it and save the current key state instance, right after the GamePad input processing for XBox:

keyboardState = Keyboard.GetState();

Now we have the current key state stored in keyboardState. The state instance has 3 useful methods:

  • IsKeyDown() returns whether a particular key is pressed.
  • IsKeyUp() returns whether a particular key is released.
  • GetPressedKeys() returns an array of all pressed keys.

Individual keys are represented by the Keys enumerated type. A Keys value (that is one particular key) is taken as a parameter by the first 2 methods. The last method then returns an array of Keys values.

Let's check whether the Enter key is pressed, and if so, then let's play our sound:

if (keyboardState.IsKeyDown(Keys.Enter))
    soundRow.Play();

Let's try.

We can see this solution has one drawback - the code is being executed constantly for as long as we hold the key pressed. There are dozens of sounds playing at the same time, giving us headache. Sometimes we may actually want the game to work like this, for example, if we held the gas pedal down, the car would keep accelerating, but as soon as we released it, the speed would drop. Sometimes, however, we want a key to be checked only once when it's pressed and held. Let's see how to achieve that. We'll declare another field that will keep the previous key state, right next to our key state field:

public KeyboardState keyboardState, previousKeyboardState;

We'll modify the logic inside the Update() method to store the previous key state inside our newly added previousKeyboardState field:

previousKeyboardState = keyboardState;
keyboardState = Keyboard.GetState();

Now we just need to check whether the Enter key was released the last time but now is pressed:

if (keyboardState.IsKeyDown(Keys.Enter) && previousKeyboardState.IsKeyUp(Keys.Enter))
    soundRow.Play();

Let's try again.

Since we'll want to use this behavior more often, without typing the same long condition everywhere, let's create a NewKey() method for this purpose. It'll take a Keys enumeration type value as a parameter to check whether the key is now held down, but wasn't the last time:

public bool NewKey(Keys key)
{
    return keyboardState.IsKeyDown(key) && previousKeyboardState.IsKeyUp(key);
}

The method is public because we'll use it later from outside our game class. Finally, let's modify the Enter key processing in Update():

if (NewKey(Keys.Enter))
    soundRow.Play();

As you can see, the code is now more clear.

Mouse

We still have some time. Even if we won't use the mouse input in our Tetris game, let's at least show how to use it. First, let's use the standard Windows cursor. The cursor is disabled in the game window by default. To enable it, we change the value of the IsMouseVisible property inside the Initialize() method:

IsMouseVisible = true;

Such cursor is quite ugly, so we'll keep it disabled :) As we've already said, our game won't use the mouse input, so we don't have any mouse cursor sprite available. If you want, you may draw one by yourself and add it in. But for now, we'll draw the "X" character instead, to represent the cursor.

Just like we use the Keyboard class for the keyboard input, MonoGame has also the Mouse class available for the mouse input. It works exactly the same. As with the keyboard, we have the MouseState type here for the mouse. We'll add a new mouseState field of this type into our game class:

public MouseState mouseState;

Inside the Update() method, we'll store the state the same way as we did with the keyboard state:

mouseState = Mouse.GetState();

Now, let's move to the Draw() method, where we'll draw the "cursor" and display the current cursor coordinates and the mouse button state.

Let's start with the cursor. We just need to use the X and Y properties of the mouse state instance which specify the cursor position. The cursor will be drawn as the last one, to be rendered above everything else (the spriteBatch.End() line must still be the last one, of course):

spriteBatch.TextWithShadow(fontCourierNew, "X", new Vector2(mouseState.X, mouseState.Y), Color.White);

Now let's draw the mouse position and button state text:

string text = String.Format("{0},{1} {2}", mouseState.X, mouseState.Y, mouseState.LeftButton);
spriteBatch.TextWithShadow(fontCourierNew, text, new Vector2(0, 700), Color.White);

Cursor and Rectangle Collision

Finally, we'll take a look at how to detect rectangle-point collisions. We already know that MonoGame has the Vector2 structure. It even provides another similar structures, from which we'll use Rectangle and Point. The difference between them is that the point has integer coordinates and lacks methods such as vector sum. Nevertheless, it's very common to use the vector type all the time in MonoGame projects, since it's used by internal MonoGame methods, such as rendering methods.

Let's create a rectangle and set it to cover the robot in the background approximately. After clicking the robot, we'll display the "Don't click me" text :) Now, of course, we're just messing around. In practice we'd be using some robot object. Of course, we'll eventually use objects, such as block, in our project. Let's add the following fields to the class:

private Rectangle robotRectangle;

Inside the Initialize() method, we'll set the rectangle approximately to the robot's coordinates, settings its top-left corner and its dimensions:

robotRectangle = new Rectangle(775, 345, 115, 245);

The specific coordinates can be acquired by opening the background in MS Paint or any other editor and selecting the area, which results in the editor showing the selection coordinates and dimensions. Now let's move back to the Draw() method and add the text rendering:

if (robotRectangle.Contains(new Point(mouseState.X, mouseState.Y))
&& (mouseState.LeftButton == ButtonState.Pressed))
    spriteBatch.TextWithShadow(fontBlox, "Don't click me", new Vector2(600, 220), Color.Lime);

The button state is compared to the ButtonState enumerated type value, which can be Pressed or Released. Ideally, this check should be in the Update() method, but it doesn't matter for our example. If we wanted more complex behavior, we'd create a previousMouseState field, just like for the keyboard.

Robotris, a sample game made in MonoGame

In the next lesson, Dividing a MonoGame Project into Components, we'll take a look at how to divide our game project into several game components. The whole project with today's solution is available to download below.


 

Download

Downloaded 1x (17.75 MB)
Application includes source codes

 

Previous article
Drawing and Writing in MonoGame
All articles in this section
Tetris From Scratch
Article has been written for you by David Capka
Avatar
Do you like this article?
No one has rated this quite yet, be the first one!
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university The author learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities (3)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!