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
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::RightMouse movement
sf::Event:MouseMoved
event.mouseMove.x //new location of cursor event.mouseMove.yText input (typing)
sf::Event:TextEntered
event.text.unicode //the character that was typedKey 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.heightMore 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.
Quick code events
Check your repo for the m2/Events
program. Complete the TODOs and demo to me before you leave.
Remember, Program1 is due tonight!