Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
20. October 2015

 

In the previous tutorial we covered the basics of using graphics in SFML.  Chances are however your game isn’t going to be composed of simple shapes, but instead made up of many sprites.  That is exactly what we are going to cover today.

 

As always, there is an HD version of this tutorial available here

 

First off, we should start by defining what exactly a Sprite is.  In the early days of computers, sprite had special meaning as there was literally sprite hardware built into early 8bit computers.  A sprite is basically an image on screen that can move.  That’s it.   In SFML this relationship is easily demonstrated by it’s class hierarchy.

 

image

 

There is one very key concept to understand with sprites however.  A sprite in SFML represents and image or texture on screen that can be moved around.  However, it does not own the texture or image!  This makes the Sprite class fairly light weight, which certainly isn’t true about Texture or Image, the classes that actually contain all the data in the image.  Perhaps it’s easiest to start with a simple demonstration.

 

First we need an image to work with.  I am using a dragon sprite from the recent Humble Indie Gamedev Bundle.  The image looks like so:

dragonBig

 

Obviously you can use whatever image you want, just be sure to copy it into the working directory of your application.  In Visual Studio, the working directory can be located in the project’s properties panel under Debugging called Working Directory:

image

 

The image can be any of the following formats: bmp, hdr, gif, jpg, png, pic, psd, tga.  Keep in mind, not all formats are created equal.  Bitmap for example does not support transparency encoding and are generally quite large, but lose no image details and are simple to work with.  Gif has some patent issues and should generally be avoided.  Png seems like genuinely a good mix between features, size and quality and is well supported by content creation tools.

 

Ok, enough setup, let’s get to some code.

// Demonstrate sprite drawing in SFML
#include "SFML/Graphics.hpp"

int main(int argc, char ** argv){
  sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game");

  sf::Event event;

  sf::Texture texture;
  texture.loadFromFile("images/dragonBig.png");

  sf::Sprite sprite(texture);

  while (renderWindow.isOpen()){
    while (renderWindow.pollEvent(event)){
      if (event.type == sf::Event::EventType::Closed)
        renderWindow.close();
    }

    renderWindow.clear();
    renderWindow.draw(sprite);
    renderWindow.display();
  }
}

 

And when you run that:

image

 

As you can see, the experience is remarkably consistent to drawing using graphics primitives.  The big difference here is that we create our Sprite by providing a texture, which in turn we loaded from file with a call to Texture::loadFromFile().  There exist methods to load from stream or memory if preferred.  It is again important to remember that the Sprite does not own the Texture.  This means if the texture goes out of scope before the Sprite, the sprite will draw a blank rectangle.  This also means that several sprites can use the same texture.

 

Now you may have noticed that in addition to sf::Texture, there is a class called sf::Image and you may be wondering why.  There is one very simple difference at play here.  A Texture resides in the memory of your graphics card, while an image resides in system memory.  The act of copying an image from system memory to the GPU is quite expensive, so for performance reasons you almost certainly want to use Texture.  That said, Texture isn’t easily modified, so if you are working on a dynamic texture or say, creating a screen shot, Image is the better choice.  There exist methods to switch between the two types, but they are also fairly heavy in performance, so do not do them on a frame by frame basis.  At the end of the day

 

Let’s take a quick look at creating a dynamic image next.  Not something you are going to do often for most games granted, but it makes sense to mention it now.

// Demonstrate creating an Image
#include "SFML/Graphics.hpp"

