# Creating a platformer game in 30 minutes

## 2013/10/28

This post is the translation of my blog bost from habr.com. I used to spend a lot of time creating games (I loved platformers and pixelart especially) and learning how to code things, back to these days. I wrote seven blog posts in 2013-2014 describing something about games and mainly connected with C++, sometimes Lua, Java, Unity3D, based on my experience with this stuff.

This post was the most successful of this period and had rating +80 (a notable good post as for habr.com). Of course, skipping years, I wish I did some things another way, since my code and stuff isn’t good enough as it could be, but users liked it, and the post is of interest of myself, comparing what I was doing earlier and what I keep doing nowadays.

The post is actual as for 2013 and contains some unavoidable cyrillic letters on screenshots. But it’s not a problem, is it?

Hello! Today we will bake a platformer game, using C++, Box2D and SFML, as well as an editor of 2D game maps - Tiled Map Editor.

Here is the result (the map was created in 5 minutes + the game was working slow during the recording + the screen size isn’t such stretched - it is a Bandicam defect): YouTube video.

Source codes and a .exe file - at the end of the article.

## “What, where, when?”

### Box2D

We will use this library to simulate physics in the platformer (colliding with blocks, gravitation). Maybe it was too much to use the library just for blocks, but let’s have a fancy life. Why Box2D? Because it’s the most popular and free physical library.

### SFML

Why SFML? I wanted to use SDL at first, but it’s too limited in comparison to SFML, I’d had to invent some wheels by myself. Just thanks to the author of SFML for saved time! We use it to draw graphics.

### Tiled Map Editor

What does Tiled Map Editor do here? Have you ever tried to create maps for games? I’d bet your very first map looked as something like this.

Spoiler

1111111
1000001
1001001
1000011
1000111
1111111


It’s a pretty uneffective solution! It’s better to write something like map editor, but we all understand that it is a term constrution and harder than creating such “maps” as above.

Tiled Map Editor is one of such map editors. This is great and dope, and we can save every map created in the editor (consists of objects, tiles, its layers) in a XML-like .tmx file and then parse it via the special library in C++. Let’s dive in.

## Creating a map

Download TME from the official site. Create new map “File->Create…”

The orientation should be orthogonal (unless you make an isometric platformer), and the layer format XML, since we’ll be reading this format. By the way, you can’t change neither the layer format, nor the tile size in a created map.

### Tiles

Then we go to “Map->New tileset…”, load our tileset:

And now you must have something like this:

What is the matter of the tile layers? Almost every game has multi-layer maps. The first layer is earth (ice, grass, etc.), the second one are buildings (barracks, forts, city hall, etc., the background is transpared, by the way), the third one are trees (spruce, fir, etc., the background the same). That is, we draw the first layer firstly, then the second, and the third at last.

The process of creating layers is imprinted on the next 3 screenshots:

The list of the layers:

### Objects

What’s an object in terms of TME? An object has its name, type and value properties. This panel is responsible for objects

You can learn the meaning of every button by yourself. Then let’s try to create an object. Delete the layer named “Колобоша” (“Kolobosha”), create an object layer, you can use this name “Колобоша” again. Choose “Insert tile object” from the object panel (or you can choose a Shape to place), click on Kolobosha tile and place an object somewhere. Then click right mouse button on the object and then “Object properties…”. Change the object name to Kolobosha.

Then save the map.

In general, there are not hard points in map editors. It’s time to parse the map.

## Parsing maps

There are a cool library to parse XML files - TinyXML, load its sources.

Create a Visual Studio project. Include TinyXML files (or just paste them to the project excluding xmltest.cpp). Then link includies and lib-files of SFML in “Project->Properties”. If you don’t have a clue how to do this - welcome to Google.

Create Level.h for maps.

#ifndef LEVEL_H
#define LEVEL_H

#pragma comment(lib,"Box2D.lib")
#pragma comment(lib,"sfml-graphics.lib")
#pragma comment(lib,"sfml-window.lib")
#pragma comment(lib,"sfml-system.lib")

