Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Creating a simple tilemap powered game mechanic  (Read 2647 times)

0 Members and 1 Guest are viewing this topic.

codewizard

  • Newbie
  • *
  • Posts: 9
    • View Profile
Creating a simple tilemap powered game mechanic
« on: January 21, 2014, 07:26:18 pm »
Hello,

I am currently doing a project (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 (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 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.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10826
    • View Profile
    • development blog
    • Email
Re: Creating a simple tilemap powered game mechanic
« Reply #1 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?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

codewizard

  • Newbie
  • *
  • Posts: 9
    • View Profile
Re: Creating a simple tilemap powered game mechanic
« Reply #2 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/) where implementing a 2d platformer is described as follows:
  • Decompose movement into X and Y axes, step one at a time. If you’re planning on implementing slopes afterwards, step X first, then Y. Otherwise, the order shouldn’t matter much. Then, for each axis:
  • Get the coordinate of the forward-facing edge, e.g. : If walking left, the x coordinate of left of bounding box. If walking right, x coordinate of right side. If up, y coordinate of top, etc.
  • Figure which lines of tiles the bounding box intersects with – this will give you a minimum and maximum tile value on the OPPOSITE axis. For example, if we’re walking left, perhaps the player intersects with horizontal rows 32, 33 and 34 (that is, tiles with y = 32 * TS, y = 33 * TS, and y = 34 * TS, where TS = tile size).
  • Scan along those lines of tiles and towards the direction of movement until you find the closest static obstacle. Then loop through every moving obstacle, and determine which is the closest obstacle that is actually on your path.
  • The total movement of the player along that direction is then the minimum between the distance to closest obstacle, and the amount that you wanted to move in the first place.
  • Move player to the new position. With this new position, step the other coordinate, if still not done.

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?

gabrieljt

  • Newbie
  • *
  • Posts: 18
    • View Profile
    • Email
Re: Creating a simple tilemap powered game mechanic
« Reply #3 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.

codewizard

  • Newbie
  • *
  • Posts: 9
    • View Profile
Re: Creating a simple tilemap powered game mechanic
« Reply #4 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).

 

anything