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

Author Topic: [SOLVED] Collision Handling between entity and tilemap - SFML Game Dev Book  (Read 9744 times)

0 Members and 2 Guests are viewing this topic.

OualidH38

  • Newbie
  • *
  • Posts: 46
    • View Profile
    • Email
Hello there  :D

I'm currently programming a 2D RPG using the SFML Game Development book.
I used quite the same architecture for my game such like scene nodes etc.
The tilemap is also a scene node, I managed to store the integer map into a std::vector, then the tilemap is displayed with a VertexArray.

I'm actually in Chapter 7 Implementing gameplay.

My question is:
How do I react to a collision between the player entity and a tile?

I managed to "detect" the collision by using a function that use the actual position of the player entity and the actual TileMap integer array. But to react to that collision I need to know the futur position of the entity and most of all how to allow or not the player entity movement?


So to summarise, I'm able to know on which tile the player is actually on, but I don't know the tile where the player will be before the movement and how should I "block" the movement.


According to the book, what is the best way to handle that kind of collision?
Using the SceneNode::checkNodeCollision(...) & World::handleCollisions() functions or an other kind of function?

Or perhaps the tilemap should have been an entity?

I really don't know, I will appreciate some help on that matter  ;D
« Last Edit: April 02, 2016, 11:12:45 pm by OualidH38 »

ZeroZ30o

  • Newbie
  • *
  • Posts: 47
    • View Profile
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #1 on: February 20, 2016, 01:29:07 pm »
Either make a way to detect the tiles to the sides of the player or make a "ghost" player that moves to the tile before the player does, and if it detects a collision, then it would return to the player, otherwise, it would trigger the player movement.

This is a huge, quite stupid workaround tho, I really suggest you simply code a way to detect tiles on the sides.
To do this simply do player.position.x + 1 and that should give you the position on the right, if your player and tiles use the same system of position. Otherwise, adjust it accordingly.

OualidH38

  • Newbie
  • *
  • Posts: 46
    • View Profile
    • Email
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #2 on: February 20, 2016, 02:49:34 pm »
Hey ZeroZ30o thanks for the reply!  :D

I didn't think about a "ghost" player, it seems to be a good idea, but I'd rather prefer working with coordinates instead of objects.

If I do like player.position.x + 1, I will handle the collision at player.position.x + 1, but it is not necessary the futur position.

What I should do is to get the position of the player entity with mPlayer->getPosition() and the velocity with mPlayer->getVelocity(), then calculate the collision at the futur position and apply the futur position to the player like this:


sf::Vector2f position = mPlayer->getPosition(); //get the current position
sf::Vector2f velocity = mPlayer->getVelocity() * dt.asSeconds();//get the player velocity

if(mPlayer->checkCollision(TileMap, position, velocity){
//check if there's no collision
mPlayer->setPosition(position + velocity);
}



checkCollision(TileMap tilemap, sf::Vector2f position, sf::Vector2f velocity){
//add the velocity to the current position to get the futur position
position += velocity;

if(!Collision(tilemap, position))//if there's no collision at the future position
return true;
else //collision!
return false
}
 

This is what I've done so far, the collision is well detected, but the player is still moving when the collision happens, which means that the reaction is not appropriate.
I'm looking for a better way to handle the collision in adequation with the book architecture. There's probably something I missed.

OualidH38

  • Newbie
  • *
  • Posts: 46
    • View Profile
    • Email
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #3 on: February 20, 2016, 05:32:45 pm »
I found a solution finally, don't know if it's the best way.
The reason why the way I react to a collision was useless was because I was always updating the entity inside the updateCurrent function.
I've made a public boolean to switch the upgradeCurrent function from "do move(mVelocity * dt.asSeconds()" to "do nothing".
So when a collision occurs I switch the boolean to false and I don't set the position to the entity.
If there's no collision I just switch the boolean value to true, set the position....and TADAA it works!


But if someone has a better way to do I take it  ;)

OualidH38

  • Newbie
  • *
  • Posts: 46
    • View Profile
    • Email
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #4 on: March 31, 2016, 07:46:56 pm »
Hello everyone !  ;D

I'm back to collision issues since I started to use Tiled Editor and the sfml-tmxloader. Now I'm wondering how could I handle efficiently a collision between an entity and a tilemap created with Tiled?

I use a TileMap class (that inherit from SceneNode class), which allows me to load a tilemap from TiledEditor using sfml-tmxloader.

So far I was able to detect a collision between the player entity and objects from the tilemap, here is the code:
std::vector<sf::Vector2f> points;
    points.push_back(sf::Vector2f(0.f, 8.f));
    points.push_back(sf::Vector2f(-8.f, 0.f));
    points.push_back(sf::Vector2f(8.f, 0.f));
    points.push_back(sf::Vector2f(0.f, 16.f));
 
    sf::Vector2f position = mPlayerCharacter->getWorldPosition();
 
    std::vector<tmx::MapLayer>& layers = mTileMap->getLayers();
 
    for (auto& layer : layers)
    {
        if (layer.type == tmx::ObjectGroup)
        {
            for (auto& obj : layer.objects)
            {
                if (layer.name == "Objects")
                {
                    for (auto& point : points) {
 
                        if (obj.Contains(position + point))
                        {
                            std::cout << "Collision";
                            break;
                        }
                       
                       
                    }
 
                }
            }
           
        }
           
    }
 

But I was wondering how to react to that collision according to the book way-of-doing-things.

Usually I just take the current position and calculate the future position, then I test the future position, if there's no collision I just set the future position to the sprite, like this :
std::vector<sf::Vector2f> points;
    points.push_back(sf::Vector2f(0.f, 8.f));
    points.push_back(sf::Vector2f(-8.f, 0.f));
    points.push_back(sf::Vector2f(8.f, 0.f));
    points.push_back(sf::Vector2f(0.f, 16.f));
 
 
sf::Vector2f position = mSprite.getPosition();
sf::Vector2f velocity = getVelocity() * dt.asSeconds();
position += velocity;
 
//Testing collision in the future position
for(auto& point : points) {
 
if(obj.Contains(position + point)) {
    //Collision!
}
else
    mSprite.setPosition(position);
}
 

But I don't know how to implement this logic according to the entity system. I tried to handle this directly in my Character class (which looks like the Aircraft class) but I wasn't able to get a correct collision response.
I don't see how I could get the future position of an entity in a generalize approach.

I was thinking of using the SceneNode::checkNodeCollision to handle the collision, which will give me something like this:

//Inside the handleCollision function


void World::handleCollision(sf::Time dt) {

        std::set<SceneNode::Pair> collisionPairs;
        mSceneGraph.checkSceneCollision(mSceneGraph, collisionPairs);

        for (SceneElement::Pair pair : collisionPairs) {

                if (matchesCategories(pair, Category::PlayerCharacter, Category::TileMap)) {

                        auto& player = static_cast<Character&>(*pair.first);
                        auto& tileMap = static_cast<TileMap&>(*pair.second);

                        player.checkTileMapCollision(tileMap, dt);
                }
....


// Inside the Character class

void Character::checkTileMapCollision(TileMap& tileMap, sf::Time dt){

std::vector<sf::Vector2f> points;
    points.push_back(sf::Vector2f(0.f, 8.f));
    points.push_back(sf::Vector2f(-8.f, 0.f));
    points.push_back(sf::Vector2f(8.f, 0.f));
    points.push_back(sf::Vector2f(0.f, 16.f));
 
 
sf::Vector2f position = getWorldPosition();
sf::Vector2f velocity = getVelocity() * dt.asSeconds();
position += velocity;
 
//Testing collision in the future position
for(auto& point : points) {
 
if(obj.Contains(position + point)) {
    //Collision!
}
else
    setPosition(position);
}
 


I don't know if my logic is correct to be honest. Looking for some help guys, any idea is welcomed  ;D
 

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #5 on: March 31, 2016, 09:00:55 pm »
what a coincidence, i'm also making a game using same game-structure as in that book.  ;D
but it is 2d platformer.

i went through same issue as you experienced so far. luckily i managed to make it work here. what i did is, i have created invisible entities from object layer i call it solid. and i test the collision as any entity in scenegrap if two entities collided i checked for how much is the penetration in each other then i resolve the collision base on where the collision happened from top or bottom or even from left and right. by using manifold.

there is a guy wrote beautiful tutorial in this subject here a link:
http://www.randygaul.net/2013/03/28/custom-physics-engine-part-2-manifold-generation/


this works fine in 2d platformer but wasn't sure if it will work in RPG too but i guess it worth a try  ;)

here my project if you want to check how i did it, i will post it in project section but the game is not finished yet i just implemented the collision.
https://github.com/MORTAL2000/Simple-Super-Mario-

OualidH38

  • Newbie
  • *
  • Posts: 46
    • View Profile
    • Email
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #6 on: March 31, 2016, 10:47:28 pm »
Thank you for the reply!  :D

So you have managed to parsed tiled objects into entities, then you handle the collision between the player and those entities.

But I'm not sure to understand how you react to a collision. How do you resolve the collision?


Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #7 on: March 31, 2016, 11:16:54 pm »
resolving the collision is the easiest part, in my case, i stored manifold data in sf::Vector3f and then moved the player back to the last good position before collision happened like this:

void Player::resolve(const sf::Vector3f& manifold, SceneNode* other)
{
....
        switch (other->getType())
        {
        case Type::Solid: // invisible entity represent the tile where player should stop moving if hit it
                move(sf::Vector2f(manifold.x, manifold.y) * manifold.z); // move it back to the good position
                if (manifold.x != 0)//we hit a wall so stop
                        setVelocity({});
                break;
        default: break;
        }
}

OualidH38

  • Newbie
  • *
  • Posts: 46
    • View Profile
    • Email
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #8 on: March 31, 2016, 11:54:10 pm »
Thanks for your help, I will look at it tomorrow  ;)

OualidH38

  • Newbie
  • *
  • Posts: 46
    • View Profile
    • Email
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #9 on: April 01, 2016, 02:05:31 pm »
Ok I think I've understand the logic, moving the entity to the last correct position. I will write some code tonight.

For an RPG like mine, I just need to check if manifold.x != 0 (left/right collision) and manifold.y - manifold.z value (top/bottom collision) inside the resolve function right?
« Last Edit: April 01, 2016, 02:07:56 pm by OualidH38 »

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #10 on: April 01, 2016, 03:38:32 pm »
i didn't make any RPG games, so i can't help you much in this topic. but for the concept of manifold in collisions yeah, it works fine in my project and assumed it will work on yours as well.

for better understanding the manifold, please take a look to this link. it explains the concept of manifold very well.

http://trederia.blogspot.com/2016/02/2d-physics-101-pong.html

« Last Edit: April 01, 2016, 03:40:50 pm by MORTAL »

OualidH38

  • Newbie
  • *
  • Posts: 46
    • View Profile
    • Email
Re: Collision Handling between entity and tilemap - SFML Game Dev Book
« Reply #11 on: April 02, 2016, 03:25:24 pm »
I made it, it is working perfectly!

To resolve the collision in my case, I just had to move the entity back to the last correct position, doesn't matter if it is a top/bottom or left/right collision. I used the same getManifold function, this is the resolve function:
void Character::resolve(const sf::Vector3f & manifold) {

        move(sf::Vector2f(manifold.x, manifold.y) * -manifold.z);
}
 

Thank you MORTAL for your help  ;D