Skip to main content
GameDev.net gamedev.net
🔒 Locked

A complete C++ game from scratch using SFML

Started by Serapth Sep 8, 2011 at 5:00 PM 53 replies 52k views
Original Post
Serapth
Serapth
It seems like no matter how much you tell people to not start using C++, many do anyways. That said, I have noticed one thing, many if not most of the tutorials the people are going to start with are complete crap. The vast majority are written as if it was still 1983 and when you see some of the code people post in these forums, you can tell these tutorials are having a negative effect. Now, a simple tutorial cannot be a replacement for a solid study or even a short book. What it can do though, is be a hell of a supplement.

So, I have put together Game From Scratch -- C++ Edition which documents the development of a simple game from the very beginning till the end (using SFML). Instead of being constrained to the small spaces allocated to a book or site sample, I can go on and on and on, and at times, I do! Consider it part example game, part programming primer and part soapbox. I am not going to teach you to program, you will need a understanding of the very basics at least, but hopefully I will teach you how to program a little bit better.

It's fairly early on, only five "chapters" thus far, but I think you will find even what is there useful if you are just starting out. Of course, I will be updating and adding content on a nearly daily basis. Although the game is simple, I am coding it in such a way that the practices should be transferable to a project of just about any size.

To the more veteran minded among us, if you see and glaring errors or mistakes, please point them out to me and I will address them... a tutorial isn't much good if its teaching incorrect material! On occasion I do things "badly" to introduce a concept later, but I generally will acknowledge when I do so. On occasion too, I choose clarity over functionality, at least clarity in my opinion.

So if you are just starting out programming, check it out. If you are a wizened master and see a mistake, let me know. I also take reasonable pride in my ability to communicate in English, so if you see glaringly awful abuses of the English language, please let me know!

Cheers and hopefully it's helpful.
Serapth
Serapth
Oh, and there are a ton of links(mostly for individual source downloads) and hopefully none of them are broken, if you encounter a broken link, please let me know.

Also, you may notice a bit of a bug, in that the splashscreen and mainmenu don't handle window close events. This is actually intentional at this point.
pulpfist
pulpfist
Nice looking tutorial.
I took a quick look at chapter 4 and one thing that struck me was that each Visual object contains a sf::Image rather than a pointer/reference to a sf::Image.
Wouldn't it be a good idea to separate the images from that class, making it a "global" resource instead? Or at least explain for the reader that storing the image data in each visual object could become a waste of resources in the long run?
Serapth
Serapth

Nice looking tutorial.
I took a quick look at chapter 4 and one thing that struck me was that each Visual object contains a sf::Image rather than a pointer/reference to a sf::Image.
Wouldn't it be a good idea to separate the images from that class, making it a "global" resource instead? Or at least explain for the reader that storing the image data in each visual object could become a waste of resources in the long run?



Warning, spoilers...

When I introduce the second paddle, which uses the same Image, the behaviour of this code is going to be changed. This is an example of code that's going to change to illustrate a concept, something ill do quite often.


That said, yes, it's is wasteful as soon as the relationship between sprites and images isn't one to one. At this point though, it's not wasteful.
Codarki
Codarki
I don't quite like your first introduction to classes. Also, I don't agree with your reasons to do so. Free functions are part of the a class's public interface in C++. If you don't need real class, then why not just use free functions. Free functions would be much softer landing for beginners.

You don't need to expose a singleton in header, nor global objects. If you need global access, just use free function which is global accessor.

Something like:

// Game.h
#pragma once

void StartGame();


// Game.cpp
#include "stdafx.h"
#include "Game.h"

enum GameState { Uninitialized, ShowingSplash, Paused,
ShowingMenu, Playing, Exiting };

GameState _gameState = Uninitialized;
sf::RenderWindow _mainWindow;

bool IsExiting()
{
if(_gameState == Exiting)
return true;
else
return false;
}

void GameLoop()
{
sf::Event currentEvent;
while(_mainWindow.GetEvent(currentEvent))
{

switch(_gameState)
{
case Playing:
{
_mainWindow.Clear(sf::Color(sf::Color(255, 0, 0)));
_mainWindow.Display();

if(currentEvent.Type == sf::Event::Closed)
{
_gameState = Exiting;
}
break;
}
}
}
}

void StartGame()
{
if(_gameState != Uninitialized)
return;

_mainWindow.Create(sf::VideoMode(1024, 768, 32),"Pang!");
_gameState = Playing;

while(!IsExiting())
{
GameLoop();
}

_mainWindow.Close();
}


I mean maybe it's not good to first teach everyone to use statics (or globals), then tell them to unlearn it when projects grow bigger and they run into problems. IMO all static and global instances belong into cpp file in C++, it's easier to get rid of them later.

Also I'm somewhat against private member functions. I'd rather like to see them as free functions (in anonymous namespace) inside the cpp file, or even extracted to implementation detail class.
pulpfist
pulpfist

