XNA Mouse Input and Handling

This is short tutorial on how I’m handling some of the mouse commands. I’m dumping it here in case some one find it useful:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

static public class InputHandler
{

}

Notice the libraries that are included. Only the ones that are strictly necessary. The reason it’s static and public, it’s to allow all other parts of the program access to it. This is handy because it will allow us to control menu selections, and gameplay at the same time without having to initialize the class everywhere. This are the initial members of the class:

static public class InputHandler
{
//Texture
static Texture2D nullTex;
static int spacing = 7;

//GetMouse
static Vector2 mouseRecOrigin = Vector2.Zero;

static MouseState prevMouseState = Mouse.GetState();
static public MouseState MouseState
{ get { return prevMouseState; } }

static Rectangle mouseRec = Rectangle.Empty;
static public Rectangle MouseRec
{ get { return mouseRec; } }

static bool isLeftClick = false;
static public bool LeftClick
{ get { return isLeftClick; } }

static bool isRightClick = false;
static public bool RightClick
{ get { return isRightClick; } }

}

Tons of variables here, but as we can see, these are all private, and only a few have accessor functions. That’s because we don’t want any other class to change the information that it’s being passed around. The other reason is because we’re going to use some private variables to track information that will become useful a bit later. I have also included a texture here. This is because we want to be able to draw a rectangle if the left button is pressed and dragged. The texture is a 1×1 transparent png. The first function is not a constructor. There is no need because we want to get the mouse information as soon as we start the program. For this reason, we initialize the state of the mouse as soon as it launches. That defeats the whole purpose of having a constructor in this case. All other information will be processed as it is received. The first function is Update() function:

static public void Update()
{
MouseHandler();
}

Tada! Yes.. tis’ that bad. The problem here is that our class is a general input handling class. That means that we don’t want to limit ourselves to just mouse input (even though it is what we’re doing initially). Now for the real MouseHandler(). This is a rather large function. So I will break it down into little pieces: The initialization:

static private void MouseHandler()
{
  MouseState mouseState = Mouse.GetState();
  isLeftClick = false;
  isRightClick = false;

Get the current state of the mouse is pretty obvious. The little less obvious is isLeftClick and isRightClick. We want to make this two false as soon as we enter the function. We’re only going to turn them on if there is an action in progress. If there is no action, they will drop all the way down as false. This are the single right and left click variables. That’s important to know.

  //the left button has been pressed.
  //Create the rectangle
  if (prevMouseState.LeftButton == ButtonState.Released && mouseState.LeftButton == ButtonState.Pressed)
  {
    mouseRec = new Rectangle((int)mouseState.X, (int)mouseState.Y, 0, 0);
    mouseRecOrigin = new Vector2(mouseState.X, mouseState.Y);
  }

the first thing we do is compare the previous value to the new value. If it was not pressed before, and it’s pressed now, we create a rectangle. The X, Y coordinates are set to the position of the mouse. We also set the MouseRecOrigin values to X, Y. This is a private variable which is used to keep track of where the rectangle was started. This will allow us to draw rectangles in every directions.

  //the left button is on hold.
  //Update rec with appropiate dimensions
  if (prevMouseState.LeftButton == ButtonState.Pressed && mouseState.LeftButton == ButtonState.Pressed)
  {
    //Capture the origin and width
    if (mouseState.X > mouseRecOrigin.X)
      mouseRec.Width = mouseState.X - mouseRec.X;
    else
    {
      mouseRec.Width = (int)mouseRecOrigin.X - mouseState.X;
      mouseRec.X = mouseState.X;
    }

    //capture the origin and height
    if (mouseState.Y > mouseRecOrigin.Y)
      mouseRec.Height = mouseState.Y - mouseRec.Y;
    else
    {
      mouseRec.Height = (int)mouseRecOrigin.Y - mouseState.Y;
      mouseRec.Y = mouseState.Y;
    }
  }

This chunk is the one that we’ll probably care the most. I had seen a tutorial here which is a great resource. The problem I found with their way of doing things, is that it didn’t allow for the mouse to be dragged in a top-right diagonal. So this was my solution to their issue. Not the most elegant, but it certainly gets the job done.

First we make sure that the mouse was previously pressed and it continues being pressed. This is the only time that we want to draw/create a rectangle. Now, if the current position X of the mouse is greater than the origin X, then that means we’re dragging the mouse to the right. In this case, the rectangle’s origin should be X. In this case we only care about the width of the rectangle which we calculate by subtracting the current position from the old position.

However, if the current position X is less than the origin X, then we’re dragging the mouse to the left. Because of this, we need to not only find the new width of the rectangle, but also find the new origin. We find a new origin because we want to keep the rectangle straight with non-negative width. In this case, we subtract the position X of the mouse from the origin to calculate the width.

Additionally, we store the current location of the mouse to the origin of the rectangle. The update done for Y is identical, except for the change in the variable name from X to Y. The only things left to do are capture the single clicks.

  //the left button has been released
  if (mouseState.LeftButton == ButtonState.Released &&
      prevMouseState.LeftButton == ButtonState.Pressed &&
      mouseRec.Width == 0 && mouseRec.Height == 0)
  {
    isLeftClick = true;
  }

  //Check for right mouse click (No drag allowed)
  if (mouseState.RightButton == ButtonState.Released && prevMouseState.RightButton == ButtonState.Pressed)
    isRightClick = true;

very simple code here. In the case of the left click we want to check the previous status of pressed to the new status of released. That means it’s been let go. However because we do care about the drag characteristics of the left click, we do one more check against if the rectangle has a width or height. This will ensure that it’s a single click or a drag. For the right click, we’re just going to check the status. Finally the clean up code:

  //Clear the rectangle
  if (mouseRec != Rectangle.Empty &&
      mouseState.LeftButton == ButtonState.Released &&
      prevMouseState.LeftButton == ButtonState.Released)
    mouseRec = Rectangle.Empty;

  //update the mouse state
  prevMouseState = mouseState;
}//End input handler

