Vince
Vince

Reputation: 2646

SDL C++ Mouse Input Handling Toggle Effect

So I'm working on a tower defense game. And I need to select a tile where the user clicks the mouse (right now i'm just drawing a border around the tile). Now the code I have works sometimes and I'm not sure how to get it to work better. Right now with the code I have when I click on an area, the tile gets selected and sometimes it deselects right away and other times it doesn't. Almost like I'm holding the mouse down and it just keeps "clicking" the mouse. What I need is for it to select the tile without deselecting it no matter how long the mouse button is being pressed. My mouse input class is set up like this:

MouseInput.h

#pragma once

#include <SDL.h>

class MouseInput
{
public:
    MouseInput();
    ~MouseInput();

    void Update();

    bool GetMouseButton(int index);
    SDL_Event GetEvent();

    bool GetPressed();
    bool GetReleased();

private:
    void GetButtonStates();

private:
    bool mouseArray[3];
    int x, y;
    bool justReleased;
    bool justPressed;
    SDL_Event e;
};

MouseInput.cpp

#include "Input.h"

MouseInput::MouseInput()
    :
    x(0),
    y(0),
    justReleased(false),
    justPressed(false)
{
    SDL_PumpEvents();

    for (int i = 0; i < 3; i++)
    {
        mouseArray[i] = false;
    }
}

MouseInput::~MouseInput()
{

}

void MouseInput::Update()
{
    SDL_PollEvent(&e);

    justReleased = false;
    justPressed = false;

    if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT)
        justPressed = true;
    else if (e.type == SDL_MOUSEBUTTONUP && e.button.button == SDL_BUTTON_LEFT)
        justReleased = true;
}

bool MouseInput::GetPressed()
{
    return justPressed;
}

bool MouseInput::GetReleased()
{
    return justReleased;
}

bool MouseInput::GetMouseButton(int index)
{
    if (index <= 3 && index >= 1)
    {
        return mouseArray[index - 1];
    }

    return false;
}

SDL_Event MouseInput::GetEvent()
{
    return e;
}

void MouseInput::GetButtonStates()
{
    SDL_PollEvent(&e);

    if (e.type == SDL_MOUSEBUTTONDOWN)
    {
        // 1 = Left; 2 = Center; 3 = Right
        mouseArray[e.button.button - 1] = true;
    }

    if (e.type == SDL_MOUSEBUTTONUP)
    {
        mouseArray[e.button.button - 1] = false;
    }
}

In my Game class I have setup the mouseinput as well as a variable to store the coordinates of the tile that was clicked. I also Have a variable pressed to check to see if the mouse was pressed. In my updating function I check if the mouse was not pressed. If the mouse was not pressed, then I check if the mouse is pressed that way the game can get the coordinates of the mouse position. I know that sounds crazy so here is some of my code:

std::pair<int, int> coords; // Will store the coordinates of the tile position that the mouse rests in
bool pressed; // If the mouse was pressed (i should call it toggle because it's true if the mouse was pressed once, then false if the mouse was pressed again, then true...ect.)

Updating function

Mouse Class

bool clicked; // In my custom Mouse.h file and set to false in constructor
MouseInput input; // In my custom Mouse.h file

Mouse class Update()

input.Update(); // Calls the MouseInput updating

if (input.GetPressed() && !clicked)
{
    clicked = !clicked;
    printf("OK");
}
//else if (clicked && input.GetReleased())
else if (clicked && input.GetPressed()) // Somewhat works. It works almost like before. If I click the mouse it shows up then sometimes when I click again it works, and other times I have to click a couple times
{
    clicked = !clicked;
}

In my game class I just check if mouse clicked is true, then draw what i need

if (mouse.GetClicked())
{
    // Draw what I need here.
    // Currently is only drawn when I hold down the mouse
    // And not drawn when I release the mouse.
}

Then when I'm drawing the square around the tile, I only draw it if pressed = true. Otherwise it is not drawn. This code works sometimes, and other times it doesn't. In order to get the effect I want, I have to real quickly click the mouse. If I don't, sometimes it appears as if I'm holding the mouse button down and the square just flashes.

Upvotes: 0

Views: 1859

Answers (1)

hnefatl
hnefatl

Reputation: 6037

The issue is that the "mouse-down" event is exactly that - an event. It should happen once, be handled, and then not occur until the button is physically pressed again. With your code, once pressed it'll set the internal state to "pressed" and then not change it until it's released. Which is okay, except your code polling this internal state treats it as if it's an event, not as state, so every time the update function runs while the mouse button is pressed, it'll see a "new button press".

This (simplified version of your code that uses one button and an idea of showing an image only when a button's pressed) might help you visualise it:

bool buttonPressed = false;
bool imageShown = false;
while (true)
{
    SDL_Event e;
    SDL_PollEvent(&e);
    if (e.type == SDL_MOUSEBUTTONDOWN)
        buttonPressed = true;
    else if (e.type == SDL_MOUSEBUTTONUP)
        buttonPressed = false;

    if (!imageShown && buttonPressed)
        imageShown = true;
    else if (buttonPressed)
        imageShown = false;
}

Do you see how each time the loop runs, even if no events have been fired this iteration, the state remains? This would cause the imageShown variable to keep toggling on and off each iteration, until a "button up" event is fired to reset the state.

You should probably rework a bit of your input controller - you need to expose a way to differentiate from "button being pressed right now" and "button that's been pressed for a while".

An idea would be to provide eg. ButtonsPressed and ButtonsJustPressed members (although I'm sure you can think of more appropriate names). The ButtonsJustPressed members should be reset to false each time you call GetButtonStates, so they're only true if on that specific iteration of the loop, they've been pressed, while the ButtonsPressed members are exactly what you're doing right now.

A tweaked version of the above to take this into account:

bool buttonJustPressed = false;
bool buttonJustReleased = false;
bool imageShown = false;
while (true)
{
    // As far as we know, this iteration no events have been fired
    buttonJustPressed = false;
    buttonJustReleased = false
    SDL_Event e;
    SDL_PollEvent(&e);
    if (e.type == SDL_MOUSEBUTTONDOWN)
        buttonJustPressed = true;
    else if (e.type == SDL_MOUSEBUTTONUP)
        buttonJustReleased = true;

    if (!imageShown && buttonJustPressed)
        imageShown = true;
    else if (imageShown && buttonJustReleased)
        imageShown = false;
}

See how this version will only change the visibility of the image if a button has just been pressed/released - if the button's being held down, the events aren't fired, so the state remains false, and the image's state remains the same.

Upvotes: 0

Related Questions