int main(int argc, char ** argv){
  sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game");

  sf::Event event;

  sf::Image image;
  image.create(640, 480, sf::Color::Black);
  
  bool isBlackPixel = false;
  sf::Color blackPixel(0,0,0,255);
  sf::Color whitePixel(255, 255, 255, 255);

  //Loop through each vertical row of the image
  for (int y = 0; y < 480; y++){
    //then horizontal, setting pixels to black or white in blocks of 8
    for (int x = 0; x < 640; x++){


      if (isBlackPixel)
        image.setPixel(x, y, blackPixel);
      else
        image.setPixel(x, y, whitePixel);
      // Every 8th flip colour
      if (!(x % 8))
        isBlackPixel = !isBlackPixel;
    }
    // Offset again on vertical lines to create a checkerboard effect
    if(!(y%8))
      isBlackPixel = !isBlackPixel;
  }

  sf::Texture texture;
  texture.loadFromImage(image);
  sf::Sprite sprite(texture);

  while (renderWindow.isOpen()){
    while (renderWindow.pollEvent(event)){
      if (event.type == sf::Event::EventType::Closed)
        renderWindow.close();
    }

    renderWindow.clear();
    renderWindow.draw(sprite);
    renderWindow.display();
  }
}

 

When you run this example you should see:

image

 

Here you can see we can modify the pixels directly in our sf::Image.  However to display it on screen we still need to move it to texture and populate a sprite.  The difference is direct access to the pixel data.  Another important capability of sf::Image is the method saveToFile which enables you to well, save to file.  Obviously useful for creating screenshots and similar tasks.

 

You may notice depending on the resolution or composition of your source image that you texture may not look exactly like your source image.  This is because there is a smoothing or antialiasing filter built in to SFML to make images look smoother.  If you do not want this, perhaps going for that chunky 8bit look, you can turn it off with a call to setSmooth(false);

 

That is all we are going to cover today.  In the next tutorial part we will take a look at spritesheets, so we can have multiple different sprites in a single source image.

 

The Video

 

Programming , , ,

15. October 2015

 

In the previous tutorials we covered configuration, game loop and finally keyboard handling.  Today we are going to move on to the basics of graphics programming in SFML.  We are going to start with simple geographic primitives, then in the following tutorial we will move on to sprites.  You will notice right away that SFML is remarkably consistent in it’s approach to graphics, just like it is with Events.

 

As always, there is an HD video of this tutorial available here.

 

Let’s jump right in with a simple example that draws a blue circle on the screen.

// Demonstrate primitve drawing in SFML

#include "SFML/Graphics.hpp"

int main(int argc, char ** argv){
  sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game");

  sf::Event event;

  sf::CircleShape circleShape(200);
  circleShape.setFillColor(sf::Color::Blue);

  while (renderWindow.isOpen()){
    while (renderWindow.pollEvent(event)){
      if (event.type == sf::Event::EventType::Closed)
        renderWindow.close();
    }

    renderWindow.clear();
    renderWindow.draw(circleShape);
    renderWindow.display();
  }


}

Run this code and you will see:

image

This example creates a circle with a radius of 200 pixels that is filled with the colour blue.  Drawing is simply a matter of passing the shape into the draw() method of RenderWindow.  This is the way you draw pretty much everything in SFML.  If you take a look at the class reference for Drawable, the base class of any object passed into draw(), you can get a good idea of what classes are available.

image

 

As you can see, our CircleShape is part of that hierarchy.   Other than showing how to draw things to a window, this example shows us a few key concepts.  First, notice that we didn’t specify the location of our circle and by default it drew itself at the top left corner of the screen?  This shows us two critical things.  First, that the origin (0,0) of a window in SFML is the top left corner.  Second, that the origin/pivot ( the point within a shape a Drawable is drawn relative to ) is also it’s top left corner.  What if for example we wanted to position our circle relative to it’s midpoint instead?  Fortunately this is quite simple:

  sf::CircleShape circleShape(200);
  circleShape.setOrigin(circleShape.getRadius(), circleShape.getRadius());
  // or you could have calculated the mid point like so:
  // circleShape.setOrigin(circleShape.getLocalBounds().width / 2.0f, circleShape.getLocalBounds().height / 2.0f);

  circleShape.setFillColor(sf::Color::Blue);