#include <string>
#include <vector>
#include <map>
#include <SFML/Graphics.hpp>


This is the beginning of the file.

Further we define the object structure.

struct Object
{
int GetPropertyInt(std::string name);
float GetPropertyFloat(std::string name);
std::string GetPropertyString(std::string name);

std::string name;
std::string type;
sf::Rect<int> rect;
std::map<std::string, std::string> properties;

sf::Sprite sprite;
};


Look at this. As I was saying, in TME every object may have properties. Properties are taken from a XML file, written to std::map, and then in can be obtained via the function GetProperty(…). name is the object name, type is its type, rect is the rectangle describing the object. At last, sprite is a sprite (an image) - is part of the tileset taken for the object. An object may don’t have sprite.

Then we have the layer structure which is a dead simple.

struct Layer
{
int opacity;
std::vector<sf::Sprite> tiles;
};


The layer has opacity (yeah, we can do a semitransparent layer!) and the list of tiles.

Then we have the level class:

class Level
{
public:
Object GetObject(std::string name);
std::vector<Object> GetObjects(std::string name);
void Draw(sf::RenderWindow &window);
sf::Vector2i GetTileSize();

private:
int width, height, tileWidth, tileHeight;
int firstTileID;
sf::Rect<float> drawingBounds;
sf::Texture tilesetImage;
std::vector<Object> objects;
std::vector<Layer> layers;
};

#endif


LoadFromFile loads the map from a file. This is the main part of Level class. GetObject returns the first object with given name, GetObjects returns the list of objects with given name. Well, it shall have been done via using the type of needed object, but it was more convenient for me to catch stuff using its name, because in the editor an object’s name is drawn above the object, but the type isn’t. Draw draws all the tiles (not objects!) taking a reference to a RenderWindow.

Then create Level.cpp:

#include "level.h"

#include <iostream>
#include "tinyxml.h"


Work with object structure at first

int Object::GetPropertyInt(std::string name)
{
return atoi(properties[name].c_str());
}

float Object::GetPropertyFloat(std::string name)
{
return strtod(properties[name].c_str(), NULL);
}

std::string Object::GetPropertyString(std::string name)
{
return properties[name];
}


Layer doesn’t need realization, go to Level:

bool Level::LoadFromFile(std::string filename)
{
TiXmlDocument levelFile(filename.c_str());

// Load an XML-map
{
std::cout << "Loading level \"" << filename << "\" failed." << std::endl;
return false;
}

// Use map container
TiXmlElement *map;
map = levelFile.FirstChildElement("map");

// An example of the map: <map version="1.0" orientation="orthogonal"
// width="10" height="10" tilewidth="34" tileheight="34">
width = atoi(map->Attribute("width"));
height = atoi(map->Attribute("height"));
tileWidth = atoi(map->Attribute("tilewidth"));
tileHeight = atoi(map->Attribute("tileheight"));

// Take the tileset description and the id of the first tile
TiXmlElement *tilesetElement;
tilesetElement = map->FirstChildElement("tileset");
firstTileID = atoi(tilesetElement->Attribute("firstgid"));

// source is the path to the image
TiXmlElement *image;
image = tilesetElement->FirstChildElement("image");
std::string imagepath = image->Attribute("source");

// Try to load the tileset
sf::Image img;

{
std::cout << "Failed to load tile sheet." << std::endl;
return false;
}

// Clear the color (109, 159, 185) out of the map
// In general, the tileset can have any color as background, but I didn't find a solution
// to parse a hex string like "6d9fb9" to the color
// Load the texture from the image
// Smooth is prohibited
tilesetImage.setSmooth(false);

// Get the amount of tileset rows and columns
int columns = tilesetImage.getSize().x / tileWidth;
int rows = tilesetImage.getSize().y / tileHeight;

// A vector consists of image rectangles (TextureRect)
std::vector<sf::Rect<int>> subRects;

for(int y = 0; y < rows; y++)
for(int x = 0; x < columns; x++)
{
sf::Rect<int> rect;

rect.top = y * tileHeight;
rect.height = tileHeight;
rect.left = x * tileWidth;
rect.width = tileWidth;

subRects.push_back(rect);
}

// Working with layers
TiXmlElement *layerElement;
layerElement = map->FirstChildElement("layer");
while(layerElement)
{
Layer layer;

// If we have the opacity property, then set the opacity, otherwise it won't be semitransparent
if (layerElement->Attribute("opacity") != NULL)
{
float opacity = strtod(layerElement->Attribute("opacity"), NULL);
layer.opacity = 255 * opacity;
}
else
{
layer.opacity = 255;
}

// <data> container
TiXmlElement *layerDataElement;
layerDataElement = layerElement->FirstChildElement("data");

if(layerDataElement == NULL)
{
std::cout << "Bad map. No layer information found." << std::endl;
}

// <tile> container - description of tiles of each layer
TiXmlElement *tileElement;
tileElement = layerDataElement->FirstChildElement("tile");

if(tileElement == NULL)
{
std::cout << "Bad map. No tile information found." << std::endl;
return false;
}

int x = 0;
int y = 0;

while(tileElement)
{
int tileGID = atoi(tileElement->Attribute("gid"));
int subRectToUse = tileGID - firstTileID;

// Set the TextureRect of each tile
if (subRectToUse >= 0)
{
sf::Sprite sprite;
sprite.setTexture(tilesetImage);
sprite.setTextureRect(subRects[subRectToUse]);
sprite.setPosition(x * tileWidth, y * tileHeight);
sprite.setColor(sf::Color(255, 255, 255, layer.opacity));

layer.tiles.push_back(sprite);
}

tileElement = tileElement->NextSiblingElement("tile");

x++;
if (x >= width)
{
x = 0;
y++;
if(y >= height)
y = 0;
}
}

layers.push_back(layer);

layerElement = layerElement->NextSiblingElement("layer");
}

// Working wih objects
TiXmlElement *objectGroupElement;

// If we have object layers
if (map->FirstChildElement("objectgroup") != NULL)
{
objectGroupElement = map->FirstChildElement("objectgroup");
while (objectGroupElement)
{
// <object> container
TiXmlElement *objectElement;
objectElement = objectGroupElement->FirstChildElement("object");

while(objectElement)
{
// Get all data - type, name, position, etc.
std::string objectType;
if (objectElement->Attribute("type") != NULL)
{
objectType = objectElement->Attribute("type");
}
std::string objectName;
if (objectElement->Attribute("name") != NULL)
{
objectName = objectElement->Attribute("name");
}
int x = atoi(objectElement->Attribute("x"));
int y = atoi(objectElement->Attribute("y"));

int width, height;

sf::Sprite sprite;
sprite.setTexture(tilesetImage);
sprite.setTextureRect(sf::Rect<int>(0,0,0,0));
sprite.setPosition(x, y);

if (objectElement->Attribute("width") != NULL)
{
width = atoi(objectElement->Attribute("width"));
height = atoi(objectElement->Attribute("height"));
}
else
{
width = subRects[atoi(objectElement->Attribute("gid")) - firstTileID].width;
height = subRects[atoi(objectElement->Attribute("gid")) - firstTileID].height;
sprite.setTextureRect(subRects[atoi(objectElement->Attribute("gid")) - firstTileID]);
}

// Create object structure
Object object;
object.name = objectName;
object.type = objectType;
object.sprite = sprite;

sf::Rect <int> objectRect;
objectRect.top = y;
objectRect.left = x;
objectRect.height = height;
objectRect.width = width;
object.rect = objectRect;

// Properties of the object
TiXmlElement *properties;
properties = objectElement->FirstChildElement("properties");
if (properties != NULL)
{
TiXmlElement *prop;
prop = properties->FirstChildElement("property");
if (prop != NULL)
{
while(prop)
{
std::string propertyName = prop->Attribute("name");
std::string propertyValue = prop->Attribute("value");

object.properties[propertyName] = propertyValue;

prop = prop->NextSiblingElement("property");
}
}
}

// Pass the object to the vector
objects.push_back(object);

objectElement = objectElement->NextSiblingElement("object");
}
objectGroupElement = objectGroupElement->NextSiblingElement("objectgroup");
}
}
else
{
std::cout << "No object layers found..." << std::endl;
}

return true;
}


