When I Drag A Piece Really Fast, My Mouse Drops It, Why?

Started by
6 comments, last by Heelp 7 years, 9 months ago

Guys, I'm making a 2d game. when I hover with the mouse over a piece and I click and hold the left button and then move the mouse, it starts dragging the piece, cool.

But when I start moving the mouse faster, my cursor drops the piece, wtf is that?

[attachment=32577:mousedrop.gif]

(the recording software I used is bugged, basically my cursor is in the center of the piece when I drag it, it's kind of slowed down here, don't pay attention to it.)

Here are my functions.


void App::handleEvents()
{
    while( SDL_PollEvent( &event ) )
    {
        //If mouse event happened
        if( event.type == SDL_MOUSEMOTION )
        {
            //Get mouse position
            SDL_GetMouseState( &mouseX, &mouseY );

            //If the mouse coordinates are somewhere over the piece, set to true.
            if( mouseX > piece->getRect().x && mouseX < piece->getRect().x + piece->getRect().w &&
                mouseY > piece->getRect().y && mouseY < piece->getRect().y + piece->getRect().h )
            {
                mouseOnPiece = true;
            }

            else
            {
                mouseOnPiece = false;
            }
        }
        
        //If left button is clicked, set to true
        if( event.type == SDL_MOUSEBUTTONDOWN )
        {
            if( event.button.button == SDL_BUTTON_LEFT )
            {
                leftMouseButton = true;
            }
        }

        else if( event.type == SDL_MOUSEBUTTONUP )
        {
            if( event.button.button == SDL_BUTTON_LEFT )
            {
                leftMouseButton = false;
            }
        }

        if( event.key.keysym.sym == SDLK_ESCAPE || event.type == SDL_QUIT )
        {
            running = false;
        }
    }
}

void App::handleLogic()
{
    //If the mouse is over the piece and you hold the left button, then set the center of the piece to the cursor
    if( mouseOnPiece == true && leftMouseButton == true )
    {
        piece->setRect( mouseX - TILE_EDGE / 2, mouseY - TILE_EDGE / 2 );
    }
}
Advertisement

SDL events don't give an event for every pixel that the mouse visits, you just get samples where the mouse is spotted. If you move fast enough, the gap between two sample is big enough for


mouseX > piece->getRect().x && mouseX < piece->getRect().x + piece->getRect().w &&
mouseY > piece->getRect().y && mouseY < piece->getRect().y + piece->getRect().h

to decide "false".

First: Hey, what's up Alberth, I haven't seen you in a long time. :)

Second: How do I do it then, is there any other way that I can do it. But I haven't even capped the framerate. This means that this pollEvent stuff maybe iterates 300 times per second, how can it miss by so much?

Guys, I fixed it with this, basically I just lock the piece to be over the mouse until I release the left mouse button, so I doesn't really care now if the mouse is on the piece all the time.


        //when you hold the left mouse button and your mouse is over the piece, set lock to true
        if( mouseOnPiece == true && leftMouseButton == true )
        {
            lockPieceToMouse = true;
        }

and when left mouse button is released, set lock to false
        else if( event.type == SDL_MOUSEBUTTONUP )
        {
            if( event.button.button == SDL_BUTTON_LEFT )
            {
                leftMouseButton = false;
                lockPieceToMouse = false;
            }
        }

I think part of the problem (in addition what Alberth said, that's what finally causes it to occur) is that you have a bit of wrong program logic for dragging. Or maybe not"wrong", but at least odd.

The "correct" logic for dragging looks somewhat like this:
1. if you get a mouse down event:
- remember position
- remember "mouse down"

2. if you get a mouse up event, decide:
- was I dragging? ---> stop doing that, drop object right where it is
- otherwise ---> if mouse went up, it must have gone down before, so send a "click" event to object under position if there is one (might be a clickable button)

3. if you get a move event: is the mouse button known to be already down AND is the spot where it was pressed inside draggable object?
---> no: do nothing, unless state is already "is dragging"
---> yes: set state to "is dragging", take delta from current pos and mouse down pos, move object by that amount

I don't know why, but what I hate in me the most in this life is when I start optimizing hundreds of really small things that never really matter, so I can gain a couple of nanoseconds, only to see that I've overcomplicated the whole business so much that I missed to see something much more simple and important which turns all the hundreds of small gains to dust.

Sometimes I feel that there are a few chosen people in this world who can see the big picture and do something big in this life, and then there are others like me that will work for them, cleaning up some miserable details.

Has it happened to someone else? :mellow:

... only to see that I've overcomplicated the whole business so much that I missed to see something much more simple and important which turns all the hundreds of small gains to dust.
This is not something to hate. Your realization means you're becoming aware that the bigger picture is at least equally important, which in turn means you're growing as programmer.

It won't be long until you're advocating to others not to write complicated designs and not to optimize until you profiled things :D

2. if you get a mouse up event, decide:
- otherwise ---> if mouse went up, it must have gone down before, so send a "click" event to object under position if there is one (might be a clickable button)


I would note that for a good UI experience you should only send a click event if the mouse up event happened over the same object as the mouse down event.

Aside from avoiding surprises with slippy fingers, it provides a way to "cancel" a click by moving your cursor elsewhere on the screen.

Second, in any UI with clicks or drags, you want the application to "grab" the mouse cursor while the button is down. Some frameworks/OSes do this automatically, some require the user code to get involved. SDL provides SDL_CaptureMouse for this purpose, and its documentation even explicitly calls out "dragging objects" as a probable use case of the function.

These two things together will go a _very_ long way towards making a mouse-based game UI feel less like a rickety game and more like a "real" application.

There are some more advanced approaches here too, e.g. only initiating a drag if it moves a certain distance from the draggable object (but keep dragging even if the mouse returns). That may not be appropriate for pieces in game, though, which usually wants more responsive feedback.

The rest of making the UI feel high quality is all in the art/feedback. Hover effects for anything that is clickable/draggable, "activation" effects for anything that has been pressed and will be triggered if released, good indication of an active drag, feedback on whether a drop target is valid, etc. The user should always have a very clear indication of what actions are available (is this thing clickable or draggable) and any particular action will do (can I click this? I pressed my button; if I let go of my button now, will anything happen and if so what? what will happen if I drop my dragged game piece over this particular board location?)

Sean Middleditch – Game Systems Engineer – Join my team!

Aside from avoiding surprises with slippy fingers, it provides a way to "cancel" a click by moving your cursor elsewhere on the screen.

True story, 'canceling' is on my to-do list, it really helps me much when I play other games. As soon as I finish the game, I will add that detail.

Thanks.

This topic is closed to new replies.

Advertisement