Here we are setting the point draw calls are performed relative to by calling setOrigin().  This call sets that point in pixel coordinates relative to the top left corner of the Drawable.  Now this code will result in the following:

image

One other concept we didn’t cover is actually positioning our objects.  It is simply a matter of calling setPosition() like so:

circleShape.setPosition(100.0f, 100.0f);

This will result in the shape being drawn 100 pixels right and 100 pixels down from the top left corner of the window.  Of course the origin value is going to influence how that draw is performed.

 

Another important concept to cover is draw order.  Let’s throw in another circle, this time a red one, to illustrate what I mean:

    sf::CircleShape circleShape(200);
    circleShape.setFillColor(sf::Color::Blue);
    circleShape.setPosition(100.0f, 100.0f);

    sf::CircleShape circleShape2(200);
    circleShape2.setPosition(0.0f,0.0f);
    circleShape2.setFillColor(sf::Color::Red);
  
    //snip

    renderWindow.clear();
    renderWindow.draw(circleShape);
    renderWindow.draw(circleShape2);
    renderWindow.display();
  

 

Now run it and:

image

 

The order of draw calls is critically important.  Swap around the code, like so:

    renderWindow.clear();
    renderWindow.draw(circleShape2);
    renderWindow.draw(circleShape);
    renderWindow.display();

And you get:

image

 

As you can see, the later the draw call, the higher the z order.  Or put simply, things draw first are drawn first, then subsequent draw calls are drawn over top.  For future reference, this order is often referred to as the z-index.

 

Now that we have graphics to play with, there are a few things we can do with them…  such as move, rotate and scale.  Each of those functions transforms relative to the current position.  You can also directly set all three values using setPosition, setRotation and setScale.  With shapes, you also have control over the contour or line that composes the shape, like so:

  sf::CircleShape circleShape(200);
  circleShape.setFillColor(sf::Color::Blue);
  circleShape.setPosition(50.0f, 50.0f);
  circleShape.setOutlineThickness(10);
  circleShape.setOutlineColor(sf::Color::Green);

Here we set the line width to 10pixels and the color to green, resulting in:

image

 

As you may have noticed from the inheritance diagram earlier, there exist other shapes, ConvexShape and RectangleShape.  RectangleShape should be pretty self explanatory, bull ConvexShape requires a touch of explanation.  The Convex shape represents a convex hull, which is to say a closed shape of lines that never intersect or exceed 180 degrees.  You define a ConvexShape as a series of points, but it is important that you draw your shape in either a clockwise or counter-clockwise order so SFML knows how to draw it.  Here is an example creating a ConvexShape:

  sf::ConvexShape convexShape;
  convexShape.setPointCount(5);
  convexShape.setFillColor(sf::Color(147, 112, 227)); // PURPLE!
  convexShape.setOutlineThickness(3.0f);
  convexShape.setOutlineColor(sf::Color::White);
  convexShape.setPoint(0, sf::Vector2f(0.0f, 0.0f));
  convexShape.setPoint(1, sf::Vector2f(renderWindow.getSize().x, 0.0f));
  convexShape.setPoint(2, sf::Vector2f(renderWindow.getSize().x, renderWindow.getSize().y));
  convexShape.setPoint(3, sf::Vector2f(renderWindow.getSize().x / 2, renderWindow.getSize().y / 2));
  convexShape.setPoint(4, sf::Vector2f(0, renderWindow.getSize().y));


Resulting in

image

 

That’s it for basic graphics and shape rendering.  In the next tutorial we will expand upon this graphics knowledge and start looking at using sprites.

 

The Video

 

Programming , , ,

14. October 2015

 

Tiled is a popular open source map editor for creating 2D maps using tiled graphics.  I have used Tiled in several previous tutorials and no doubt will be using Tiled in several future series, so instead of covering it partially over and over again, I decided to do a Tiled video tutorial series that I will simply link to.  This is that tutorial series.  As I add new tutorials I will link them below.

 

Right now the contents consist of:

 

An earlier text based introduction to Tiled from GameFromScratch is available here.

If you are interested in learning out to make isometric tiles in Blender check this tutorial.

 

Previous tutorials that have used Tiled include:

 

Direct version of the videos embedded below:

Art, Programming , ,

13. October 2015

 

The world of game engines is dominated by the two giants mentioned above, Unity and Unreal.  Both have huge bank accounts, huge communities and hundreds of ship titles behind them.  They are by no means alone, there are a number of other commercial 3D engines such as Havok’s Vision Engine, Gamebryo, CryEngine, Hero Engine, Autodesk’s Stingray and more, to say nothing of the many and varied low/no cost and open source options out there.  You’d have to be absolutely nuts to enter the commercial 3D AAA game engine space at this point wouldn’t you?  The folks over at MaxPlay obviously don’t think so.

 

Spun off from Technicolor, MaxPlay is a well funded start up formed from several industry veterans.  Their focus seems to be on cloud based collaboration of disparate teams.

 

Details are fairly vague at this point, but a lot can be gleaned by this Venture Beat article.

 

From that article:

MaxPlay was founded to define and engineer a new software platform to build and operate high-quality games in today’s increasingly complex, global, multi-platform environment.

“Bottomline, we need to help game developers find fun faster, and then be able to continuously optimize that fun for their users,” said Bain.

Starting two years ago, MaxPlay went about building a extensible service-oriented architecture that is built on an asset-driven database structure that fully leverages the cloud. That means that people from all parts of a game company — from artists to programmers — can work together simultaneously in the same project. The MaxPlay engine also takes advantage of whatever computing resources are available, whether it’s a single core, or computing brain, or many different ones. You can preview changes in third-party tools, such as the Maya content creation tool, in the runtime, so you can see instantaneously how changes to art might turn out.

And

The tools use modern analytics that instrument the prototype so that developers can know exactly what is going on at any given time. In the landscape for a “tower defense” game that I viewed, it was easy to see from the analytics that the tanks were moving too fast and that they had to be slowed down so that the defenders had a chance to stop them. The developers fixed the problem on the fly, slowing the tanks using a multithreaded script editor. The developer made the change in Austin, Texas, where MaxPlay has an office, and they showed up live at the demo in San Francisco.

 

Details, including pricing, are extremely vague at this point.  There is a blog but it is virtually empty.  You can sign up on the homepage to receive more information as it becomes available.  I am always interested on new game engines, especially from a tutorial perspective, so I will keep an eye on how this one develops.

Programming ,

12. October 2015

 

In our prior tutorial we created a simple application that showed the basics of event handling in SFML.  In this tutorial we are going to expand upon that concept a bit further and show how to read the keyboard, both using events and direct polling.  Although not strictly SFML related (anymore anyways), we are also going to be covering random number generators as part of our demonstration, a very common task in game development.

 

As always, there is an HD video version of this tutorial.

 

Earlier we used the RenderWindow method pollEvent() to check for and respond to the Closed event.  Now let’s look at the process of responding to keyboard events.

while (renderWindow.pollEvent(event)){
    //Handle events here
    if (event.type == sf::Event::EventType::Closed)
      renderWindow.close();

    //Respond to key pressed events
    if (event.type == sf::Event::EventType::KeyPressed){
      if (event.key.code == sf::Keyboard::Space){
        // Do something here
      }
    }
  }

 

As we can see, responding to a KeyPressed event is nearly identical to a Closed event.  The key difference is, once you have identified that the event is a keyboard event you have additional information available in the event’s key structure.  The value of key.code is a sf::Keyboard::Key enum for each available key.  In addition to the KeyPressed event, there is also a KeyReleased event that is sent when the key is, well, released.  By default KeyPressed will be fired again and again if held down.  If you would prefer to have only a single event fired for a key press you can change this behaviour using the RenderWindow setKeyRepeatEnabled().

 