The rest of Level functions:

Object Level::GetObject(std::string name)
{
// Only the first object with given name
for (int i = 0; i < objects.size(); i++)
if (objects[i].name == name)
return objects[i];
}

std::vector<Object> Level::GetObjects(std::string name)
{
// All objects with given name
std::vector<Object> vec;
for(int i = 0; i < objects.size(); i++)
if(objects[i].name == name)
vec.push_back(objects[i]);

return vec;
}

sf::Vector2i Level::GetTileSize()
{
return sf::Vector2i(tileWidth, tileHeight);
}

void Level::Draw(sf::RenderWindow &window)
{
// Draw all the tiles (DON'T draw the objects!)
for(int layer = 0; layer < layers.size(); layer++)
for(int tile = 0; tile < layers[layer].tiles.size(); tile++)
window.draw(layers[layer].tiles[tile]);
}


We’re done with Level.h!

Let’s test it. Create main.cpp and write:

#include "level.h"

int main()
{
Level level;

sf::RenderWindow window;
window.create(sf::VideoMode(800, 600), "Level.h test");

while(window.isOpen())
{
sf::Event event;

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

window.clear();
level.Draw(window);
window.display();
}

return 0;
}


The map can look as you want!

You can play with objects:

main.cpp

#include "level.h"
#include <iostream>

int main()
{
Level level;

Object kolobosha = level.GetObject("Kolobosha");
std::cout << kolobosha.name << std::endl;
std::cout << kolobosha.type << std::endl;
std::cout << kolobosha.GetPropertyInt("health") << std::endl;
std::cout << kolobosha.GetPropertyString("mood") << std::endl;

sf::RenderWindow window;
window.create(sf::VideoMode(800, 600), "Kolobosha adventures");

while(window.isOpen())
{
sf::Event event;

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

window.clear();
level.Draw(window);
window.display();
}

return 0;
}


Results:

Then you’re done with objects, it’s time for Box2D:

## Falling boxes

We want to create a 3D-action platformer game. There are objects on the map, namely player, enemy, block, or coins. We initialize the player, force him to do some commands via keyboard and have physics laws. Enemies walk here and there, push away the player if he’s too close and die if the player jumped on them. Blocks are fixed “in space” as static objects, the player can jump on them. Coins give nothing, just disapper when collided with the player.

Open main.cpp, erase all the code we wrote earlier, and write this:

#include "level.h"
#include <Box2D\Box2D.h>

#include <iostream>
#include <random>

Object player;
b2Body* playerBody;

std::vector<Object> coin;
std::vector<b2Body*> coinBody;

std::vector<Object> enemy;
std::vector<b2Body*> enemyBody;


Here we include level.h and Box2D.h. iostream is needed to output data to the console, random to generate the direction of enemy movement. Then we have the player and vectors - each enemy, coin and the player have own Object and b2Body (a body in terms of Box2D).

Warning - blocks have no body, because they’re active in the game only as physical objects, and don’t take part in the game logic.

Further:

int main()
{
srand(time(NULL));

Level lvl;

b2Vec2 gravity(0.0f, 1.0f);
b2World world(gravity);

sf::Vector2i tileSize = lvl.GetTileSize();


srand(time(NULL)) is needed for random.

Load the map, create b2World, passing gravity to this. Also, the gravity may have another direction, and direction from (0, 10) is stronger than (0, 1). Then we take the needed tile size.

Then we create block bodies:

	std::vector<Object> block = lvl.GetObjects("block");
for(int i = 0; i < block.size(); i++)
{
b2BodyDef bodyDef;
bodyDef.type = b2_staticBody;
bodyDef.position.Set(block[i].rect.left + tileSize.x / 2 * (block[i].rect.width / tileSize.x - 1),
block[i].rect.top + tileSize.y / 2 * (block[i].rect.height / tileSize.y - 1));
b2Body* body = world.CreateBody(&bodyDef);
b2PolygonShape shape;
shape.SetAsBox(block[i].rect.width / 2, block[i].rect.height / 2);
body->CreateFixture(&shape,1.0f);
}

bodyDef.type = b2_staticBody;


Blocks are static, they have no weight and fixed in space:

bodyDef.position.Set(block[i].rect.left + tileSize.x / 2 * (block[i].rect.width / tileSize.x - 1),
block[i].rect.top + tileSize.y / 2 * (block[i].rect.height / tileSize.y - 1));


Here we set block positions. If we set the same position as the object has, we will have a cruel error.

b2Body* body = world.CreateBody(&bodyDef);


Create the block body in world. We don’t have any work with the body later (and don’t save it anywhere).

b2PolygonShape shape;
shape.SetAsBox(block[i].rect.width / 2, block[i].rect.height / 2);


Each body has some shapes. I won’t go deep to this theme, because the blocks (and other bodies) are happy with a single rectangle.

body->CreateFixture(&shape,1.0f);


Link the shape with the body.

Then we do the same with enemies, coins and the player, with slight differencies:

	coin = lvl.GetObjects("coin");
for(int i = 0; i < coin.size(); i++)
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(coin[i].rect.left + tileSize.x / 2 * (coin[i].rect.width / tileSize.x - 1),
coin[i].rect.top + tileSize.y / 2 * (coin[i].rect.height / tileSize.y - 1));
bodyDef.fixedRotation = true;
b2Body* body = world.CreateBody(&bodyDef);
b2PolygonShape shape;
shape.SetAsBox(coin[i].rect.width / 2, coin[i].rect.height / 2);
body->CreateFixture(&shape,1.0f);
coinBody.push_back(body);
}

enemy = lvl.GetObjects("enemy");
for(int i = 0; i < enemy.size(); i++)
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(enemy[i].rect.left +
tileSize.x / 2 * (enemy[i].rect.width / tileSize.x - 1),
enemy[i].rect.top + tileSize.y / 2 * (enemy[i].rect.height / tileSize.y - 1));
bodyDef.fixedRotation = true;
b2Body* body = world.CreateBody(&bodyDef);
b2PolygonShape shape;
shape.SetAsBox(enemy[i].rect.width / 2, enemy[i].rect.height / 2);
body->CreateFixture(&shape,1.0f);
enemyBody.push_back(body);
}

player = lvl.GetObject("player");
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(player.rect.left, player.rect.top);
bodyDef.fixedRotation = true;
playerBody = world.CreateBody(&bodyDef);
b2PolygonShape shape; shape.SetAsBox(player.rect.width / 2, player.rect.height / 2);
b2FixtureDef fixtureDef;
fixtureDef.shape = &shape;
fixtureDef.density = 1.0f; fixtureDef.friction = 0.3f;
playerBody->CreateFixture(&fixtureDef);

bodyDef.fixedRotation = true;


This means that the body can’t rotate.

All bodies done, it’s time to initialize graphics!

	sf::Vector2i screenSize(800, 600);

sf::RenderWindow window;
window.create(sf::VideoMode(screenSize.x, screenSize.y), "Game");


Obvious code, creates a window with certain size and title.

	sf::View view;
view.reset(sf::FloatRect(0.0f, 0.0f, screenSize.x, screenSize.y));
view.setViewport(sf::FloatRect(0.0f, 0.0f, 2.0f, 2.0f));


Here we create a View for the window.

