Tuesday, May 31, 2011

XNA Game Design: Mouse Input (part 1)

Implementation for cursor input is a bit more complex, but still very similar to the keyboard's. If the keyboard input is just a case of checking boolean values of keyboard keys, mouse input is more like Cartesian graphing. Just like the keyboard, the mouse has it's own status property called MouseStateMouseState contains X and Y coordinates that are relative to the upper-left origin of the game window.

If you've played with the empty game window before, you might have noticed the mouse cursor disappears when moved onto the game window. This is a default property of XNA Windows games, and can be changed in the Game1 class with the line IsMouseVisible = true;.still, this does not provide us with a custom cursor graphic, and the mouse will still not be able to interact with our game.

Like the Keyboard class, XNA provides a Mouse class which has it's own GetState(method. However, the X and Y integers contained in the mousestate returned by Mouse.GetState() will contain negative values if they are above or to the left of the origin, and will return values greater than the width and height of the game window if they exceed the Game Window's boundaries.

So why is this bad?

Lets say you have a game where the character is the mouse cursor, and is being chased by angry monsters that bounce around the screen, and the goal of the game is to last as long as possible. If this problem isn't fixed, the player could move their cursor off the screen, and walk away. Effectively the player's coordinates would be outside the confines the game window, and the enemies would never reach the player.
Your game should not be broken by the player moving the cursor off the screen! As this cheesy photoshop shows, negative cursor coordinates (as well as coordinate beyond the confines of the game window) beat the game's logic, and the player can successfully cheat. This is bad!

The Cursor class we will be creating is different from the XNA Mouse class for two reasons. Of course the XNA Mouse class is protected and immutable, but more importantly it contains all the information about the actual onscreen mouse relative to our game window. We need this metadata to handle certain interactions with the Windows shell.

As such, the update and draw methods we will be implementing will focus more on the following two aspects:
  • Coordinates that are conducive to our game window.
  • A custom cursor texture that can be easily changed depending on the desired scenario.

Alright, lets get this general framework going.
static class Cursor
    //Current state of the Mouse
    public static MouseState State = Mouse.GetState();
    //Previous state of the Mouse
    public static MouseState Prevstate;
    //Graphic used to represent the cursor
    public static Texture2D Texture;
    //value representing mouse coordinates
    public static Vector2 Pos;

    public static void Update()
        PrevState = State;
        State = Mouse.GetState();
        Pos = new Vector2((float) state.X, (float) state.Y);

    public static void Draw(Spritebatch spritebatch)
        //We'll write this code later

Look familiar? It is essentially the same method as the keyboard. Still, this doesn't address the aforementioned problem, nor will it allow us to draw our special cursor graphic.

As you can see, we also added two new properties to the class These are Pos and Texture.

Again, these properties are public and static because we will not be making instances of Cursor, and because we want their access to be commonplace throughout the game. Those two types however, Texture2D and Vector2, are XNA framework types. Texture2D represents a two-dimensional image (of which we can assign a raster graphic, which we will be do in the next lesson) and Vector2; a set of two float values that can be used to identify a vector, or in our case, the Cartesian coordinates of the mouse. The reason we will be using Vector2 rather than Point (similar to a vector2, but composed of two integers instead of two floats) to represent our mouse cursor will be explained later, but you are right in thinking that such a datatype would be more conducive to representing the Cartesian coordinates of our cursor. You were thinking that, right?

Texture and Pos will both be implemented in the next lesson, and we will also tie-in our completed Input class into our main loop, giving our game the input it needs.

Yesterday's Homework Solution: In order to create a method that can tell the difference between holding a button down and pushing a button, we would need to check the previous state of the keyboard using our PrevState  property. Like before, we want the method to be static so it's implementation is ubiquitous.

    public static bool IsKeyTrigger(Keys k) //returns true if key is triggered at one moment. returns false if held.
        if ((State.IsKeyDown(k)) && !(PrevState.IsKeyDown(k))) return true;
        else return false;

Because State would return true so long as the key is pressed down, PrevState would only return false at the first moment the key is pressed. The next time Input.Update() is run, this method will begin returning false until the key is released and PrevState reset to false.

Homework: This lesson introduced three new XNA classes; Texture2DVector2 and Point. All three are quite useful, and we will be seeing them many more times in the future. Another class we will see frequently is the Rectangle class. Rectangle object, as you can imagine, represents a 2D space with an (X, Y) origin, a width and height.

Given a Rectangle r, return a boolean value indicating whether the cursor is within the confines of r. For reference, here are the members of rectangle, vector2 and point.

No comments:

Post a Comment