Prevent multiple keys pressed at same time SFML

I was making a snake game in SFML, and whenever someone presses more than 1 key at a time, the condition of the snake not going in the opposite direction of what it was already in fails. Is there a way to disable SFML from allowing the user to press two keys at a time, since it basically breaks my game?
Why don't you just check if multiple keys are pressed within your own code?
How? I tried making a flag that doesn't do anything if multiple keys are pressed, but that has no effect, but it doesn't work, since the code is checked in the next frame of the game, not the same frame. since i am using a fixed time step, all of this is evaluated in the first frame of the game. This is what my loop looks like:

1
2
3
4
5
6
7
8
9
10
void SnakeGame::run()
{
	while (gameWindow_.isOpen())
	{
		proccessGameEvents();
		updateGame();
		renderGraphics();
		sf::sleep(sf::milliseconds(90));
	}
}


And here is the reading function for input

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void SnakeGame::proccessGameEvents()
{
	sf::Event gameEvent;
	while (gameWindow_.pollEvent(gameEvent))
	{
		if (gameEvent.type == sf::Event::Closed)
		{
			gameWindow_.close();
		}
		else if (gameEvent.type == sf::Event::KeyPressed)
		{
			switch (gameEvent.key.code)
			{
			case sf::Keyboard::Escape:
				gameWindow_.close();
				break;
			case sf::Keyboard::Up:
				if (!(snake_.getDirection() == Direction::DOWN))
				{
					snake_.changeDirection(Direction::UP);
				}
				break;
			case sf::Keyboard::Down:
				if (!(snake_.getDirection() == Direction::UP))
				{
					snake_.changeDirection(Direction::DOWN);
				}
				break;
			case sf::Keyboard::Left:
				if (!(snake_.getDirection() == Direction::RIGHT))
				{
					snake_.changeDirection(Direction::LEFT);
				}
				break;
			case sf::Keyboard::Right:
				if (!(snake_.getDirection() == Direction::LEFT))
				{
					snake_.changeDirection(Direction::RIGHT);
				}
				break;
			default:
				break;
			}
		}
	}
}
Your input system should not be so strongly coupled with your game logic. Your processGameEvents function should not be polling for input, you should leave that to a dedicated input class/function.

Instead, in a separate input function, poll for input. Each time a key press is detected, set a variable to indicate its state. Your processGameEvents function should simply call the snake_.changeDirection based on the value of the said variables.

Pseudo code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
enum class Direction {
    Up,
    Down,
    Left,
    Right,
    Count
};

// Keep a copy of both this frame's and the last frame's direction in order to compare them
Direction lastDirection;
Direction newDirection;

void Input() {
    // Poll here and set the new direction
    newDirection = // bla bla bla
}

void Update {
    if (lastDirection == Direction::Left && newDirection == Direction::Right)
        gameOver();
    // Etc...
    
    if (gameEnded)
        return;
    snake_.changeDirection(lastDirection);
}

Last edited on
@JagerDesu, while I appreciate your overall idea, I decided to think a little more and go a different route, which also fixed the problem. What I decided to do was use a std::deque<Direction> of direction commands, and my code was as follows:

Here is the input function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void SnakeGame::proccessGameEvents()
{
	sf::Event gameEvent;
	while (gameWindow_.pollEvent(gameEvent))
	{
		if (gameEvent.type == sf::Event::Closed)
		{
			gameWindow_.close();
		}
		else if (gameEvent.type == sf::Event::KeyPressed)
		{
			switch (gameEvent.key.code)
			{
			case sf::Keyboard::Escape:
				gameWindow_.close();
				break;
			case sf::Keyboard::Up:
				directionCommands_.push_back(Direction::UP);
				break;
			case sf::Keyboard::Down:
				directionCommands_.push_back(Direction::DOWN);
				break;
			case sf::Keyboard::Left:
				directionCommands_.push_back(Direction::LEFT);
				break;
			case sf::Keyboard::Right:
				directionCommands_.push_back(Direction::RIGHT);
				break;
			default:
				break;
			}
		}
	}
}


And the corresponding update function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void SnakeGame::updateGame()
{
	if (!directionCommands_.empty())
	{
		snake_.changeDirection(directionCommands_.front());
		directionCommands_.pop_front();
	}
	sf::Vector2f currentSnakeLocation = snake_.getLocation();
	//Checks if the snake is out of bounds
	if ((currentSnakeLocation.x >= windowWidth_ - 16) || (currentSnakeLocation.x < 16))
	{
	}
	else if ((currentSnakeLocation.y >= windowHeight_ - 16) || (currentSnakeLocation.y < 16))
	{
	}
	//End snake out of bounds checking
	snake_.moveForward();
}
While it may not be an issue for a small game such as this, your new approach introduces unwanted behavior. You are allocating memory for each key press detected, and only popping the latest command from the deque, ultimately leaking memory in the event that there are multiple direction commands within the deque.
But doesn't the destructor of my snake game class automatically call the destructor of the deque class? How is memory leaked? Sorry for my naive nature, I just didn't understand what you are trying to say.
Last edited on
You are allocating memory for each key press detected

Where does he allocate memory?

But doesn't the destructor of my snake game class automatically call the destructor of the deque class?

Normally the destructor is called automatically when directionCommands_ goes out of scope. There is nothing to worry about, unless you use new or malloc in your code which you don't seem to do.
Thanks @Thomas1965, I was wondering how I could be leaking memory when I don't even use new (malloc is out of the question anyways). Also one more question: Would it be better to just use a queue for something like this or is deque still okay to use?
A deque provides more functionality than a queue but you don't need it.
You add values at the end and remove them from the front and for that the queue is fine.
I think @JagerDesu is referring to push_back, which will call new if the array grows past capacity.

Basically, each processGameEvents call you can add up to 4 elements to your directionCommands_ vector. However, you have a one-to-one relationship with your updateGame command, which only pops one object off. This'll be fine if you spend most of your cycles of game loop getting no input (or even just one press), but the moment you're consistently getting more than one keypress the vector will just grow continuously, until you (possibly) run out of memory.

For example, if I sat there spamming all the arrow keys (for some reason), after an hour playing your game the vector will have around 1,000,000 elements in it. Of course, the idea that that might happen is patently ridiculous in this circumstance, and even then after an hour I'd only have eaten ~4MB with that array, but imagine if you used similar logic for something that stored 3D meshes in it. On a slightly related note, at that point I'd need to wait about 20 minutes for the game to stop processing my input. You can solve this by eating the whole thing each updateGame call.

A memory leak doesn't have to be "heap-allocated memory goes out of scope and I can't free it anymore". It can also be "massive array of random data I don't actually need/could get rid of is using up all my memory and I got none left".

As for your last question, a queue is just a wrapper on a deque anyway (well, normally), so it doesn't really matter. In this case I'd use a queue to make it more obvious what the intended purpose and usage of the var is, and since you don't need to iterate over it.
I get what you are saying. So since i switched to a queue, should I just push_back a command if the size of the queue is less than a given amount, say ten? Or would it be better to clear the queue every time I pop a command?
You can do whatever you like; as I said, in this circumstance it isn't really a problem anyway. However, I'd probably just eat all your input at once by wrapping the updateGame function in a while (!directionCommands_.empty()). That way you'll never have more than four elements in your queue anyway.
Ok I am probably go with what I said, since

while (!directionCommands_.empty()) probably would not work, since if I popped all the commands in the same frame, wouldn't that make the other directions interpreted in the same frame break the logic of the code? (I can't have the snake going in the opposite direction of what he is going in already.)
Is there any difference between doing 4 actions interspersed with rendering and 4 actions directly one after another? If no, there's no reason not to loop. If yes, well, you should probably fix that.
Topic archived. No new replies allowed.