Why do we need this? To have a game in pixel art style, we multiply the screen size by 2 with using sf::View and all images are drawn with 2 times wider width and height.

    while(window.isOpen())
{
sf::Event evt;

while(window.pollEvent(evt))
{
switch(evt.type)
{
case sf::Event::Closed:
window.close();
break;


The window closes by pressing the red cross. We had the same code earlier.

			case sf::Event::KeyPressed:
if(evt.key.code == sf::Keyboard::W)
playerBody->SetLinearVelocity(b2Vec2(0.0f, -15.0f));

if(evt.key.code == sf::Keyboard::D)
playerBody->SetLinearVelocity(b2Vec2(5.0f, 0.0f));

if(evt.key.code == sf::Keyboard::A)
playerBody->SetLinearVelocity(b2Vec2(-5.0f, 0.0f));
break;


This is more interesting. We add speed to the player by pressing WAD keys.

world.Step(1.0f / 60.0f, 1, 1);


We update the Box2D world here. The first argument takes the frequency of updating (every 160 seconds), and the numbers of velocityIterations and positionIterations. The higher the last two values, the more realistic physics the game has. Since we don’t have any hard shapes like in Angry Birds, only rectangles, we’re okay with a single iteration.

for(b2ContactEdge* ce = playerBody->GetContactList(); ce; ce = ce->next) { b2Contact* c = ce->contact;


Here we work with collisions between the player and other bodies.

cpp
for(int i = 0; i < coinBody.size(); i++)
if(c->GetFixtureA() == coinBody[i]->GetFixtureList())
{
coinBody[i]->DestroyFixture(coinBody[i]->GetFixtureList());
coin.erase(coin.begin() + i);
coinBody.erase(coinBody.begin() + i);
}


Collisions with coins. If a money collided with the player, it just removed from the vectors.

			for(int i = 0; i < enemyBody.size(); i++)
if(c->GetFixtureA() == enemyBody[i]->GetFixtureList())
{
if(playerBody->GetPosition().y < enemyBody[i]->GetPosition().y)
{
playerBody->SetLinearVelocity(b2Vec2(0.0f, -10.0f));

enemyBody[i]->DestroyFixture(enemyBody[i]->GetFixtureList());
enemy.erase(enemy.begin() + i);
enemyBody.erase(enemyBody.begin() + i);
}


If an enemy collided with the player, we check if the player above the enemy or not. If yes, then the enemy removes, and the player springs up.

Otherwise, the player pushed away from the enemy.

					else
{
int tmp = (playerBody->GetPosition().x < enemyBody[i]->GetPosition().x)
? -1 : 1;
playerBody->SetLinearVelocity(b2Vec2(10.0f * tmp, 0.0f));
}
}
}


The player moves either to the left or to the right depending on his current position relatively to the enemy.

		for(int i = 0; i < enemyBody.size(); i++)
{
if(enemyBody[i]->GetLinearVelocity() == b2Vec2_zero)
{
int tmp = (rand() % 2 == 1) ? 1 : -1;
enemyBody[i]->SetLinearVelocity(b2Vec2(5.0f * tmp, 0.0f));
}
}


If the speed of an enemy is equal to 0, then we give him more speed - he goes either to the right, or to the left. It is a throw-like movement.

		b2Vec2 pos = playerBody->GetPosition();
view.setCenter(pos.x + screenSize.x / 4, pos.y + screenSize.y / 4);
window.setView(view);


Work with graphics. Get the player position, change the view center and use our view.

		player.sprite.setPosition(pos.x, pos.y);

for(int i = 0; i < coin.size(); i++)
coin[i].sprite.setPosition(coinBody[i]->GetPosition().x, coinBody[i]->GetPosition().y);

for(int i = 0; i < enemy.size(); i++)
enemy[i].sprite.setPosition(enemyBody[i]->GetPosition().x, enemyBody[i]->GetPosition().y);


Set b2Body positions to the sprites (for the player, coins and enemies).

        window.clear();

lvl.Draw(window);

window.draw(player.sprite);

for(int i = 0; i < coin.size(); i++)
window.draw(coin[i].sprite);

for(int i = 0; i < enemy.size(); i++)
window.draw(enemy[i].sprite);

window.display();


Clear the window, draw here tiles of the map, then the player, coins and enemies, and then display the window.

    }

return 0;
}
`

It’s done!

An example map:

## Sources

https://github.com/Izaron/Platformer

## Thanks

… to the authors of this topic.

… to the authors of Box2D

… to the authors of TinyXml