This is an example of Event driven programming in which your program responds to events as they occurred.  Sometimes however you would rather poll for input then wait for it to occur.  For example you might want to ask “hey computer, what keys are pressed?” or “is the control key down?”.  Fortunately SFML supports this out of the box, like so:

  if (sf::Keyboard::isKeyPressed(sf::Keyboard::R))
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl))
      // Do something

 

This code snippet first checks if the ‘R’ key is currently pressed.  It then checks to see if the Control key is pressed, as you can see in the example above the left and right control keys are separate events.  You can mix and match event driven and polled input handling however you want, but be careful not to get in a situation where you are accidentally handling the same input multiple times.

 

We are going to create a full example in a second that puts all of these processes together by randomly changing the screen color based on keys being pressed.  First, lets cover that process of creating a random number, a very common process in game development.  In fact, random number generation used to be part of SFML, but as of SFML 2.0 it was removed, partly because it didn’t make sense and partly because it’s now well entrenched in the language in a cross platform manner.

 

There are two ways we can generate random numbers, using the older C style, or the more modern C++ 11 style.  The new way is more appropriate for generating truly random numbers for security reasons and has no global state, but the old C style way should be more than sufficient for game development, so pick the way you like best.  The process is very similar… you generate a sequence of random numbers using some form of seed value, then massage the results into the numeric range you want.  Let’s look first at the C way of generating randoms:

  srand(time(NULL));               //seed random number generator with the current time
  auto randomNumber = rand() % 255;//generate a random number then confine it to a value of 0-255.

The first call sends the random number generator using the current system time.  Then we call rand() to get a random number.  One important thing to be wary off, the seeding process is fairly slow, while the rand() call is slightly lighter weight.  Next we look at the C++ way:

  std::uniform_int_distribution<int> randomColorRange(0, 255);
  std::random_device rd;
  std::mt19937 randomNumbers(rd());
  auto randomVal = randomColorRange(randomNumbers);

This example does basically the same thing as the earlier version.  The uniform_int_distribution object is a lightweight object that can be used to massage the data generated by mt19937 algorithm into actual int values within the defined range.  The mt19937 is an implementation of Mersenne Twister algorithm for generating random numbers.  The random_device is a built in object for creating a seed for your random number generator instead of using the current time ( although it’s quite possible that random_device uses the time behind the scenes ).  Using random_device is heavy weight, as is mt19937, so be careful where you generate random numbers.  However using those generated numbers with a uniform_int_distribution is light weight.

 

Ok, so we now know how to respond to keyboard events, poll the keyboard directly and generate random numbers, let’s tie it all together in a single example.

// This example demostrates the main loop

#include "SFML/Graphics.hpp"
#include <iostream>
#include <random>

int main(int argc, char ** argv) {
  sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "SFML Demo");

  sf::Event event;

  // A Clock starts counting as soon as it's created
  sf::Color color(sf::Color::Red);


  // C++ 11 way of generating a random between 0 - 255
  // mt19937 is an implementation of the Mersenne Twister pseudo random number generator
  // random_device() returns a random number to use as a seed for the mt algorithm... slow however so that's why we dont just use it for all randoms if you were wondering
  // mt results arent in a human friendly format, so we use uniform_int_distribution to "shape" the results to our range and type
  // uniform_int_distribution is a fairly "light" object.  random_device and mt19937 aren't.
  std::uniform_int_distribution<int> randomColorRange(0, 255);
  std::random_device rd;
  std::mt19937 randomNumbers(rd());
  
  // Pre-C++ 11 but more common way (*with issues, see:
  //https://www.reddit.com/r/programming/comments/1rnudl/quite_interesting_why_cs_rand_is_considered/
  // Mostly doesn't apply to game devs if not using rand for say... security.
  
  /*
  srand(time(NULL));          //seed random number generator with the current time
  auto randomNumber = rand() % 255;   //generate a random number then confine it to a value of 0 - 255.
  */

  while (renderWindow.isOpen()){
    // Check for all the events that occured since the last frame.
    while (renderWindow.pollEvent(event)){
      //Handle events here
      if (event.type == sf::Event::EventType::Closed)
        renderWindow.close();

      //Respond to key pressed events
      if (event.type == sf::Event::EventType::KeyPressed){
        if (event.key.code == sf::Keyboard::Space){
          color.r = randomColorRange(randomNumbers);
        }
      }
    }

                      
    // Now demonstrate input via polling
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::R))
      if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl))
        color.r = 0;
      else
        color.r = randomColorRange(randomNumbers);

    else if (sf::Keyboard::isKeyPressed(sf::Keyboard::G))
      if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl))
        color.g = 0;
      else
        color.g = randomColorRange(randomNumbers);
    else if (sf::Keyboard::isKeyPressed(sf::Keyboard::B))
      if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl))
        color.b = 0;
      else
        color.b = randomColorRange(randomNumbers);

    

    renderWindow.clear(color);
    renderWindow.display();
  }

}

 

