Events
We are using SFML for several reasons: initializing OpenGL, managing windows, and handling events. There are many other libraries that do similar things: SDL, DirectX, Allegro, and more.
Events
Events are generated by the user or the system and can include keys being pressed, the mouse moving, the window being closed, or a timer alarm occurring. Events can be handled with polling or callbacks.
In polling, we will ask the system if any events have occurred. The process is typically like this:
- Application renders image
- Events are queued while rendering
- After render, event queue is polled and processed
Callbacks work by registering a handler function for events.
- The handler function is registered
- While rendering, events are received
- The handler is invoked
The handler is likely invoked after the render is complete, but the exact timing must be defined in the spec.
Processing events
Your interactive applications should have a render loop. This will repeatedly render the output image and collect any events that are generated. A boolean flag should control the run loop. In SFML, the Window object records events. So, during the render loop, you will handle any new events that have occurred.
bool running = true;
while(running)
{
//handle events
...
renderImage();
window.display();
}
There can be multiple events in the queue. You should loop until all
events have been handled. You can fetch events with
Window::pollEvent()
. This method returns false when there are no
events in the queue.
sf::Event event;
while(window.pollEvent(event))
{
//check event type and handle
}
SFML events are unioned. All events are of type sf::Event
. The
specific type of event can be checked with the event.type
property.
if( event.type == sf::Event::KeyPresed )
//handle key press
if( event.type == sf::Event::MouseMoved )
//handle mouse movement
It's important to be able to close the Window. This is represented by the 'close' event. This event occurs when the OS controls are used to close the window. You should close the window if the Escape key is pressed or if the close event occurs. Set the 'running' flag to false to stop the render loop, then handle shut down.
if (event.type == sf::Event::Closed) //OS generated close event
running = false;
if ((event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape)) // Escape key : exit
running = false;
Example events
Mouse buttons sf::Event:MouseButtonPressed
event.mouseButton.x //location of click
event.mouseButton.y
event.mouseButton.button //i.e. sf::Mouse::Right
Mouse movement sf::Event:MouseMoved
event.mouseMove.x //new location of cursor
event.mouseMove.y
Text input (typing)sf::Event:TextEntered
event.text.unicode //the character that was typed
Key presses sf::Event:KeyPressed
and sf::Event:KeyReleased
event.key.code //the key that was pressed
event.key.shift //state of the shift modifier (also alt, ctrl, etc)
Window close sf::Event:Closed
Window resize sf::Event:Resized
event.size.width //new window size
event.size.height
More examples at this SFML tutorial.
Input state
Key presses often have a 'repeat-delay' that can cause an unnatural feel
when using arrow keys or WASD inputs. To handle such presses correctly,
the current state of the button needs to be tracked. This is often done
in a large boolean array of pressed/released states. SFML tracks this
state automatically. The state of a key can be queried with the
sf::Keyboard::isKeyPressed
function.
The input state is not an event! It is the current live state of the input device. Using this in an event loop is probably not what you want. Here's an SFML tutorial on states.
if ( sf::Keyboard::isKeyPressed( sf::Keyboard::A ) )
printf("I love the letter A!\n");
Time
SFML provides platform independent access to clock and time objects.
sf::Clock
tracks time while sf::Time
stores time values. Knowing
time elapsed is necessary for animations.
Once a clock is created, you can restart it with its restart
method.
This method also returns the time since the clock was created or
restarted. You can get the time elapsed since creation or last restart
with the getElapsedTime
method. Check this
page for more
details.
sf::Clock clock; //start a timer
... //do some stuff
//two ways to get the time:
sf::Time elapsed1 = clock.getElapsedTime(); //get time elapsed
sf::Time elapsed2 = clock.restart(); //get time elapsed and restart timer
sf::Time elapsed3 = elapsed2 - elapsed1; //can do math ops on time
float t = elapsed3.asSeconds();
printf("time for single line: %f\n", t);
Animation
When animating, you should treat your application as a simulation. It should render the output, collect the input, and simulate the virtual world. To properly simulate a virtual world, you must account for computers of different speeds.
If you can define the animation deltas as a function of time, you can track the time that has elapsed and animate with a certain amount per time unit. This allows a varying amount of time between simulation steps.
Sometimes this is not possible and each simulation step results in a fixed amount of change. In this case, track the time that has elapsed and sleep until it is time to simulate again. This method is more difficult and may require multiple simulation steps to catch up to the correct real time.