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:

Callbacks work by registering a handler function for events.

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.

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!