0

I have come across a situation I have not encountered before using the IF control structure, with a further nested IF. Normally the nested IF would break down what I am checking in more fine grained detail, but in this instance it actually conflicts with the outer conditional - I am checking for a mouse click.

Here is the code -

if (mouseState.LeftButton == ButtonState.Pressed)
{
     if (mouseState.LeftButton == ButtonState.Released)
     {
          //move sprite to clicked position
          spritePos.X = mouseState.X;
          spritePos.Y = mouseState.Y;
     }
}

I have a couple of questions. I was taught that the conditional argument is only checked once, if it evaluates as true then the following statements are executed. Is this true, or is it actually checked before executing every statement within the block? - as the latter would obviously cause conflict with my nested if.

If this is true, then why does my button click method not work? The sprite never moves as though the mouse release never executes. Is it because the delay between performing a check on both conditionals is so small, that I can not physically have released the button within that time? Can I be slightly hackish about this and overcome it with a time delay which might be large enough to allow the release of the mouse button by the time of checking the nested conditional?

Methods for mouse click actions that I have seen, use properties of the mouse from the previous frame, and compare to the new frame to check for a mouse click. But this method must introduce a delay of at least one frame to actually process the action. This might not be noticeable with high framerates, but just for arguments sake say I had a game rendering 20fps. UpdateInput() would be called before rendering any frame, positions should update and then the frame should render last.

If the full mouse click operation could operate within less than 1/20th of a second, wouldn't it be preferrable to process the action at once rather than store a variable upon the initial press, and compare states in the next frame? This would surely be better than introducing further delay on input.

I'm sure this must have been considered by others before though, and perhaps I'm not thinking correctly about the time delay operation actually pausing execution of the code. Can time delays pause processor execution of code, or only the result of those executions such as rendering and accepting input? They are not something which I have any experience in using, and I may be confusing how a delay would work.

Thank you, Andrew.

skaffman
  • 398,947
  • 96
  • 818
  • 769
Drew_StackID
  • 199
  • 1
  • 1
  • 9
  • What language are you programming in, and what APIs are you using. This really depends on whether the evaluation of mouseState.LeftButton can have SIDE EFFECTS which can change the state of mouseState (or wait until it is updated) – Alex Brown Nov 22 '10 at 17:20
  • Just a suggestion: your question is more like an essay than a technical question. It's unfocused and difficult to answer. Trim it down a lot. Is the code snippet yours or did you find it somewhere? Does it work? If it doesn't work, what actually happens? What do you expect to happen? The problem here (if there is one) isn't the evaluation of the if statement, that much I can tell you. – Jonathon Faust Nov 22 '10 at 17:43
  • Sorry, I thought it was short enough. I tried just to mention the things I thought of which might have an effect. Code snippet is mine - doesn't work as mentioned just underneath. The result is my arbitrarily animated sprite keeps moving rather than appearing where I click. It's just introductory getting to learn input. Language is C# using XNA 3.1. – Drew_StackID Nov 22 '10 at 18:01

3 Answers3

2

Oh there is so much wrong here...

What you are presumably doing before your block of if statements is:

MouseState mouseState = Mouse.GetState();

Now the GetState function is what actually determines the current state of the mouse and packs it into a MouseState structure. With the = you're assigning that state into the mouseState variable.

The only time that mouseState will change is when you change it yourself! So your conditional that looks like this:

if (mouseState.LeftButton == ButtonState.Pressed) {
    if (mouseState.LeftButton == ButtonState.Released) { /* "do stuff" */ }
}

You're checking if mouseState.LeftButton, which isn't changing, is equal to two completely different things! So obviously "do stuff" will never run.

Now the other part of the problem I can illustrate by making the code work the way you seem to think it should work:

if (Mouse.GetState().LeftButton == ButtonState.Pressed) {
    // Some kind of delay, perhaps?
    if (Mouse.GetState().LeftButton == ButtonState.Released) { /* "do stuff" */ }
}

So the state is updated each time you check it.

Now here's the problem with this code. Your mouse must make the transition from the "Pressed" state to the "Released" state between the two if statements. If it does not, the condition will not trigger.

Increasing the length of the delay will not fix it for many reasons: First of all it is taking time away from actually running your game. Second of all it is impossible to increase it to 100% of the frame's time - so there will always be a percentage chance that your condition will not trigger.

