Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
2. June 2015

 

So a lot has happened in the land of SFML since I created my SFML tutorial series and I decided to check out what’s new.  I happen to be on my Macbook, so Visual Studio wasn’t an option.  Time for a bit of a confession… I hate Xcode, especially for C++ development.  I just find it to be a downright unpleasant, disorganized, unintuitive mess and C++ is a third class citizen.  The alternatives such as Code::Blocks never really appealed to me, and Qt Creator always seems to make you jump through half a hundred extra steps.

 

Thankfully CLion, a cross platform C++ IDE from JetBrains, the makers of IntelliJ, was recently released.  So I decided to kill two birds with one stone…  check out what’s new with SFML and evaluate CLion all at the same time.  This involves setting up the development environment and probably the area where most new C++ game developers fall flat on their faces.

 

So, if you are interested in developing using SFML on Mac using CLion… this post is for you!  We are going to walk through installing SFML and configuring your very first project.

 

Installing SFML

First we need to download and install SFML.  There are two ways to go about this…  first you can head to SFML downloads page, download the Mac OS zip package, extra it, and copy the files to the locations specified in the readme file...

 

… or you can use homebrew.  This is what I did.  Assuming you have homebrew installed, at a terminal simply type:

brew update
brew install sfml


In theory this will download and install all the requisite parts of SFML.  In theory.  If it fails, go back to the manual process.

 

Creating a new project in CLion 

I’m going to assume at this point you’ve already got CLion installed.  If not, it’s available here and can be installed as a 30 day trial.

Once installed, fire up CLion, it’s time for us to create a new project.

 

 

Ss1

 

Select new Project

 

Ss2

 

Name however and wherever you wish, just be sure to remember the location.

 

SS3

In the Project panel, locate CMakeList.txt and double click it.  This file is basically your CMake based project file, which CLion uses for it’s project format, as does SFML.

 

CMakeList.txt will open in the editor, replace with the follow text:

 

cmake_minimum_required(VERSION 3.2)
project(SFMLDemo)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

set(SOURCE_FILES main.cpp)
add_executable(SFMLDemo ${SOURCE_FILES})

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake_modules")
find_package(SFML REQUIRED system window graphics network audio)
if (SFML_FOUND)
    include_directories(${SFML_INCLUDE_DIR})
    target_link_libraries(SFMLDemo ${SFML_LIBRARIES})
endif()

 

If you’re going to screw up, this is the part you will screw up.  Let me point out the critical portions here.

 

Ss3 5

 

Make sure your project name is set correctly everywhere I boxed in red.  Also make sure your cmake_modules directory is set right...

 

Oh yeah, about that… make a directory called cmake_modules.  We need to copy this file (FindSFML.cmake) into that folder.  Be sure you save as Raw if downloading from Github and make sure the extension stays cmake, not cmake.txt as Mac OS is found of doing.  Or you could just create a new file called FindSFML.cmake in CLion and copy/paste the contents.  The choice is yours, but in the end your project should look like:

Ss5

 

 

Creating a Run Configuration

 

Now we are ready to run our application.  Select the menu Run->Edit Configuration...

 

Ss4

 

Be sure to set the working directory to you project folder ( or wherever you are going to put assets ), and chose your project as the executable, then hit Apply/OK.

 

You should now be able to run your application using the pulldown at the top of CLion, like so:

Ss6

Use the play icon to run, the bug icon to debug.

 

The default application should run and we should see:

Ss7

 

Now let’s try with some actual SFML code.  Replace the contents of main.cpp with the following:

#include <SFML/Graphics.hpp>

 

 

int main() {

 

    sf::RenderWindow window(sf::VideoMode(640,480,32),"Hello SFML");

 

    sf::Font font;

    font.loadFromFile("OpenSans-Bold.ttf");

 

    sf::Text text("Hello World",font,11);

    text.setCharacterSize(32);

    text.setPosition(window.getSize().x/2 - text.getGlobalBounds().width/2,

                     window.getSize().y/2 - text.getGlobalBounds().height/2);

 

 

    while(window.isOpen()){

 

        sf::Event event;

        while(window.pollEvent(event)) {

            if(event.type == sf::Event::Closed){

                window.close();

            }

 

            window.clear(sf::Color::Black);

            window.draw(text);

            window.display();

        }

    }

    return 0;

}

 

