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.
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
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;
}
}
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