[quote name='pulpfist' timestamp='1315502665' post='4859122']
Nice looking tutorial.
I took a quick look at chapter 4 and one thing that struck me was that each Visual object contains a sf::Image rather than a pointer/reference to a sf::Image.
Wouldn't it be a good idea to separate the images from that class, making it a "global" resource instead? Or at least explain for the reader that storing the image data in each visual object could become a waste of resources in the long run?



Warning, spoilers...

When I introduce the second paddle, which uses the same Image, the behaviour of this code is going to be changed. This is an example of code that's going to change to illustrate a concept, something ill do quite often.


That said, yes, it's is wasteful as soon as the relationship between sprites and images isn't one to one. At this point though, it's not wasteful.
[/quote]


Fair enough, but I would still mention it early.

Serapth
Serapth

I don't quite like your first introduction to classes. Also, I don't agree with your reasons to do so. Free functions are part of the a class's public interface in C++. If you don't need real class, then why not just use free functions. Free functions would be much softer landing for beginners.

You don't need to expose a singleton in header, nor global objects. If you need global access, just use free function which is global accessor.

Something like:

// Game.h
#pragma once

void StartGame();


// Game.cpp
#include "stdafx.h"
#include "Game.h"

enum GameState { Uninitialized, ShowingSplash, Paused,
ShowingMenu, Playing, Exiting };

GameState _gameState = Uninitialized;
sf::RenderWindow _mainWindow;

bool IsExiting()
{
if(_gameState == Exiting)
return true;
else
return false;
}

void GameLoop()
{
sf::Event currentEvent;
while(_mainWindow.GetEvent(currentEvent))
{

switch(_gameState)
{
case Playing:
{
_mainWindow.Clear(sf::Color(sf::Color(255, 0, 0)));
_mainWindow.Display();

if(currentEvent.Type == sf::Event::Closed)
{
_gameState = Exiting;
}
break;
}
}
}
}

void StartGame()
{
if(_gameState != Uninitialized)
return;

_mainWindow.Create(sf::VideoMode(1024, 768, 32),"Pang!");
_gameState = Playing;

while(!IsExiting())
{
GameLoop();
}

_mainWindow.Close();
}


I mean maybe it's not good to first teach everyone to use statics (or globals), then tell them to unlearn it when projects grow bigger and they run into problems. IMO all static and global instances belong into cpp file in C++, it's easier to get rid of them later.

Also I'm somewhat against private member functions. I'd rather like to see them as free functions (in anonymous namespace) inside the cpp file, or even extracted to implementation detail class.


This is actually one of those area's ( actually the area ) I most hummed and hawed about if I was introducing needless complexity or not. However, as time goes on, there actually will be some (justified) global instances with Game:: being the sole point of contact. I don't actually swear users off them, as frankly in a few cases, they make sense. ( I have also grown to dislike how I named Gameloop )


I am curious though why you are anti private member functions?
Serapth
Serapth

[quote name='Serapth' timestamp='1315503212' post='4859124']
[quote name='pulpfist' timestamp='1315502665' post='4859122']
Nice looking tutorial.
I took a quick look at chapter 4 and one thing that struck me was that each Visual object contains a sf::Image rather than a pointer/reference to a sf::Image.
Wouldn't it be a good idea to separate the images from that class, making it a "global" resource instead? Or at least explain for the reader that storing the image data in each visual object could become a waste of resources in the long run?



Warning, spoilers...

When I introduce the second paddle, which uses the same Image, the behaviour of this code is going to be changed. This is an example of code that's going to change to illustrate a concept, something ill do quite often.


That said, yes, it's is wasteful as soon as the relationship between sprites and images isn't one to one. At this point though, it's not wasteful.
[/quote]


Fair enough, but I would still mention it early.


[/quote]


I wonder if I can come up with a clean solution to mark code as "going to be improved later".

Codarki
Codarki

This is actually one of those area's ( actually the area ) I most hummed and hawed about if I was introducing needless complexity or not. However, as time goes on, there actually will be some (justified) global instances with Game:: being the sole point of contact. I don't actually swear users off them, as frankly in a few cases, they make sense. ( I have also grown to dislike how I named Gameloop )


I am curious though why you are anti private member functions?

Well, they clutter the public interface. Private member functions are implementation detail of the class, not related to the public interface. Sometimes they introduce new types to the header, so you have to either predeclare or include them. Causes recompilations when the implementation changes.

So basically the encapsulation is better without private member functions.

This is pretty recent realization for me, but I'm constantly more convinced about it. It's a fun experiment to take some random class with a lot of private functions, and then extract them to a new class as public functions. This new class can be visible only to the one class they're from, but this does clear up things. Also if you want to re-use the private functions in another class, no inheritance is required.
pulpfist
pulpfist

[quote name='pulpfist' timestamp='1315512274' post='4859195']
[quote name='Serapth' timestamp='1315503212' post='4859124']
[quote name='pulpfist' timestamp='1315502665' post='4859122']
Nice looking tutorial.
I took a quick look at chapter 4 and one thing that struck me was that each Visual object contains a sf::Image rather than a pointer/reference to a sf::Image.
Wouldn't it be a good idea to separate the images from that class, making it a "global" resource instead? Or at least explain for the reader that storing the image data in each visual object could become a waste of resources in the long run?