All we are doing in this example is setting the clear color of the RenderWindow based on keyboard input.  On pressing the spacebar we handle this using event driven input and set the Red channel of our clear color to a random value between 0-255.  A value of 0 means no red, while 255 is fully red.  We then directly poll the keyboard for input look to see if the R/G/B keys are pressed.  If they are, we assign that color channel to a random value.  If we also have the control key pressed, we instead set that channel to 0.  Therefore pressing CTRL+R, CTRL+G, CTRL+B will result in a black window.

 

As you can see, SFML makes it simple to handle keyboard input, both using an event driven model or by polling.  One thing it does not provide however is any sense of history.  We can check if a key was pressed or released during a given frame but we can’t really track the history a very common requirement.  That said, rolling your own solution is very simple, as shown below:

// This example demostrates the main loop

#include "SFML/Graphics.hpp"
#include <unordered_map>
#include <iostream>

int main(int argc, char ** argv) {
  sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "SFML Demo");

  sf::Event event;

  // If true, you will continue to receive keyboard events when a key is held down
  // If false, it will only fire one event per press until released
  renderWindow.setKeyRepeatEnabled(false);

  std::unordered_map<int, bool> keys;
  std::list<int> changedKeys;

  while (renderWindow.isOpen()){
    changedKeys.clear();

    while (renderWindow.pollEvent(event)){
      if (event.type == sf::Event::EventType::Closed)
        renderWindow.close();

      if (event.type == sf::Event::EventType::KeyPressed){
        if (keys.count(event.key.code) == 0){
          keys[event.key.code] = true;
          changedKeys.push_back(event.key.code);
        }
      }
      if (event.type == sf::Event::EventType::KeyReleased){
        if (keys.count(event.key.code) == 1){
          keys.erase(event.key.code);
          changedKeys.push_back(event.key.code);
        }
      }
    }

    std::cout << "Currently pressed keys: ";

    // To get the actual value as a string, you need to use Thor or write your own version
    for (auto& keyValue : keys)
      std::cout << keyValue.first << " ";
    std::cout << std::endl;

    if (!changedKeys.empty()){
      std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Keys changed" << std::endl;
    }
    renderWindow.clear();
    renderWindow.display();
  }

}


This approach is pretty typical of SFML.  The basic tools are there for you, but you may have to build a layer on top to fit your game.  That said, there is a library called Thor that covers many of these common scenarios, essentially a utility library built over top of SFML providing additional functionality.  For example, in addition to providing the kind of functionality we just implemented above, there is also a utility for turning SFML Key enum values into a printable format.  We will probably cover using at least parts of Thor at a later point in this series, but for now I want to focus on core SFML.

 

The Video


Programming , , ,

Month List

DisqusCommentsSummary

Course-specific creative-The Complete iOS 9 Developer Course - Build 18 Apps