pySFML basics

Warning

The module has recently been renamed from sf to sfml, to be more clear and avoid clashes. However, it’s easy to still use sf as the namespace in your code; just write import sfml as sf. This is the approach that we follow in this tutorial and in the examples. The reference uses sfml though, since it’s the “official” namespace.

Welcome to pySMFL’s official tuturial! You are going to learn how to display an image and move it based on user input. But first, here is the full listing:

import sfml as sf


def main():
    window = sf.RenderWindow(sf.VideoMode(640, 480),
                             'Drawing an image with SFML')
    window.framerate_limit = 60
    running = True
    texture = sf.Texture.load_from_file('python-logo.png')
    sprite = sf.Sprite(texture)

    while running:
        for event in window.iter_events():
            if event.type == sf.Event.CLOSED:
                running = False

        if sf.Keyboard.is_key_pressed(sf.Keyboard.RIGHT):
            sprite.move(5, 0)
        elif sf.Keyboard.is_key_pressed(sf.Keyboard.LEFT):
            sprite.move(-5, 0)

        window.clear(sf.Color.WHITE)
        window.draw(sprite)
        window.display()

    window.close()


if __name__ == '__main__':
    main()

You can get the python-logo.png file here, or use any other image file supported: bmp, dds, jpg, png, tga, or psd.

Note

If you’re new to Python, you may find the last two lines confusing. They’re not necessary to make the script run: if you remove them as well as the def main(): line and adjust the indentation accordingly, the program will still run fine. But it’s a good practice to use this pattern in your scripts.

The main() function that we defined isn’t a “standard” function that gets automatically called, like in C or C++. So we call the function ourself if __name__ == __main__, i.e. if our file has been launched by the user, rather than imported by some code. You can find more information here: http://stackoverflow.com/questions/419163/what-does-if-name-main-do

Creating a window

Windows in pySFML are created with the RenderWindow class. This class provides some useful constructors to create directly our window. The interesting one here is the following:

window = sf.RenderWindow(sf.VideoMode(640, 480), 'SFML Window')

Here we create a new variable named window that will represent our new window. Let’s explain the parameters:

  • The first parameter is a VideoMode, which represents the chosen video mode for the window. Here, the size is 640x480 pixels, with a depth of 32 bits per pixel. Note that the specified size will be the internal area of the window, excluding the titlebar and the borders.
  • The second parameter is the window title.

If you want to create your window later, or recreate it with different parameters, you can use its RenderWindow.create() method:

window.create(sf.VideoMode(640, 480), 'SFML Window');

The constructor and the RenderWindow.create() method also accept two optional additionnal parameters: the first one to have more control over the window’s style, and the second one to set more advanced graphics options; we’ll come back to this one in another tutorial, beginners usually don’t need to care about it. The style parameter can be a combination of the sf.Style flags, which are NONE, TITLEBAR, RESIZE, CLOSE and FULLSCREEN. The default style is Style.RESIZE | Style.CLOSE.

# This creates a fullscreen window
window.create(sf.VideoMode(800, 600), 'SFML Window', sf.Style.FULLSCREEN);

Video modes

When you create a VideoMode, you can choose the bits per pixel with a third argument. If you don’t, it is set to 32, which is what we do in our examples, since it’s probably the most common value.

In the previous examples, any video mode size works because we run in windowed mode. But if we want to run in fullscreen mode, we have to choose one of the allowed modes. The VideoMode.get_fullscreen_modes() class method returns a list of all the valid fullscreen modes. They are sorted from best to worst, so sf.VideoMode.get_fullscreen_modes()[0] will always be the highest-quality mode available:

window = sf.RenderWindow(sf.VideoMode.get_fullscreen_modes[0], 'SFML Window', sf.Style.FULLSCREEN)

If you are getting the video mode from the user, you should check its validity before applying it. This is done with VideoMode.is_valid():

mode = get_mode_from_somewhere()

if not mode.is_valid():
    # Error...

The current desktop mode can be obtained with the VideoMode.get_desktop_mode() class method.

Main loop

Let’s write a skeleton of our game loop:

# Setup code
window = sf.RenderWindow(sf.VideoMode(640, 480), 'SFML window')
# ...

while True:
    # Handle events
    # ...

    window.clear(sf.Color.WHITE)

    # Draw our stuff
    # ...

    window.display()

RenderWindow.clear() fills the window with the specified color. (If you don’t pass any color, black will be used.) You can create “custom” color objects with the Color constructor. For example, if you wanted to a pink background you could write window.clear(sf.Color(255, 192, 203)). The call to RenderWindow.display() simply updates the content of the window.

This code doesn’t look right currently, because we have a loop that doesn’t really do anything: it just draws the same background over and over. Don’t worry, it will make more sense once we will actually draw stuff.

If you run this program and look at your process manager, you’ll see that it is using 100% of one of your processor’s time. This isn’t surprising, given the busy loop we wrote. A simple fix is to set the RenderWindow.framerate_limit attribute:

window.framerate_limit = 60