Finally we need the rtf file used in this example, I downloaded it from right here, and copied the file OpenSans-Bold.ttf to the root of our project directory. It is important this matches the run directory you specified earlier.  When run you should now see:

 

Ss8

 

 

There you go, a fully configured, really to run SFML project on MacOS, without an Xcode in sight!  There is one thing to be aware of though… right now the line:

find_package(SFML REQUIRED system window graphics network audio)

Is linking in every single SFML module, needed or not.


10. May 2015

 

SFML, the Simple and Fast Media Library, just release version 2.3.  SFML is a cross platform C++ based mostly 2D based game library built over top of OpenGL.  Although getting a bit long in the tooth, GameFromScratch created an SFML C++ game dev tutorial series if you are interested in learning more.

 

As to release 2.3, here are the announced changes:

 

SFML 2.3

General
  • Examples only link against sfml-main in release mode (#610, #766)
  • Replaced unsigned int with std::size_t for array indices and sizes (#739)
  • Fixed some issues with the Doxygen documentation (#750)
  • Added support for EditorConfig (#751)
  • Hide success message for CMake in quiet mode (#753)
  • Improved documentation for statuses with sf::Ftp (#763)
  • Moved stb_image into the extlibs directory (#795)
  • Changed the SOVERSION to major.minor (#812)
  • Fixed warnings about switch-statements (#863)
  • Added missing includes in the general headers (#851)
  • [Android] Updated toolchain file and dependencies (#791)
  • [Linux] Fixed missing pthread dependency (#794)
  • [OS X] Relaxed CMake installation rules regarding framework dependencies (#767)

 

Window
Features

 

Bugfixes
  • Fixed glXSwapIntervalSGI being broken for some driver implementations (#727, #779)
  • Fixed simultaneous context operations causing crashes on some AMD hardware (#778, #779)
  • Fixed joystick identification (#809, #811)
  • [iOS] Fixed various issues including stencil bits, device orientation and retina support (#748)
  • [iOS] Fixed inconsistency between sf::Touch::getPosition and touch events (#875)
  • [Linux] Fixed Alt+F4 not getting triggered in window mode (#274)
  • [Linux] Fixed Unix joystick stuff (#838)
  • [OS X] Fixed typo in JoystickImpl.cpp to prevent a crash (#762, #765)
  • [OS X] Fixed an issue in InputImpl::getSFOpenGLViewFromSFMLWindow (#782, #792)

 

Graphics
Features
  • Replaced GLEW with loader generated by glLoadGen (#779)
  • Added a new constructor to sf::Color that takes an sf::Uint32 (#722)
  • Updated stb_image to v2.02 (#777)
  • Updated FreeType to v2.5.5 (#799, #804)
  • Added checks for software OpenGL (#870)

 

Bugfixes
  • Fixed GL_ARB_compatibility not being detected (#859)
  • Fixed pixel format selection (#862)
  • Bumped back the OpenGL version requirement to 1.1 (#858)

 

Audio
Features
  • Dropped libsndfile and started using Vorbis, FLAC and OGG directly (#604, #757)
  • Added a FLAC file to the sound example (#815)

 

Bugfixes
  • Fixed access violation error in the destructor of sf::AudioDevice (#30, #602)
  • [OS X] Fixed threading issue with sf::SoundStream and OpenAL (#541, #831)

 

Network
Bugfixes
  • Fixed sf::TcpSocket not handling partial sends properly (#749, #796)

News


18. May 2012

 

 

At the request of a reader I put together a guide on how to build your SFML 1.6 project in release mode, then how to package it for distribution.

 

It’s part of the main tutorial series, but can be accessed directly here.  If you are looking into how to distribute a release version of your SFML game, be sure to check it out

 

 

In doing so, I ran into a NASTY C++ related problem, which I will mention in a post shortly!

Programming


4. May 2012

 

 

I have to say, Nathan does quick work, much faster than I do! Smile

 

As I said in this earlier post Nathan volunteered to port my SFML C++ tutorial to SFML 2.0.  Well, he is done.  So in addition to the 1.6 projects, you can now download source for SFML 2.0.

 

Keep in mind, the instruction are still for 1.6, that hasn’t changed, but you can now see code for either version.  Shortly I will edit the tutorial posts themselves to include the new download option, but for now you can download the 2.0 version sources here.  Part 8 does not include the FMOD sources, which were for demonstration purposes only and made the project quite a bit larger.

 

GameFromScratch C++ Edition SFML 2.0 downloads:

Part 6

Part 7

Part 8

Part 9

 

 

While he was porting, he ran into and reminded me of a bug that I encountered and Ninja-edited for future versions.  You may find that on occasion the ball disappears completely, at least in versions before part 9.  That was because GameBall used a counter from when the game started, instead of when play started.  So if you took a while to start the game, the ball could actually move on its own ( and off screen ).  The fix was a fairly simple flag I added in part 9.  For the fix details, take a look at the first few lines of GameBall::Update().

 

Thanks again Nathan.

Programming


14. March 2012

 

 

In part 1 we look at a very basic example of sending information across the wire from SFML to NodeJS.  In part 2 we looked at JSON encoding high score data, that is then sent via UDP socket to a Node server.  Now we complete the process, by having Node return high score data back to your C++ SFML app.  By the end of this section, you will have all the code you need for a functioning ( if somewhat fragile ) high score client and server.

 

 

First we look at the C++ code.  Things are very similar to part 2, except code has been refactored a bit for re-use.  Lets look now.

 

 

#include "SFML/Network.hpp" #include "JSON.h" #include <iostream> void MakePacket(const wchar_t* action, const wchar_t * name, const float score, sf::Packet &packet) { JSONObject data; data[L"action"] = new JSONValue(action); data[L"name"] = new JSONValue(name); data[L"score"] = new JSONValue(score); JSONValue * val = new JSONValue(data); data.clear(); std::wstring dataString = val->Stringify(); delete val; std::string notSoWide; notSoWide.assign(dataString.begin(),dataString.end()); packet.Append(notSoWide.c_str(),notSoWide.length()); } int main(int argc, char* argv[]) { sf::IPAddress ip("127.0.0.1"); sf::SocketUDP socket; sf::Packet packet; unsigned short port = 1000; unsigned short respPort = 1001; if(argc == 1) { //No arguments means program should retrieve scores and print them MakePacket(L"GetScores",L"",0.0f,packet); socket.Bind(respPort); socket.Send(packet,ip,port); char buffer[512]; // The buffer to store raw response data in sf::IPAddress respIP; // The ip address where the response came from size_t respSize; // The amount of data actually written to buffer // Now receive a response. This is a blocking call, meaning your program // will hang until a response is received. socket.Receive(buffer,512,respSize,respIP,port); socket.Close(); std::string respString(buffer,respSize); // Now lets turn the string back into JSON JSONValue * jsonHighScores = JSON::Parse(respString.c_str()); if(!jsonHighScores->IsObject()) { std::cout << "Something went wrong, not good."; return -1; } JSONObject root = jsonHighScores->AsObject(); if(root.find(L"Scores") != root.end() && root[L"Scores"]->IsArray()) { JSONArray scores = root[L"Scores"]->AsArray(); std::cout << "Current high scores:" << std::endl; for(int i = 0; i < scores.size();i++) { JSONObject curObj = scores[i]->AsObject(); std::wcout << "Name:" << curObj[L"Name"]->AsString(); std::cout << " High Score:" << curObj[L"Score"]->AsNumber() << std::endl; } } delete jsonHighScores; } else if(argc == 3) { MakePacket(L"AddScore", std::wstring(argv[1],argv[1] + strlen(argv[1])).c_str(), atof(argv[2]), packet); if(socket.Send(packet,ip,port) != sf::Socket::Done) { std::cout << "An error ocurred sending packet" << std::endl; } socket.Close(); } else { std::cout << "Invalid usage, proper format is player name then score, for example:" << std::endl; std::cout << "Or run with no arguments to get a list of scores returned" << std::endl; std::cout << "Scoreboard \"Player Name\" 42" << std::endl; return -1; } return 0; }

Click here to download Scoreboard.cpp

 

 

We have reorganized slightly to use a set of if/else’s based on the number of parameters passed in.  The common code between the two handled conditions has been moved to the method MakePacket(), which contains nothing new from last part.  It’s the section where argc == 1 ( which means there were no parameters specified, as argc at 1 represents the executables name ) that we are interested in.  If the user runs the application from the command line with no parameters, we want to fetch the high scores from the server.

 

 

The request process is the same, although we are passing the action GetScores instead.  One key difference is we Bind our port.  Think of this action as say “Yo!  This port is ours!”.  Only one application per computer can have access to a given port.  This is why we run our server on 1000, but then bind our response port on 1001, since client and server on running on the same machine.  Unlike when we add a score, for GetScores we want to listen for a response, which is what we do with socket.Receive().  Keep in mind, this action blocks, so your program wont be able to continue until this is done.  There are alternatives ( like Selector ) if this behavior is undesirable.

 

 

Now assuming Receive() worked correctly ( which in the Real World™ you shouldn’t!), buffer will be full of our JSON encoded string, while respSize will represent the amount of buffer that was actually used.  Using these two pieces of information, lets create a string from only the meaningful bits ( the rest of the buffer will be full of gibberish ).  We now turn our string back into JSON ( in production code, I would extend the JSON library to work with standard strings ), and check to see if it is a valid JSON object, error out if it’s not.  Now it’s a matter of parsing out the JSON into meaningful form.  Remember that a JSONObject is actually just a STL map of key/value pairs, so we find our array of type JSONArray named Scores.  Each item in that array is in turn another map, so we loop through them all find the value for “Name” and “Score”, turn them back into their native type and display them out to the console.  And that’s about it.

 

 

Now lets take a look at the server side of things.  Here we made much less invasive changes, so lets just look at what has changed.  To see the fully modified server.js click here.  I no doubt forgot a small change here or there, I always do!

 

 

First we add another condition to the switch statement as follows:

 

case "GetScores": console.log("Get Scores called"); // Turn highscores back into a JSON encoded string var highScoreAsString = JSON.stringify(highScores); // Create a buffer to hold that string var responseBuffer = new Buffer(highScoreAsString.length); // Write the string to the buffer responseBuffer.write(highScoreAsString); // Send it back to the client, using the addressing information // passed in via rinfo server.send(responseBuffer,0,responseBuffer.length, rinfo.port,rinfo.address, function(err, sent){ if(err) console.log("Error sending response"); else console.log("Responded to client at " + rinfo.address + ":" + rinfo.port ); }); break;

 

 

The code is pretty much documented, the nutshell version is, we turn our high score information into a JSON encoded string, make a buffer large enough for the string, copy the string to the buffer, then call send to the address and port the request came from, as defined in rinfo.

 

 

Then, mostly because we can, we use Node to create an extremely simple high score web server, so if you visit this site with your browser you can get a current list of high scores.  Look how laughably easy that task is!

 

 

// Now, lets show off a bit, with the worlds simplest web server. // Dynamically creates webpage showing current high scores. var webServer = require('http'); webServer.createServer(function(req,res){ res.writeHead(200, {'Content-Type': 'text/html'}); res.write("<html><body><h1>High Scores</h1><ul>"); for(i=0;i < highScores.Scores.length;++i) { res.write(highScores.Scores[i].Name + "&nbsp;&nbsp;" + highScores.Scores[i].Score + "<br />"); } res.write("</ul></body></html>"); res.end(); }).listen(80);

 

 

 

And… that’s it!  So now, lets take a look at things in action.  Open a command prompt and launch your server.js in node.  Then in another command prompt run scoreboard.exe, like this:

 

 

Here is using Scoreboard from the command line:

image

 

 

And here is the server handling these requests:

 

image

 

 

Finally, fire up a web browser and hit 127.0.0.1 ( assuming you don’t have another webserver running on your machine ):

 

image

 

 

It ain’t pretty, but it is a fully functioning high score server.  All you need to do is add a layer of security, harden things a bit with an iota of fault tolerance, pretty things up a bit and you are set.

 

 

As always, you can download the complete project zip here.

Programming


GFS On YouTube

See More Tutorials on DevGa.me!

Month List