We have three checks here. First we check if the rectangle is empty. If it’s empty, we just keep going. No need to set it again. If there is a rectangle, then we check if the current status of the mouse is pressed. If it’s not, it’s because the user might have just released. If that is the case, we want to leave the rectangle alone, so whatever is next in line can use the rectangle as needed.

If the previous state was released, then it means that it’s been released for two frames: Clear the rectangle. Finally we just update the previous mouse state. Now, for the drawing! First things first: Load the texture:

static public void Load(ContentManager theContentManager)
{
  if (nullTex == null)
    nullTex = theContentManager.Load(@"gfx/1x1");
}

Simple enough. I do the null check in case some other part of the program is also trying to load it. In that case we don’t wanna cause errors so we just return. Now, for the actual drawing:

static public void Draw(SpriteBatch spriteBatch)
{

  spriteBatch.Begin(SpriteBlendMode.AlphaBlend);

  //Set up the rectangle.
  Rectangle dstRec = new Rectangle(mouseRec.X, (int)mouseRec.Y, 1, 1);
  Color color = Color.WhiteSmoke;

Again, tons of code, so we break it down. First initalize the variables. Pass spriteBatch as an argument, and begin! Now we set up a temporary rectangle. The origin is going to be the origin of the mouseRec. However, the width and height are only 1 because that’s the size of the border we want for the dragged rectangle. We also set a color because we’re going to use it in the draw function. So better to not hardcode something 4 times.

  if (mouseRec.Width > 0)
  {
    int x = mouseRec.Width / spacing;
    for (int i = 0; i <= x; i++)
    {
      //draw horizontal top
      spriteBatch.Draw(nullTex, dstRec, new Rectangle(0, 0, nullTex.Width, nullTex.Height), color);

      if (mouseRec.height > 0)
      {
        //draw horizontal bottom
        dstRec.Y += mouseRec.Height;
        spriteBatch.Draw(nullTex, dstRec, new Rectangle(0, 0, nullTex.Width, nullTex.Height), color);
        dstRec.Y -= mouseRec.Height;
      }

      //advance
      dstRec.X += spacing;
    }
  }

We’re going to have to do two independent checks. One for width and one for height. We could potentially do it in one, but it’s just messy if we do. So much better to have two separate statements. First things first, we set up a sentinel value. This value is the width of the rectangle. The result is the space between each graphical assets to be drawn as a border. I like 7 because it gives you this ton of space between dots. Makes it oddly cool. Now we loop through the entire width.

Notice that the condition is not less than but also equal to. This is because if you don’t draw one more, you’re going to have a odd gap on the bottom right corner. This way the extra space is filled by a extra iteration. Now, I draw the bottom horizontal line. For this we first check if the height is greater than zero. If it is not greater than zero, then we don’t even worry about drawing it. This will certainly save cycles. If the height is greater, then we will move the Y position of our temp rectangle to the bottom of the rectangle. and we draw one more sprite there.

Next we reset it back at the top for good measure. The tutorial I linked above has this whole thing outsourced to a function which is called 2 times. I rather do it in the same loop. it’s one less jump and we have all the information at hand. The verticals are identical to the horizontals. The only difference is that we’re moving to the right column if the width is greater than zero:

if (mouseRec.Height > 0)
{
  dstRec.X = mouseRec.X;
  int Y = mouseRec.Height / spacing;
  for (int i = 0; i <= Y; i++)
  {
  //draw vertical right
  spriteBatch.Draw(nullTex, dstRec, new Rectangle(0, 0, nullTex.Width, nullTex.Height), color);

    if(mouseRec.Width > 0)
    {
      //draw vertical left
      dstRec.X += mouseRec.Width;
      spriteBatch.Draw(nullTex, dstRec, new Rectangle(0, 0, nullTex.Width, nullTex.Height), color);
      dstRec.X -= mouseRec.Width;
    }

    dstRec.Y += spacing;
  }
}

  spriteBatch.End();
}

Finally we close the spriteBatch! PHEWW… that was long. But I think at the end, it makes for a nice effect and certainly adds a ton of functionality! The final result?

mouseborder

Comments are closed.