## News:

11 March 2016 - Forum Rules

## A Simple 2D Collision Map Template for You (C++ and SFML)

Started by RetroRain, November 27, 2022, 12:11:23 AM

#### RetroRain

Hey guys.  Long time no see.

I just finished working on a simple 2D collision map system in C++, using the SFML graphics library.  The whole point of making this, is so that you can easily implement a collision system for your 2D games, without the need for any spatial partitioning.

I have found implementing collision detection to be the hardest part of game programming.

My system simply uses a little bit of math to get the tile coordinates and displaces the player accordingly.

If you want to make a game like Pacman or Zelda 1, use the 8x8 tile system.  If you want to make a game like Mario, Megaman, or Metroid, use the 16x16 tile system.

All you have to do is create and import your tilemap, select the tile mode based on the cell size of your tilemap, and create a simple 2D tilemap array.  The collision map is meant to be the backbone of your tilemap.

In other words, you will have two maps: One for your tiles (the tilemap), and one for the Collision Map (which is what this is), containing the tile properties, such as solids, spikes, water, etc.

This code is for anyone who knows C++ and SFML but has had a hard time with implementing 2D collision detection.

Another reason I made up this template, is so that I can convert it to 6502 assembly code when I go back to making a NES homebrew.

The code is in a single file.  All you have to do is copy and paste this into your IDE of choice!

NOTE: The minimal recommended player size is 16x16.  Too big, such as 64x64, will cause collision issues.

Here is the code.  Enjoy!

