SFML community forums

Help => General => Topic started by: codewizard on January 21, 2014, 07:26:18 pm

Title: Creating a simple tilemap powered game mechanic
Post by: codewizard on January 21, 2014, 07:26:18 pm
Hello,

I am currently doing a project (https://github.com/offset/draw-it (https://github.com/offset/draw-it)) where I'm developing an application which is able to extract lines of an image and which then uses this information to create a game with the lines being the level walls. I already got the first part some time ago, but the part with the game (where I'm using SFML) really, really troubles me. (mainly because I haven't done any game programming before)
I rudimentary know how an SFML game works (this whole game loop stuff) and also have some idea how a tilemap powered game works, but I am just unable to get it into my code. The difficulty is that the level's content (which is stored in an std::vector<std::vector<int> >) is generated dynamically.
To generate the tilemap I use a modification of the code on http://www.sfml-dev.org/tutorials/2.1/graphics-vertex-array.php (http://www.sfml-dev.org/tutorials/2.1/graphics-vertex-array.php) (with vertices).
The update function looks like this:
void Game::update(sf::Time deltaTime)
{
    sf::Vector2f velocity(0.f, 0.f);
   
    if(isMovingLeft)
    {
        velocity.x -= 1.f * 5.f;
    }
    if(isMovingRight)
    {
        velocity.x += 1.f * 5.f;
    }
    if(isJumping && !hasBeenJumping)
    {
        velocity.y -= 3.f * 5.f;
    }
   
    if((player.getPosition().x + velocity.x) >= Play::getInstance()->getMapSize().x)
    {
        velocity.x = Play::getInstance()->getMapSize().x-1;
    }
    if((player.getPosition().x + velocity.x) <= 0)
    {
        velocity.x = 1;
    }
    if((player.getPosition().y + velocity.y) >= Play::getInstance()->getMapSize().y)
    {
        velocity.y = Play::getInstance()->getMapSize().y-1;
    }
    if((player.getPosition().y + velocity.y) <= 0)
    {
        velocity.y = 1;
    }
   
    // gravity
    velocity.y += 9.81;
   
    player.move(velocity * deltaTime.asSeconds());
   
    view.setCenter(player.getPosition().x, player.getPosition().y);
}
As you can see, there is no collision detection. I coulndn't figure out on how to implement it.
The whole code is on this githup repository (https://github.com/offset/draw-it (https://github.com/offset/draw-it) and I would really, really appreciate it if you could concrete tips on how to make this a game, because I need to get this done in the next 2-3 weeks.

P.S.: The game in my application just needs to be a very, very basic game.

P.P.S.: The relevant classes in my reposity are play, tilemap, game and entities.

To make it clear: I don't want you to do my work, but I'm currently stuck in a mental hole and have no clue how to get out.
Title: Re: Creating a simple tilemap powered game mechanic
Post by: eXpl0it3r on January 22, 2014, 01:31:52 am
You need to ask specific questions if you want answers. To "How do I make this game?" nobody will really answer, because they don't want to spend their time figuring out at first what you want to actually achieve and after having that designing the game for you.

Where exactly are you stuck? What can't you understand? What is puzzling your brain?
Title: Re: Creating a simple tilemap powered game mechanic
Post by: codewizard on January 28, 2014, 05:08:30 pm
Ok, I see. Sorry for describing my problem(s) so confusing, I guess it was some kind of deadline panic  ;)

My original problem was that I couldnt figure out how to implement a collision detection in a tile map driven game with SFML and with the way I stored my level.
So, by now, I got something similar to a solution:
I found a website (higherorderfun.com/blog/2012/05/20/the-guide-to-implementing-2d-platformers/ (http://higherorderfun.com/blog/2012/05/20/the-guide-to-implementing-2d-platformers/)) where implementing a 2d platformer is described as follows:

I tried to implement it in my update() loop and it looks as follows (this is purely the collision detection, handling input and setting movement has been done before):
   
    // we calculate the x coords of the side of the player which is in the direction of the movement
    int coordFwdFEdgeX;
    // player.getPosition() yields the upper left corner
    if (movement.x < 0)
    {
        coordFwdFEdgeX = player.getPosition().x;
    } else
    {
        coordFwdFEdgeX = player.getPosition().x + playerSize.x;
    }
    // if the forward facing edge has negative x coords we don't check for collision because he then is out of the level
    if(coordFwdFEdgeX >= 0)
    {
        // with which line(s) of tiles (resp. rows) does the player collide?
        std::vector<int> collidingLinesX;
        if(movement.y < 0)
        {
            for(int i = player.getPosition().y; i >= static_cast<int>(player.getPosition().y+movement.y); --i)
            {
                // the tole size is 5
                collidingLinesX.push_back(i/5);
            }
        } else if (movement.y == 0)
        {
            collidingLinesX.push_back(player.getPosition().y);
        } else
        {
            for(int i = player.getPosition().y; i <= static_cast<int>(player.getPosition().y+movement.y); ++i)
            {
                collidingLinesX.push_back(i/5);
            }
        }
        // we scan along these lines for obstacles
        // the x coord of the closest obstacle is stored
        // at the moment it is a random number, later this number is used to make sure there has been a collision
        int closestObstacleX = 30111995;
        if(movement.x < 0)
        {
            for(uint i = 0; i < collidingLinesX.size(); ++i)
            {
                // each row I check every tile between the column of the player position and the column of the new player position
                for(int x =(coordFwdFEdgeX+movement.x)/5; x < (coordFwdFEdgeX/5); ++x)
                {
                    // levelMap contains the level data and is a vector<vector<int> >, it is stored as follows: levelMap[y][x]
                    // empty blocks are stored as 0
                    if(myLevelMap[collidingLinesX[i]][x] != 0)
                    {
                        // if there has been an obstacle the x position is stored
                        closestObstacleX = x*5;
                    }
                }
            }
            // if there has been a collision we only allow the player to move just before the obstacle
            if(closestObstacleX != 30111995)
            {
                movement.x = -(coordFwdFEdgeX - closestObstacleX);
            }
        } else
        // we do the same as before with a positive move
        {
            for(uint i = 0; i < collidingLinesX.size(); ++i)
            {
                for(int x = (coordFwdFEdgeX+movement.x)/5; x > (coordFwdFEdgeX/5); --x)
                {
                    if(Play::getInstance()->getPhysicsMap()[collidingLinesX[i]][x] != 0)
                    {
                        closestObstacleX = x*5;
                    }
                }
            }
            if(closestObstacleX != 30111995)
            {
                movement.x = closestObstacleX - coordFwdFEdgeX;
            }
        }
    }
    // now the y-axis
    // determining the side which faces the moving direction
    int coordFwdFEdgeY;
    if(movement.y < 0)
    {
        coordFwdFEdgeY = player.getPosition().y;
    } else
    {
        coordFwdFEdgeY = player.getPosition().y + playerSize.y;
    }
    // we don't want to try to access a negative value by index
    if(coordFwdFEdgeY >= 0)
    {
        // with which line(s) of tiles does the player collide?
        std::vector<int> collidingLinesY;
        if(movement.x < 0)
        {
            for(int i = player.getPosition().x; i >= static_cast<int>(player.getPosition().x+movement.x); --i)
            {
                collidingLinesY.push_back(i);
            }
        } else if(movement.x == 0)
        {
            collidingLinesY.push_back(player.getPosition().x);
        } else
        {
            for(int i = player.getPosition().x; i <= static_cast<int>(player.getPosition().x+movement.x); ++i)
            {
                collidingLinesY.push_back(i);
            }
        }
        // we scan along these lines for obstacles
        int closestObstacleY = 30111995;
        if(movement.y < 0)
        {
            for(int i = 0; i < collidingLinesY.size(); ++i)
            {
                for(int y = static_cast<float>(coordFwdFEdgeY+movement.y)/5; y < static_cast<float>(coordFwdFEdgeY)/5; ++y)
                {
                    if(Play::getInstance()->getPhysicsMap()[y][collidingLinesY[i]] != 0)
                    {
                        closestObstacleY = y*5;
                    }
                }
            }
            if(closestObstacleY != 30111995)
            {
                movement.y = closestObstacleY - coordFwdFEdgeY;
            }
        } else
        {
            for(int i = 0; i < collidingLinesY.size(); ++i)
            {
                for(int y = static_cast<float>(coordFwdFEdgeY+movement.y)/5; y > static_cast<float>(coordFwdFEdgeY)/5; --y)
                {
                    if(Play::getInstance()->getPhysicsMap()[y][collidingLinesY[i]] != 0)
                    {
                        closestObstacleY = y*5;
                    }
                }
            }
            if(closestObstacleY != 30111995)
            {
                movement.y = -(coordFwdFEdgeY - closestObstacleY);
            }
        }
    }
I hope the code is properly documentated and understandable. I really just tried to implement what the website described.
Unfortunately, the collision detection doesn't work, I can move right throught the solid blocks.
But interestingly, when I replace
 
for(int y = static_cast<float>(coordFwdFEdgeY+movement.y)/5; y > static_cast<float>(coordFwdFEdge[b]Y[/b])/5; --y)
in the check for the y obstacles with
 
for(int y = static_cast<float>(coordFwdFEdgeY+movement.y)/5; y > static_cast<float>(coordFwdFEdge[b]X[/b])/5; --y)
I get a a vertical obstacle detection, although the player sometimes get randomly teleported. The same doesn't apply for x.
Any hints?
Title: Re: Creating a simple tilemap powered game mechanic
Post by: gabrieljt on January 29, 2014, 04:09:03 am
Hmmm...

I am new to game development too and I am reading the SFML Game Development Book.

It is very enlightening.
Covers a lot of topics common to almost every game, including collision detection.

I recommend it.
Title: Re: Creating a simple tilemap powered game mechanic
Post by: codewizard on January 30, 2014, 10:58:38 pm
Hmmm...

I am new to game development too and I am reading the SFML Game Development Book.

It is very enlightening.
Covers a lot of topics common to almost every game, including collision detection.

I recommend it.

I also read this book (partially) and though I really liked it, the collision detection in it isn't applicable to my problem because I don't have several objects which need to be checked against each other, but I have a player and lots of potential obstacles (which are stored in a vector).