Therefore the better way to handle input is to track the state state between frames. And the best way to do this is, just like all the tutorials demonstrate, to store a MouseState lastMouseState; between calls to your Update function. So your code should look something like this:

MouseState mouseState = Mouse.GetState();
if (lastMouseState.LeftButton == ButtonState.Pressed)
{
    if (mouseState.LeftButton == ButtonState.Released)
    {
        /* "do stuff" */
    }
}
lastMouseState = mouseState;

Now, you might say, this still leaves you with the actual problem you mentioned in the first place: what if the use clicks so quickly that the mouse state changes and then changes back between calls to GetState?

Well - my first response to that is "don't worry about it". This is a known issue with the XNA input framework. But in practice you will find that people do not generally click or type so quickly that this causes actual problems at 60FPS.

If your game draws at significantly less than 60FPS, you can still run your updates at 60FPS. To reduce your draw rate you can use Game.SupressDraw or make Game.BeginDraw return false (see here and here). If you also need to run your updates at less than 60FPS, you can still update input at 60FPS (or faster!), but it's a lot more work. (Note that XNA input needs to be done on the main thread.)

(Personally I would suggest you just run at 60FPS and stop worrying about it!)

Finally, if that is not enough for you, you can mess about with interop and hook the message pump and catch WM_LBUTTONUP messages. This is massive overkill, but is the "correct" way to get what you want.

Community
  • 1
  • 1
Andrew Russell
  • 26,924
  • 7
  • 58
  • 104
  • Thanks Andrew, that information is really useful. I'm not likely to try any of the more advanced things, I've been programming on/off for a while now finding it hard to get into, so every time I try and give it another shot it's fairly new to me and I forget things. Gave Steve the right answer because he got there a little sooner, but the information you provided was great, thanks. – Drew_StackID Nov 23 '10 at 14:50
1

Andrew, your issue has nothing to do with the way if statements work. mouseState represents the state of the mouse when GetState() was called. It won't change just because the mouse changes its state. For you code snippet to work like you are expecting it to, you would have to call GetState() before each if statement. The mouse state only changes when you poll it or manually set it.

Steve H
  • 5,479
  • 4
  • 20
  • 26
  • I feel so stupid for not noticing that. Thanks for pointing that out to me. – Drew_StackID Nov 23 '10 at 14:46
  • @Drew: Er, you will also need to press the mouse down and release it within the nanosecond between when the first conditional is checked and when the second conditional is checked, which I'm sure is not what you want... – BlueRaja - Danny Pflughoeft Jul 01 '11 at 04:09
-1

it doesn't work, because if you press Left Button then there is no way to release it before program checks if (mouseState.LeftButton == ButtonState.Released) condition.

Can time delays pause processor execution of code

Input delay by mouse and keyboard is unnoticeable.

I'd use variable to store Left Button state each frame:

while (1) // ininity loop
{
    if (mouseState.LeftButton == ButtonState.Pressed)
    {
         button_left_pressed = 1; // is pressed
    }
    else // Left Button is not pressed
    {
         if ( button_left_pressed ) // but was pressed (is released)
         {
             spritePos.X = mouseState.X;
             spritePos.Y = mouseState.Y;
             button_left_pressed = 0; // is not pressed
         }
    }
    // do another action        
}
RobertO
  • 2,655
  • 1
  • 20
  • 18
  • Hi atlavis, I think this should work, it looks very similar to the examples for user input which I have been viewing. I was just a little unsure as to why it didn't work my way. Thanks for answering a couple of my questions. – Drew_StackID Nov 22 '10 at 18:04
  • I have implemented a solution similar to one I found in a tutorial here http://bluwiki.com/go/XNA_Tutorials/Mouse_Input. I notice you have edited your post since I last checked about the time delay. I realise putting a time delay in may cause other issues with where the cursor was when the button was released, and where it is when I finally update the sprite position. But can you just confirm again if a time delay would actually delay code execution (even within the same block)? I think they do as how else can framerates be limited, but just want to be sure. Thanks. – Drew_StackID Nov 22 '10 at 18:41
  • yes, any time delay function stops code execution, like delay (time in miliseconds) in PASCAL – RobertO Nov 22 '10 at 18:45
  • Here I found examples for C#: http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/b4f9a160-06d2-4f5b-9c1e-e3961a18a6aa – RobertO Nov 22 '10 at 18:57
  • or here http://www.programmersheaven.com/mb/csharp/355557/355557/delay-function-in-c/?S=B20000 – RobertO Nov 22 '10 at 19:04