This line tells SFML to ensure that the window isn’t updated more than 60 times per second. It should to go in the setup code.

Event handling basics

The most common way to handle events in pySFML is to use RenderWindow.iter_events(). You can still use RenderWindow.poll_event() like in C++ SFML, but it will just make the code look a bit clumsy.

If you’re used to C++ SFML, you will need to change your habit: pySFML events only have the attributes that make sense for this particular event; there’s no equivalent to the C++ union.

You need to test the type attribute to know kind of event you’re looking at. Here are the event types:

  • sf.Event.CLOSED
  • sf.Event.RESIZED
  • sf.Event.LOST_FOCUS
  • sf.Event.GAINED_FOCUS
  • sf.Event.TEXT_ENTERED
  • sf.Event.KEY_PRESSED
  • sf.Event.KEY_RELEASED
  • sf.Event.MOUSE_WHEEL_MOVED
  • sf.Event.MOUSE_BUTTON_PRESSED
  • sf.Event.MOUSE_BUTTON_RELEASED
  • sf.Event.MOUSE_MOVED
  • sf.Event.MOUSE_ENTERED
  • sf.Event.MOUSE_LEFT
  • sf.Event.JOYSTICK_BUTTON_PRESSED
  • sf.Event.JOYSTICK_BUTTON_RELEASED
  • sf.Event.JOYSTICK_MOVED
  • sf.Event.JOYSTICK_CONNECTED
  • sf.Event.JOYSTICK_DISCONNECTED

In our case, we just use the “closed” event to stop the program:

for event in window.iter_events():
    if event.type == sf.Event.CLOSED:
        running = False

Most event objects contain special attributes containing useful values, but CLOSED doesn’t, it just tells you that the user want to close your application. KEY_PRESSED is another common event type. Events of this type contain several attributes, but the most important one is code. It’s an integer that maps to one of the constants in the Keyboard class.

For example, if we wanted to close the window when the user presses the Escape key, our event loop could look like this:

while running:
    for event in window.iter_events():
        if event.type == sf.Event.CLOSED:
            running = False
        elif event.type == sf.Event.KEY_PRESSED:
            if event.code == sf.Keyboard.ESCAPE:
                running = False

See Event types reference for the list of all events and the attributes they contain.

Note

In fullscreen mode, you can’t rely on the window manager’s controls to send the CLOSED event, so it’s a good idea to set a shortcut like we just did to make sure the user is able to close the application.

Drawing the image

You will need to use at least two classes for displaying the image: Texture and Sprite. It’s important to understand the difference between these two:

  • Textures contain the actual image that you want to display. They are heavy objects, and you shouldn’t have the same image/texture loaded more than once in memory. Textures objects can’t be displayed directly; for example there’s no way to set the (x, y) position of a texture. You need to use sprites for this purpose.
  • Sprites are lightweight objects associated with a texture, either with the constructor or the Sprite.texture attribute. They have many visual properties that you can change, such as the (x, y) position, the zoom or the rotation.

In practice, you might have several creatures displayed on screen, all from the same image. The image would be loaded only once into memory, and several sprite objects would be created. They would all have the same texture property, but their position would be set to the creature’s position on screen. They could also have a different rotation or other effects, based on the creature’s state.

There are two main steps to displaying our image. First, we need to load the image in the setup code and create the sprite:

texture = sf.Texture.load_from_file('python-logo.png')
sprite = sf.Sprite(texture)

Now, we can display the sprite in the game loop:

window.clear(sf.Color.WHITE)
window.draw(sprite)
window.display()

Real-time input handling

What if we want to do something as long as the user is pressing a certain key? For example, we want to move our logo as long as the user is pressing the right arrow key, or the left key. In that case, it’s not enough to know that the user just pressed the key. We want to know whether he is still holding it or not.

To achieve that, you would need to set a boolean to True as soon as the user is pressing the key. When you get the “release” event for that key, you set it back to False. And you read the value of that boolean to know whether the right key is pressed or not.

As it turns out, SFML has this kind of feature built in. You can call Keyboard.is_key_pressed() with the code the key as an argument; it will return True if this key is currently pressed. The key codes are class attributes in Keyboard: for example, Keyboard.LEFT and Keyboard.RIGHT map to the left and right arrow keys. Your event loop would then look something like this:

while running:
    for event in window.iter_events():
        if event.type == sf.Event.CLOSED:
            running = False

    if sf.Keyboard.is_key_pressed(sf.Keyboard.RIGHT):
        sprite.move(5, 0)
    elif sf.Keyboard.is_key_pressed(sf.Keyboard.LEFT):
        sprite.move(-5, 0)

The Mouse class provides a similar class method, Mouse.is_button_pressed(), for when you need to know whether a mouse button is pressed.

Images and textures

Another class may be useful for displaying images: Image. The difference between a texture and an image is that a texture gets loaded into video memory and can be efficiently displayed. If you want to display an image, you need to create a texture and call Texture.load_from_image(), and then display the texture. On the other hand, you can access and modify the pixels of an image as needed.

The bottom line is: use textures by default, and use images only if it’s needed.