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

Author Topic: Tilemap collision with player  (Read 18697 times)

0 Members and 1 Guest are viewing this topic.

paulomag2106

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Tilemap collision with player
« Reply #15 on: January 06, 2014, 12:01:07 pm »
Oh, I see :D! So your Map2D class is somewhat like the "TileMap" class in the Vertex Array tutorial, right?

Thanks a million, eigenbom! Never thought I'd find someone so nice and helpful in a forum (though it seems that the SFML forums are full of helpful people).

Anyway, I'll give it a try, see how it behaves! Thanks, man!

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Tilemap collision with player
« Reply #16 on: January 06, 2014, 10:54:17 pm »
No worries, I hope your game is coming along. :)

Yep, a simple implementation would combine map2d and tilemap into one class that stores the map data and handles the drawing of the map. Likewise the Player, Monster, Bomb classes could both store data and do the drawing. Then rendering is as simple as target.draw(map); target.draw(player); etc.

A more sophisticated and flexible system would separate the model (map2d) from the view (tilemap). But for a simple game combining them is easiest.

paulomag2106

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Tilemap collision with player
« Reply #17 on: January 07, 2014, 12:11:56 am »
Dude, I saw today a game you made in 2012 or something.... called Moon... moonman? I think it's something in those lines... I LOVE what you did with the water tiles (when you dig beneath them, and they fall like particles or something)!

I also saw in that post that you did something using Box2D in the game, but how did you otherwise do the collisions and tilemap drawing in that project? Totally brill!

By the way, I'm still scratching my head over your response... the names of classes and variables and the structure of the whole thing escape me. As I said, I'm still a beginner at this thing.

Anyway, I truly appreciate all the help, no matter my difficulties and such.

Cheers, man!

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Tilemap collision with player
« Reply #18 on: January 07, 2014, 02:19:23 am »
Yeah it's Moonman, I'm still working on it. I've got a thread over at the TIGsource forums documenting my development over the last 2 years, and I update it about once or twice a week. I use Box2D for all the physics and collision, and before that I had custom physics code, along the lines of what I've been describing here.

But, let's not derail..

You inspired me to make a simple tile-based game in SFML and post it to my blog. I hope it clears some of the concepts up and gives you a better understanding. Note there are different ways to build games, and this is just one of them. One caveat, if your compiler doesn't like this:
for(auto monster: game.monsters)


then you'll have to use the old c++ style:
for(list<monster>::iterator it = game.monsters.begin(); it!=game.monsters.end(); it++){
  Monster& monster = *it;
  // monster.i = ... etc
}


Link: A BASIC TILE-BASED GAME IN SFML

paulomag2106

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Tilemap collision with player
« Reply #19 on: January 07, 2014, 04:43:43 pm »
WOW, that's totally cool! I bet many people have the same doubts I've been going through, and it's really nice to see a code example start-to-finish of a tilebased, topdown game.

Looks truly amazing, eigenbom.

By the way, I took a quick look at the tigsource post you made for Moonman, and I gotta say: you've got something FANTASTIC on the works right there, sir! The liquid mechanics remind me of Starbound (did you get inspired from them, or they from you? :))!

paulomag2106

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Tilemap collision with player
« Reply #20 on: January 07, 2014, 07:49:40 pm »
Hey, eigenbom, so I tried this function below:
void newMove(Player& player, int& levelWidth, const int* level)
{
       
        player.tileX = player.getPosition().x/32;
        player.tileY = player.getPosition().y/32;

        leftTileUp = player.tileX - 1 + player.tileY * levelWidth;
        leftTileDown = player.tileX - 1 + (player.tileY + 1) * levelWidth;
        rightTileUp = player.tileX + 1 + player.tileY * levelWidth;
        rightTileDown = player.tileX + 1 + (player.tileY + 1) * levelWidth;
        underTile = player.tileX + (player.tileY + 2) * levelWidth;
        topTile = player.tileX + (player.tileY - 1) * levelWidth;

        // get input
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
        {
                player.moveVecX = -6;
                player.tileX = player.getPosition().x/32;
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
        {
                player.moveVecX = 6;
                player.tileX = player.getPosition().x/32;
        }
        else
                player.moveVecX = 0;

        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
        {
                player.moveVecY = -6;
                player.tileY = player.getPosition().y/32;
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
        {
                player.moveVecY = 6;
                player.tileY = player.getPosition().y/32;
        }
        else
                player.moveVecY = 0;
       
        // getTiles
        if(player.moveVecX < 0 && (level[leftTileUp] != 0 || level[leftTileDown] != 0))
                player.moveVecX = 0;
        else if(player.moveVecX > 0 && (level[rightTileUp] != 0 || level[rightTileDown] != 0))
                player.moveVecX = 0;
        if(player.moveVecY < 0 && level[topTile] != 0)
                player.moveVecY = 0;
        else if(player.moveVecY > 0 && level[underTile] != 0)
                player.moveVecY = 0;

        // move player
        player.move(player.moveVecX, player.moveVecY);
}
 

and now, the player box KINDA moves ok... it detects collision to his right ok, and at his top ok, right and bottom is still glitchy (I THINK it's because of floor/ceil rounding of player position to tileX/tileY int value).

What do you think? I sorta feel like I'm close to an answer, but I think a few variables are still missing, or something...

paulomag2106

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Tilemap collision with player
« Reply #21 on: January 07, 2014, 08:59:19 pm »
oh, sorry, right and bottom collisions are working correcly, and top and left are glitchy. sorry bout that Oo...

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: Tilemap collision with player
« Reply #22 on: January 07, 2014, 10:13:04 pm »
oh, sorry, right and bottom collisions are working correcly, and top and left are glitchy. sorry bout that Oo...

You can edit your posts....
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Tilemap collision with player
« Reply #23 on: January 07, 2014, 11:59:53 pm »
So you still want to have continuous movement huh? :'(

Your code is hard to debug because you're not using enough abstraction with data structures. For example, on all those level[i + j*BLAH] lines its hard to tell at a glance if your getting the right tiles. If you built a Map2D class like I suggested then those lines would become:

Code: [Select]
int x = player.tileX, y = player.tileY;
Tile leftTileUp = map.getTile(x - 1, y);
leftTileDown = map.getTile(x - 1, y + 1);
rightTileUp = map.getTile(x + 1, y);
...

Which you'd agree is a lot simpler to read. And as long as you built and tested Map2D to be correct then we are guaranteed that those lines are also correct.

I'd build your newMove like this:

Code: [Select]
static const float TILE_WIDTH = 32.f, TILE_HEIGHT = 32.f;

// Converts screen/world position to tile coordinates
// Won't give proper values for negative coords
sf::Vector2i mapWorldToTile(sf::Vector2f p){
  return sf::Vector2i(floor(p.x  / TILE_WIDTH), floor(p.y / TILE_HEIGHT));
}

void newMove(Player& player, Map2D map){
  // First get the intended direction the player wants to move..

  int dx = 0, dy = 0;
  if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) dx = -1;
  else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) dx = 1;
  if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) dy = -1;
  else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) dy = 1;

  if (dx==0 && dy==0){
    // No movement wanted
    return;
  }

  // Get the intended new position
  static const float WALK_SPEED = 6.0f;
  sf::Vector2f newPosition = player.getPosition() + sf::Vector2f(dx * WALK_SPEED, dy * WALK_SPEED);

  // Now .. at this new position we want to check ALL corners
  // of the player to see if they intersect with a collidable tile
  // PLAYER_SIZE records the width & height of the player's collision box
  // (it should be smaller than the player sprite to allow a tiny bit of overlap)
  // (I'm also assuming that player.getPosition() is at the center of the sprite)
  static const float PLAYER_SIZE = 28.0f;
 
  // top-left corner
  sf::Vector2f topLeftPos = newPosition + sf::Vector2f(-PLAYER_SIZE/2., -PLAYER_SIZE/2.);
  sf::Vector2i topLeft = mapWorldToTile(topLeftPos );

  // better check if they are valid coords
  bool valid = topLeft.x>=0 && topLeft.x<map.width() && topLeft.y>=0 && topLeft.y<map.height();
  if (!valid){
    // Player's gone out of bounds, don't move him.
    return; 
  }
 
  Tile& tile = map.getTile(topLeft.x, topLeft.y);
  if (...tile is collidable...){
    // Don't move
    return;
  }

  // Do the same for the other corners...

}

That will give you basic continuous movement and collision detection. But it suffers from a major annoying problem: when you push diagonally into a wall the player won't move. Most games let the player slide along the wall when they push diagonally into it. But that's another problem, for another time.

paulomag2106

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Tilemap collision with player
« Reply #24 on: January 08, 2014, 12:34:06 am »
hmm, the thing with the negative coordinates I believe won't be a problem, since my game is intended to be divided in "rooms", in a way that the player will never be able to be at a position LESS than (0, 0), because these rooms will be all closed up by tiles, except for "doors" or passages into other rooms.

the part about sliding when up/down walls when pushing the player diagonally might also not be a problem, since in the end he won't be able to simple walk diagonally - he will either be jumping or falling.

apart from that, I think I understand what you're going for in this code/pseudocode :D. I'll see what I can get done with it. Thanks again! Oh, and sorry for the lack of abstraction with data structures, it's a bad habit for sure. Gotta make a cleaner code Oo...

Cheers!

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Tilemap collision with player
« Reply #25 on: January 08, 2014, 12:59:33 am »
Oh, you're making a platformer? I thought you were making a top-down game, ha! In that case, it's going to be a whole lot harder, but good luck anyway! :)

paulomag2106

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Tilemap collision with player
« Reply #26 on: January 08, 2014, 01:13:38 pm »
Yeah, I'm starting to get that =/