Warning, spoilers...

When I introduce the second paddle, which uses the same Image, the behaviour of this code is going to be changed. This is an example of code that's going to change to illustrate a concept, something ill do quite often.


That said, yes, it's is wasteful as soon as the relationship between sprites and images isn't one to one. At this point though, it's not wasteful.
[/quote]


Fair enough, but I would still mention it early.


[/quote]


I wonder if I can come up with a clean solution to mark code as "going to be improved later".


[/quote]

Yea... I though about that. It wouldn't be easy.
I was thinking about simple drawings and sketches. That way you might be able to tap into the readers subconsciousness without filling their heads with uninterpreted info too early.
pulpfist
pulpfist
Thinking about it, you could create a introduction chapter explaining the fundamental hardware in use in a simple game, like memory for example. For a complete beginner these things isn't so obvious, so a drawing of memory as a big buffer of bytes with linear addressing starting at zero, could be worth its weight in gold later on. The reader would intuitively map the information in later chapters back to those basic drawings, and fill in the overall map as they go. If the introduction chapter is simple and easy to digest, I think it could give a great boost for both you and for the reader in the learning process.

For instance, if you already explained and made some simple drawings of how and why a disk is extremely slow compared to memory (assuming we are not talking of SSD disks), the reader will understand why optimizing code that access files on disk would be rather pointless. And you don't even need to remind them, or explain why, when the time comes.

A simple explanation and sketch of memory would also be very valuable in several circumstances, like pointers, buffer overruns, dynamic allocation, etc.
I always wondered why teachers doesn't take their time to do this when they start a new programming course.
JMatser
JMatser
I noticed that all the switch statements do not have a default label. Maybe this would be a nice opportunity to introduce the concept of an assertion (something which is also missing from the project) in combination with the default label?

The assertions might also come in handy when for example loading an image. Do you really want to continue your code when you cannot load an image?
Serapth
Serapth

I noticed that all the switch statements do not have a default label. Maybe this would be a nice opportunity to introduce the concept of an assertion (something which is also missing from the project) in combination with the default label?

The problem is, in most of the switch statements, there are a few hundred events we aren't handling, so there isn't really anything I would actually do in the default, as that is not an assert-able condition. I will however look through the code and keep an eye in the future for a default: use, as it is a missing tidbit of info.


The assertions might also come in handy when for example loading an image. Do you really want to continue your code when you cannot load an image?
[/quote]


This is a very valid point.

Serapth
Serapth

Thinking about it, you could create a introduction chapter explaining the fundamental hardware in use in a simple game, like memory for example. For a complete beginner these things isn't so obvious, so a drawing of memory as a big buffer of bytes with linear addressing starting at zero, could be worth its weight in gold later on. The reader would intuitively map the information in later chapters back to those basic drawings, and fill in the overall map as they go. If the introduction chapter is simple and easy to digest, I think it could give a great boost for both you and for the reader in the learning process.

For instance, if you already explained and made some simple drawings of how and why a disk is extremely slow compared to memory (assuming we are not talking of SSD disks), the reader will understand why optimizing code that access files on disk would be rather pointless. And you don't even need to remind them, or explain why, when the time comes.

A simple explanation and sketch of memory would also be very valuable in several circumstances, like pointers, buffer overruns, dynamic allocation, etc.
I always wondered why teachers doesn't take their time to do this when they start a new programming course.


This actually sounds like it would be it's own entire tutorial arc! It would be interesting to put together a post specifically about the correlation of hardware and code, although in some ways this is the kind of info you want to isolate new developers from!
jpetrie
jpetrie
Moved to YA.
jpetrie
jpetrie
FB is for beginners to ask questions, not a place to post material targeted at beginners. YA, or the articles forum, or your journal are all better places for this kind of thread.
Serapth
Serapth
Part 5 is now live. It covers creating a game object manager class, as well as looks further into the standard C++ libraries, focusing on the incredibly handy std::map and std::for_each as well as introducing the concept of functors.

The code complexity has increased a bit from the previous 4 parts, so please let me know if I lost you or did a poor job explaining something. Again, obviously if you see any errors, let me know and I will address them.

Cheers.
Serapth
Serapth
So, I took wayyyyyyyyyyy too long in development, but PANG! part 6 is now live.

In this chapter we add user input, the game ball and address a couple new C/C++ features including protected and assert.

We aren't quite up to the level of a full game, but we are getting very close! In the next section we will handle collision detection. Hopefully I will be much faster in my next update!

Again, if you encounter any problems or have any questions, let me know!
Serapth
Serapth
All right, if part 6 took a long time, part 7 took a loooooooooooooooooooooooooooooooooooooooonnnnnngggggg time. But good things come to those that wait, no? ;)

In this chapter the ball gets moving, literally, we add collision detections and ricochets, while covering the topic of C++ asserts.


If you haven't started yet, the table of contents is right here.

Part 7 is right here. As always, full source is included at the end.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.