`#include <SFML/Graphics.hpp>#include <iostream>#include <vector>class Player{    public:        float x, y, width, height, quarterWidth, quarterHeight, hsp, vsp, hMove, vMove, moveSpeed;};//GlobalsPlayer player;int keyUp, keyDown, keyLeft, keyRight = 0;std::vector<std::vector<int>> map;int cellSize = 0;int numRows, numCols = 0;bool gridLines = false;void cellSizePrompt();void gridLinesPrompt();int getTile(float x, float y);int main(){    //Prompt user to enter in the cell size    cellSizePrompt();    //Prompt user to toggle grid lines on or off    gridLinesPrompt();    //Resize array based on the cell size    if (cellSize == 8)    {        map.resize(30, std::vector<int>(32, 0));        numRows = 30;        numCols = 32;    }    else    {        map.resize(15, std::vector<int>(16, 0));        numRows = 15;        numCols = 16;    }    //Place four solid tiles on the map    map[4][4] = 1;    map[4][11] = 1;    map[10][4] = 1;    map[10][11] = 1;    //Set up the window    sf::RenderWindow window(sf::VideoMode(256, 240), "");    window.setSize(sf::Vector2u(1024, 960));    window.setPosition(sf::Vector2i(440, 15));    window.setFramerateLimit(60);    //Initialize the player    player.x = 100;    player.y = 100;    player.width = 16;    player.quarterWidth = player.width / 4;    player.height = 16;    player.quarterHeight = player.height / 4;    player.hsp = 0;    player.vsp = 0;    player.hMove = 0;    player.vMove = 0;    player.moveSpeed = 1;    //Game loop    while (window.isOpen())    {        sf::Event event;        while (window.pollEvent(event))        {            if (event.type == sf::Event::Closed) window.close();            if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape) window.close();        }        //Input keys        keyUp = sf::Keyboard::isKeyPressed(sf::Keyboard::Up) * -1;        keyDown = sf::Keyboard::isKeyPressed(sf::Keyboard::Down);        keyLeft = sf::Keyboard::isKeyPressed(sf::Keyboard::Left) * -1;        keyRight = sf::Keyboard::isKeyPressed(sf::Keyboard::Right);        /////////Player horizontal movement        player.hMove = keyLeft + keyRight;        player.hsp = player.hMove * player.moveSpeed;        player.x += player.hsp;                /////////Player horizontal collision        //Horizontal collision from the left        if (player.hsp > 0)        {            if (getTile(player.x + player.width - 1, player.y) == 1 ||                getTile(player.x + player.width - 1, player.y + player.quarterHeight) == 1 ||                getTile(player.x + player.width - 1, player.y + (player.height / 2)) == 1 ||                getTile(player.x + player.width - 1, player.y + (player.height - player.quarterHeight)) == 1 ||                getTile(player.x + player.width - 1, player.y + player.height - 1) == 1)            {                int temp = int(player.x + player.width - 1) / cellSize;                temp = temp - (player.width / cellSize);                temp = temp * cellSize;                player.x = temp;            }        }        //Horizontal collision from the right        if (player.hsp < 0)        {            if (getTile(player.x, player.y) == 1 ||                getTile(player.x, player.y + player.quarterHeight) == 1 ||                getTile(player.x, player.y + (player.height / 2)) == 1 ||                getTile(player.x, player.y + (player.height - player.quarterHeight)) == 1 ||                getTile(player.x, player.y + player.height - 1) == 1)            {                int temp = int(player.x) / cellSize;                temp = temp + 1;                temp = temp * cellSize;                player.x = temp;            }        }        /////////Player vertical movement        player.vMove = keyUp + keyDown;        player.vsp = player.vMove * player.moveSpeed;        player.y += player.vsp;        /////////Player vertical collision        //Vertical collision from above        if (player.vsp > 0)        {            if (getTile(player.x, player.y + player.height - 1) == 1 ||                getTile(player.x + player.quarterWidth, player.y + player.height - 1) == 1 ||                getTile(player.x + (player.width / 2), player.y + player.height - 1) == 1 ||                getTile(player.x + (player.width - player.quarterWidth), player.y + player.height - 1) == 1 ||                getTile(player.x + player.width-1, player.y + player.height - 1) == 1)            {                int temp = int(player.y + player.height - 1) / cellSize;                temp = temp - (player.height / cellSize);                temp = temp * cellSize;                player.y = temp;            }        }        //Vertical collision from below        if (player.vsp < 0)        {            if (getTile(player.x, player.y) == 1 ||                getTile(player.x + player.quarterWidth, player.y) == 1 ||                getTile(player.x + (player.width / 2), player.y) == 1 ||                getTile(player.x + (player.width - player.quarterWidth), player.y) == 1 ||                getTile(player.x + player.width-1, player.y) == 1)            {                int temp = int(player.y) / cellSize;                temp = temp + 1;                temp = temp * cellSize;                player.y = temp;            }        }        window.clear();        //Draw the map        for (int i = 0; i < numRows; i++)        {            for (int j = 0; j < numCols; j++)            {                if (gridLines)                {                    if (map[i][j] == 0)                    {                        sf::RectangleShape rect(sf::Vector2f(cellSize, cellSize));                        rect.setPosition(j * cellSize, i * cellSize);                        rect.setFillColor(sf::Color::Transparent);                        rect.setOutlineThickness(-1);                        rect.setOutlineColor(sf::Color::Green);                        window.draw(rect);                    }                }                if (map[i][j] == 1)                {                    sf::RectangleShape rect(sf::Vector2f(cellSize, cellSize));                    rect.setPosition(j * cellSize, i * cellSize);                    rect.setFillColor(sf::Color::Transparent);                    rect.setOutlineThickness(-1);                    rect.setOutlineColor(sf::Color::Red);                    window.draw(rect);                }            }        }        //Draw the player        sf::RectangleShape rect(sf::Vector2f(player.width, player.height));        rect.setPosition(player.x, player.y);        rect.setFillColor(sf::Color::Transparent);        rect.setOutlineThickness(-1);        rect.setOutlineColor(sf::Color::Yellow);        window.draw(rect);                window.display();    }    return 0;}void cellSizePrompt(){    std::cout << "Please enter the cell size.  Type 8 for 8x8 or 16 for 16x16: ";    std::cin >> cellSize;    if (cellSize != 8 && cellSize != 16) cellSizePrompt();}void gridLinesPrompt(){    std::cout << "Grid lines on or off?  Type 0 for NO, or any other number for YES: ";    std::cin >> gridLines;}int getTile(float x, float y){    return (map[int(y)/cellSize][int(x)/cellSize]);}`

#### RetroRain

#1
I added two more collision points to all four sides of the player object, for a more robust collision detection.  The code has been updated in the first post.

Also, I updated the note.  The minimal recommended player size is 16x16.  Too big, such as 64x64, will cause collision issues.

And last but not least, a video and some screenshots of the template in action: