Hey all,
So I'm working on the basics of a 2D zelda-style game (as a hobby) and I've got a question about the event system.
The problem is that when I hold any movement key (WASD), my sprite animation will pause for a short period of time and then act as it should, updating the spriteRect every .1 second.
I took the problem to the console, making it print every time an event was detected, every time the animation function was called, and so on, to figure out what was causing it to pause.
I found that this happens for all events detected including ones I don't handle (like the mouse) and it's the event loop that's skipped whenever an event is held.
When I just press a movement key, it acts as it should and moves the spriteRect for a split second, then resets. The problem is only when an event is held down.
Here's the segments of code that matter-
level1.cpp:
gameWindow.setFramerateLimit(30);
while (gameWindow.isOpen())
{
sf::Event event;
while (gameWindow.pollEvent(event))
{
if (event.type == sf::Event::Closed)
gameWindow.close();
player->handle_input(event, clock);
std::cout << "event detected" << std::endl;
enemy->path(*player, clock);
}
enemy->move();
player->move();
player.cpp handle_input function:
void Player::handle_input(sf::Event event, sf::Clock& clock)
{
switch(event.type)
{
case sf::Event::KeyPressed:
{
switch(event.key.code)
{
case sf::Keyboard::W:
{
velocity.y = -1;
direction(2);
if(clock.getElapsedTime().asSeconds() > 0.1f)
{
walking();
clock.restart();
}
break;
}
case sf::Keyboard::A:
{
velocity.x = -1;
direction(3);
if(clock.getElapsedTime().asSeconds() > 0.1f)
{
walking();
clock.restart();
}
break;
}
case sf::Keyboard::S:
{
velocity.y = 1;
direction(1);
if(clock.getElapsedTime().asSeconds() > 0.1f)
{
walking();
clock.restart();
}
break;
}
case sf::Keyboard::D:
{
velocity.x = 1;
direction(4);
if(clock.getElapsedTime().asSeconds() > 0.1f)
{
walking();
clock.restart();
}
break;
}
}
break;
}
case sf::Event::KeyReleased:
{
switch(event.key.code)
{
case sf::Keyboard::W: velocity.y = 0; break;
case sf::Keyboard::S: velocity.y = 0; break;
case sf::Keyboard::A: velocity.x = 0; break;
case sf::Keyboard::D: velocity.x = 0; break;
}
break;
}
}
if(velocity.x == 0 && velocity.y == 0)
{
spriteRect.left = 0;
msprite.setTextureRect(spriteRect);
}
}
player.cpp move():
void Player::move()
{
position.x +=velocity.x;
if(position.x < 0 || position.x > (wSize.x-19))
position.x -= velocity.x;
position.y += velocity.y;
if(position.y < 0 || position.y > (wSize.y-25))
position.y -= velocity.y;
msprite.setPosition(position);
box = msprite.getGlobalBounds();
}
animate.cpp walking()
void Animate::walking()
{
if (spriteRect.left == (mdimensions.x * 3))
spriteRect.left = 0;
else
spriteRect.left +=mdimensions.x;
msprite.setTextureRect(spriteRect);
}
animate.cpp direction function:
void Animate::direction(int direction)
{
switch(direction)
{
case 1: spriteRect.top = 0; break;
case 2: spriteRect.top = mdimensions.y;break;
case 3: spriteRect.top = (mdimensions.y*2);break;
case 4: spriteRect.top = (mdimensions.y*3);break;
}
msprite.setTextureRect(spriteRect);
}
The sprite DOES move smoothly, with no pause, it's just the animation that pauses for a little bit.
All I'm looking for here is to get rid of that weird intial pause when events are held down and have smooth animation.
I'm about to head out to go to work, but when I get back I'll be able to respond.
It works exactly like when you hold down a key in a text box here.
The event triggers once, then there is a slight delay (defined in your OS) until it triggers faster.
And yeah, if you want something continuous the answer is in the first reply, use sf::Keyboard. ;)
Yes! This helped, I didn't understand the part about my OS so I tried the real-time input method but put it inside my event loop, which was dumb.
If I had to guess, I'd say that walking() is your "start the animation" call?
If this is so, the testing of the clock to not allow it to start on the first event means that it will not start until the second event, which will be be after the initial keyboard repeat delay. Either start it immediately when you set the movement or consider using real-time input.
I tried real-time input but did it wrong, now it works as I want.
-------------------------------------------------------------------------------------------------------------------
However I've encountered another problem. Now the movement isn't working correctly but the animation is very smooth.
The animation rolls continuously and smoothly but the sprite doesn't move continuously. It moves for roughly a second then stops and will move a pixel or so randomly.
My code right now, I changed some stuff up:
while (gameWindow.isOpen())
{
if(sf::Keyboard::isKeyPressed(sf::Keyboard::W))
player->moveUp(clock);
if(sf::Keyboard::isKeyPressed(sf::Keyboard::S))
player->moveDown(clock);
if(sf::Keyboard::isKeyPressed(sf::Keyboard::A))
player->moveLeft(clock);
if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
player->moveRight(clock);
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Q))
gameWindow.close();
sf::Event event;
while (gameWindow.pollEvent(event))
{
if(event.type == sf::Event::Closed)
gameWindow.close();
if(sf::Event::KeyReleased)
player->keyReleased(event);
}
enemy->path(*player, *clock);
enemy->move(*player);
if(!player->getBox().intersects(enemy->getBox()))
player->move();
The player moveUP/DOWN/LEFT/RIGHT. All that's really important here is to see that I set the velocity, the rest is the animation which works fine.
void Player::moveUp(sf::Clock* clock)
{
velocity.y=-1;
yDirection(-1);
if(clock->getElapsedTime().asSeconds() > 0.1f)
{
walking();
clock->restart();
}
}
void Player::moveDown(sf::Clock* clock)
{
velocity.y=1;
yDirection(1);
if(clock->getElapsedTime().asSeconds() > 0.1f)
{
walking();
clock->restart();
}
}
void Player::moveLeft(sf::Clock* clock)
{
velocity.x=-1;
xDirection(-1);
if(clock->getElapsedTime().asSeconds() > 0.1f)
{
walking();
clock->restart();
}
}
void Player::moveRight(sf::Clock* clock)
{
velocity.x=1;
xDirection(1);
if(clock->getElapsedTime().asSeconds() > 0.1f)
{
walking();
clock->restart();
}
}
The move function, very important. It's called every frame and adds velocity to positon. Might be part of the problem:
void Player::move()
{
position.x +=velocity.x;
if(position.x < 0 || position.x > (wSize.x-19))
position.x -= velocity.x;
position.y += velocity.y;
if(position.y < 0 || position.y > (wSize.y-25))
position.y -= velocity.y;
msprite.setPosition(position);
}
I'm not sure what the problem is, I've been looking at it all day, might have made a careless mistake. I want to say that the event loop is still being triggered by the key presses, but I really don't understand how the loop works to begin with.
Okay it's my keyRelease function inside the event loop. I made the console print my velocity at each call of the move() function and for some reason it's being set to zero.
The place that does that is inside my event loop:
if(sf::Event::KeyReleased)
player->keyReleased(event);
which looks like
void Player::keyReleased(sf::Event event)
{
switch(event.key.code)
{
case sf::Keyboard::W: velocity.y = 0; releaseDirection(velocity); break;
case sf::Keyboard::S: velocity.y = 0; releaseDirection(velocity); break;
case sf::Keyboard::A: velocity.x = 0; releaseDirection(velocity); break;
case sf::Keyboard::D: velocity.x = 0; releaseDirection(velocity); break;
}
}
Once again, I don't understand events very well or how the event loop works so I'm going to read around a bit.
while (gameWindow.pollEvent(event))
{
if(event.type == sf::Event::Closed)
gameWindow.close();
if(sf::Event::KeyReleased)
player->keyReleased(event);
}
You're not checking correctly the type of your event.
It should be:
if(event.type == sf::Event::KeyReleased)