In this Closer Look At we are going to be taking a closer look at the SFML framework. The Closer Look At series is a combination of an introduction, overview and getting started tutorial that is designed to help you quickly decide if a game engine/framework/library is right for you. SFML stands for Simple and Fast Multimedia Library. It’s a cross platform, open source, C++ based collection of libraries handling 2D graphics, input, audio and networking.
An HD video of this guide is available here or embedded below.
Meet SFML
SFML itself is not technically a game engine, it contains no concept of a scene graph, there are no tools, no level editor, etc. Instead it provides the base technical building blocks your build your game or game engine on top of, including platform agnostic system classes, input, audio, networking and 2D graphics. The library itself is very modular in nature and you only use what you need. In terms of scope and functionality it is very similar to LWJGL, Allegro or SDL.
SFML is written in C++ and the source is hosted in this Github repository. It uses the cross platform CMake build system enabling you to create project files for your platform and IDE of choice. There are language bindings available for many other languages including:
- C
- .NET (C#, VB.Net, F#, Managed C++, etc.)
- D
- Java
- Python
- Ruby
- Rust
The full selection of language bindings is available here. Note however, on the C and .NET bindings are officially supported, the rest are developed or contributed by the community and may be incomplete or lag behind the current release.
SFML is composed of several modules, available in static and dynamic forms and compiled in debug or release format. The modules are:
- system handles system level tasks in a cross platform manner, items such as threading, timers and strings
- window native window handling, as well as input handling (polled and event driven mouse, keyboard, touch and gamepads)
- graphics 2D graphics, sprites, textures, shapes, etc. built over OpenGL
- audio audio playback and recording as well as positional audio
- network TCP and UDP based networking as well as FTP and HTTP implementations
Your application or game has to always include the system module as all the other modules depend on it, but all others can be used independently. For example, if you wanted to use SFML for gamepad support, you could simply link system and window. It is actually quite common to use SFML in place of libraries like GLut for handling window creation and input handling in a OpenGL 3D application, ignoring the graphics, audio and networking functionality available. It should be made clear SFML is not and never will be a 3D library, although it can be used in conjunction with 3D libraries and is built over OpenGL.
SFML has traditionally been for desktop operating systems only, which I believe hindered it’s adoption to a degree. However as of version 2.2, there has been support for Android and iOS targets as well.
Developing with SFML
Since SFML is a code only solution, I suppose it makes sense to jump right in with the coding experience eh? Installation is a breeze, assuming your compiler is supported out of the box and you have experience setting up a C++ project. Configuring the linker, working directory, etc. are all huge potential tripping points for new developers. I walk through the entire process for Visual Studio 2013 in the video if you need help. If your compiler isn’t supported out of the box, you will have to build it from scratch using CMake. I actually documented this process in the past, on both Windows and MacOS with CLion if you want more information.
Ideally though, you will just use the precompiled binaries available here(2.3.1 link). Simply select the combination that is right for you. If in doubt and running on Windows, select the 32bit version.
Simply extract the libraries and configure your IDE or makefile to point to the include and lib files, while the required DLLs are in the bin directory and need to be copied into your application folder or somewhere on your system’s path. This process is beyond the scope of this guide, so if you want more details be sure to watch the video. There are however getting started tutorials for Windows, Code Blocks, Linux, XCode/Mac and using CMake. The Windows tutorial is solid, although has a few problems that could trip up new users. ( No mention of requiring the DLLs for a dynamic build, while defining the process for static linking, but not actually setting up the appropriate libraries ). Frankly if you are new and are going to have trouble with SFML, this is probably the point that it will happen. It’s not really SFML’s fault, the downside of using a language with a compile/link process from the 1970s.
Now let’s an extremely simple SFML application.
#include "SFML/Graphics.hpp" int main(int argc, char** argv){ sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Hello Cruel World"); while (renderWindow.isOpen()){ sf::Event event; while (renderWindow.pollEvent(event)){ if (event.type == sf::Event::EventType::Closed) { renderWindow.close(); } } renderWindow.clear(sf::Color::White); renderWindow.display(); } } }
This is about the simplest SFML application you can create and for good reason, it does almost exactly nothing! When you run it, it simply creates a main window 640×480 in size, like so;
It does however show you the very basics of a typical SFML game loop. The application starts up, creates a RenderWindow object, then loops forever or until that window is closed. Each pass through the loop it checks to see all the events that have occurred since that last pass through the loop. Finally it clears the screen and renders the results. If you’ve ever seen a game loop before, this should look immediately familiar.
Now let’s change things up a bit so it actually does something useful. I think it’s about time for a Hello World example!
#include <iostream> #include "SFML/Graphics.hpp" int main(int argc, char** argv){ sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Hello Cruel World"); // Create a font object and load it from file relative sf::Font font; if (!font.loadFromFile("calibri.ttf")){ return 42; // Robust error handling! } //Create Hello World text objecct using our font and size 128pt sf::Text text("Hello World", font, 128); //Set the text color to red text.setColor(sf::Color::Red); //Get the text object's physical dimensions and use them to center the text to our render window //By default things are drawn relative to their top left corner and can be changed by calling setOrigin() sf::FloatRect bounds(text.getLocalBounds()); text.setPosition(renderWindow.getSize().x/2 - (bounds.left + bounds.width/2), renderWindow.getSize().y/2 - (bounds.top + bounds.height/2)); while (renderWindow.isOpen()){ sf::Event event; while (renderWindow.pollEvent(event)){ if (event.type == sf::Event::EventType::Closed) { renderWindow.close(); } } renderWindow.clear(sf::Color::White); //Draw our text object to the window renderWindow.draw(text); renderWindow.display(); } }
In this example we draw the text Hello World centered to our screen in red.
The code is fairly extensively commented but gives you a better idea of how things work in SFML. One thing to be aware of, I had to copy the ttf into the working directory. If the font fails to load, it most likely isn’t in the correct place. By default the origin (0,0) is the top left corner, both of the window and of the object, but can be changed if you prefer.
This example shows two key concepts in SFML, resource loading and Drawables. The Font resource is loaded using it’s method loadFromFile(), although other options exist for loading. These calls are expensive and the resulting object is heavy, meaning you should be careful in where you do it and how long you keep the resource around. The sf::Text object on the other hand is rather light weight, using far less resources. It is possible to share a single sf::Font with multiple sf::Text objects.
The other thing of note here is the sf::Text class which derives from two key classes, sf::Drawable and sf::Transformable. The one class gives the Text object the ability to be drawn to the RenderWindow, while the Transformable class gives it spatial properties, enabling the option to be transformed and positioned. These two classes are inherited by several key objects in SFML including sf::Text, sf::Shape and sf::Sprite.
Speaking of Sprite’s, let take a look at loading and displaying graphics as well as handling input in our next example:
#include "SFML/Graphics.hpp" int main(int argc, char** argv){ sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Hello Cruel World"); sf::Texture texture; if (!texture.loadFromFile("GFSLogo.png")){ renderWindow.close(); } sf::Sprite sprite(texture); sprite.setPosition(0, 0); while (renderWindow.isOpen()){ sf::Event event; while (renderWindow.pollEvent(event)){ if (event.type == sf::Event::EventType::Closed) { renderWindow.close(); } if (event.type == sf::Event::EventType::KeyPressed) { // Event driven input handling if (event.key.code == sf::Keyboard::Left) sprite.move(-1, 0); //Relative transform if (event.key.code == sf::Keyboard::Right) sprite.move(1, 0); } } // Polled input handling -- mouse coordinates are in screen space, not window space if(sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) sprite.setPosition(static_cast<sf::Vector2f>(sf::Mouse::getPosition( renderWindow)));//Absolute transform renderWindow.clear(sf::Color::White); renderWindow.draw(sprite); renderWindow.display(); } }
When you run this example you see your image on screen, like so:
You can move the image left and right one pixel at a time using the left and right arrow keys. If you hold down the spacebar the image will move along with the mouse cursor.
As you can tell, the functionality between using sf::Text and sf::Sprite are very similar and follow a similar pattern. The Resource itself ( the Font in the earlier example, the Texture in this example ) are heavy weight objects and loaded at start up. The instance though, in this case the Sprite, is a much lighter weight object that uses the resource. Due to the common parent’s, the code looks virtually identical between the two objects.
Here you also see the two ways you can handle input in SFML. Can handle it in an event driven manner, responding to Input events inside your game loop. You can also poll the device directly to see what it’s status is. Both are comparable ways to work, simply pick the one that fits your work style the best or mix and match as need.
This example also shows how Transformable’s can be positioned in multiple ways. In response to the arrow keys, we move our sprite relative to it’s previous position using the move() method. Later we set the position directly using the setPosition() method. There are several other options available including matrix transforms.
Our final example is going to show how to use a GLSL vertex and fragment shader with SFML.
Here are the shaders:
shader.frag
uniform sampler2D texture; uniform float opacity; void main() { vec4 pixel = texture2D(texture, gl_TexCoord[0].xy); gl_FragColor = pixel * vec4(1.0,1.0,1.0,opacity); }
shader.vert
void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; gl_FrontColor = gl_Color; }
And finally our C++ code:
#include <SFML/Graphics.hpp> int main(int argc, char** argv){ sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Hello Cruel World"); sf::Texture texture; if (!texture.loadFromFile("GFSLogo.png")) return 42; sf::Sprite sprite(texture); sprite.setPosition(0, 0); sf::Shader shader; if (!shader.loadFromFile("shader.vert", "shader.frag")) return 42; float opacity = 1.0f; shader.setParameter("texture", sf::Shader::CurrentTexture); shader.setParameter("opacity", opacity); sf::Event event; sf::Clock clock; while (renderWindow.isOpen()){ while (renderWindow.pollEvent(event)){ if (event.type == sf::Event::EventType::Closed) renderWindow.close(); if (event.type == sf::Event::EventType::KeyPressed){ if (event.key.code == sf::Keyboard::Left) sprite.move(-1, 0); if (event.key.code == sf::Keyboard::Right) sprite.move(1, 0); } } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)){ sprite.setPosition(static_cast<sf::Vector2f>(sf::Mouse::getPosition( renderWindow))); } if (clock.getElapsedTime().asSeconds() > 0.1){ opacity -= 0.05; if (opacity < 0.0) opacity = 1.0; clock.restart(); shader.setParameter("opacity", opacity); } renderWindow.clear(sf::Color::White); renderWindow.draw(sprite, &shader); renderWindow.display(); } }
When run:
Shaders are actually beyond the scope of this document, this does however illustrates it’s fairly simple to load, pass parameters too and apply a shader in SFML. There is slightly more details in the video version if you are interested. Also for more information on GLSL check here. One thing to be aware of however, shader support on iOS and Android is currently confined to OpenGL ES, which uses the fixed function pipeline, meaning no shader support. I believe this is being addressed in future versions.
Functionality and 3rd Party Libraries
These are relatively minor examples, but they all give you a pretty good idea of what the coding experience is like and the consistency you should expect. For the most part, SFML is a straight forward library that becomes intuitive very quickly. In addition to what we have covered here, SFML also provides support for:
- Sprite/Sprite Sheets
- 2D Geometrics such as Circles and Rectangles
- Render to texture
- Vertex and Pixel Shaders
- Joystick, Keyboard, Mouse, Touch and Sensor Events
- UDP/TCP Networking
- HTTP and FTP implementations
- Audio Playback and Recording
- Spatial Audio
- Music Streaming
- Threading and Mutex support in cross platform manner
- Window creation and handling
- Cross platform string with Unicode support
- Math functionality including Matrix support
- And More
SFML is focused on providing the underpinnings of a 2D game in a cross platform manner. It however is not a full game engine, although there exist several community libraries build over top of SFML to provide game engine like functionality.
Some of these third party libraries include (but are by no means limited to):
Extends SFML adding animation, advanced math, more complex shapes, particle systems, timers and more.
Built over FFMPeg, it adds the ability to play movies.
A tmx (tiled) loader. Other tilemap libraries exist.
Dynamic lighting library.
Documentation and Community
One very strong area for SFML is the documentation. The reference materials are exceptional, and for many developers are all you will need 95% of the time. Rarely is detail glossed over and often there are good examples illustrating the usage. Additionally there are clickable inheritance diagrams making navigating familial hierarchies a breeze.
There are also a comprehensive set of tutorials that are kept current with each new release. Quality is a bit more variable than the reference material, but for the most part the materials are quite good. For a beginner starting out they will find the occasion hole to fall in.
The community is active on both the English and French forums. The active community is somewhat small, and can be a bit… harsh in their response to repetitive posts, but for the most part questions go answered fairly quickly. SFML is popular among the indie and amateur communities, so finding support in other forums such as gamedev.net, reddit or stackoverflow is also an option.
Books
SFML has been around for a number of years and as a result a couple of books on the subject are available:
There is another title, SFML Game Development by Example coming in November. SFML Game Development is the only title I have read and it is a very good book. The links above are affiliate links.
Conclusion
If you are looking for a lower level 2D multimedia library, especially if you want to work with C++, SFML is a great choice. It is very similar to SDL in what it provides, although I personally find the SFML API to be much more to my taste. The code is very clean, consistent and well documented. It was however refactored for SFML 2.0, breaking backward compatibility with many older examples and tutorial series.
Android and iOS support are finally available but are in the early stages so may not be quite as solid or stable as desktop platforms. New releases used to come at a snails pace, so if you aren’t comfortable building from source and depend on critical bug fixes, this can certainly be an issue. Recently however there have been great improvements in this regard and a nightly build option is now available.
Just be realistic in what you expect from SFML. It is not a turn key game engine, nor is it a 3D (or 2.5D) library. If you are looking for either thing, look elsewhere. It is however a very solid, well documented and well supported cross platform media layer for building a 2D game or game engine on top of.