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

Author Topic: Strange problem with sprite collision  (Read 2581 times)

0 Members and 1 Guest are viewing this topic.

XxLanternexX

  • Newbie
  • *
  • Posts: 3
    • View Profile
    • Email
Strange problem with sprite collision
« on: June 04, 2021, 10:48:56 am »
Hello everyone,

I try to do a simple thing: I have a vector of Heroes and I want to change the color's sprite when one of the hero collides another one. The sprite's color change to red when there is a collision and return to normal/original color when there is no collision.

Hero is just a simple struct with a sprite and a texture:
struct Hero
{
    sf::Sprite s;
    sf::Texture t;

    Hero(sf::Vector2f pos)
    {
        if(!t.loadFromFile("sprite_example.png"))
                throw std::runtime_error(...);
       
        s.setTexture(t);
        s.setTextureRect(sf::IntRect(0,0,64,64));
        s.setPosition(pos);

       
    }
};
So I do this:
std::vector< std::unique_ptr<Hero> > vectorHeroes;
vectorHeroes.push_back( std::make_unique<Hero>(sf::Vector2f(10.f,100.f)) ); // create hero at position 10,100
// create some other heroes...
 

Simple function that test if there is collision between two sprites:
bool isCollision(const sf::Sprite &s1, const sf::Sprite &s2) // check if there's collision between s1 and s2. Return true if it's the case.
{
    if(s1.getGlobalBounds().intersects(s2.getGlobalBounds())) return true;

    return false;
}

Then I make a double loop to test if a hero(in vector) collides another one(in vector too) :


void checkCollision()
{
    int i = 0, j = 0;

    while(i < vectorHeroes.size())
    {

        while(j < vectorHeroes.size())
        {
            if(i != j)  // makes sure the two tested sprites are different
            {
                if( isCollision( vectorHeroes[i]->s, vectorHeroes[j]->s ) )  // if there's a collision between one of the sprites with another one
                {
                    std::cout << "COLLISION WITH " << i << " AND " << j << "\n==============" << std::endl;
                    vectorHeroes[i]->s.setColor(sf::Color::Red);  // set the sprite's color to red
                }
                else
                {
                    vectorHeroes[i]->s.setColor(sf::Color(-1,-1,-1));  // set the sprite's color to the original one
                }
            }
            ++j;
        }
        ++i;
     }
}

Then I move my heroes and call checkCollision() in the game loop.

But the problem is :
1. when there's a collision: ONE sprite becomes red and not the other colliding sprites, sometimes the sprite's color does not change at all(setColor(red) is ignored).

2. the collision between certain sprites is ignored(WHY ???). It seems there is a bug in the program.

I don't know WHY there's this strange problem. What I want to do is pretty simple though.

I think it's a problem relative to the index of the vector when I browse it with while loop but not sure.

Have you a solution, please ?
PS: sorry if there's grammar mistakes

« Last Edit: June 04, 2021, 11:21:31 am by XxLanternexX »

kojack

  • Sr. Member
  • ****
  • Posts: 329
  • C++/C# game dev teacher.
    • View Profile
Re: Strange problem with sprite collision
« Reply #1 on: June 04, 2021, 01:14:10 pm »
You aren't resetting j to zero when the while(j < vectorHeroes.size()) loop is started again.

Or for a bit of a saving, you can halve the comparisions by starting the j loop with the value of i+1, then do the collision response to both objects. This makes it so the loops won't do (for example) object 5 vs 10 and 10 vs 5, only the first happens and does the work of both.
(Hope that made sense, just got home from a VERY long day at work)

XxLanternexX

  • Newbie
  • *
  • Posts: 3
    • View Profile
    • Email
Re: Strange problem with sprite collision
« Reply #2 on: June 04, 2021, 02:24:22 pm »
Hello, thanks you for your reply.

But I don't understand why and where in my code I have to reset j to zero because j = 0 at the beginning of checkCollision() each time checkCollision() is called in the game loop. Can you bring me explanations please ?
« Last Edit: June 04, 2021, 02:27:12 pm by XxLanternexX »

kojack

  • Sr. Member
  • ****
  • Posts: 329
  • C++/C# game dev teacher.
    • View Profile
Re: Strange problem with sprite collision
« Reply #3 on: June 04, 2021, 03:06:43 pm »
This:
   int i = 0, j = 0;

    while(i < vectorHeroes.size())
    {

        while(j < vectorHeroes.size())
        {

Should be:
   int i = 0;

    while(i < vectorHeroes.size())
    {
        int j = 0;
        while(j < vectorHeroes.size())
        {
So j gets reset to zero every time i increments.
Otherwise this would happen... (let's say vectorHeroes.size() is 4)
i is 0
   j is 0
   j is 1
   j is 2
   j is 3
i is 1
   j is 4 so while loop is skipped
i is 2
   j is 4 so while loop is skipped
i is 3
   j is 4 so while loop is skipped




XxLanternexX

  • Newbie
  • *
  • Posts: 3
    • View Profile
    • Email
Re: Strange problem with sprite collision
« Reply #4 on: June 04, 2021, 08:56:04 pm »
Thanks you very much. Now I understand my mistakes.

I know also how to resolve the color's problem, I have to put a break instruction after changing the color.

So the correct code is:
void checkCollision()
{
    int i = 0;

    while(i < vectorHeroes.size())
    {
        int j = 0;

        while(j < vectorHeroes.size())
        {
            if(i != j)  // makes sure the two tested sprites are different
            {
                if( isCollision( vectorHeroes[i]->s, vectorHeroes[j]->s ) )  // if there's a collision between one of the sprites with another
                {
                    vectorHeroes[i]->s.setColor(sf::Color::Red);  // set the sprite's color to red
                    break;  // break because otherwise the sprite's color does not change correctly
                }
                else
                {
                    vectorHeroes[i]->s.setColor(sf::Color(-1,-1,-1));  // set the sprite's color to the original one
                }
            }
            ++j;
        }
        ++i;
    }
}
 

After testing some code I've found another solution to solve my problem, it's an easier way to write the first solution:
void checkCollision()
{
    // Use a "for each" loop

    for(auto &i : vectorHeroes)
    {
        for(auto &j : vectorHeroes)
        {
            if( isCollision(i->s, j->s) && i != j )  // i != j is important to make sure it's two different sprites
            {
                std::cout << "COLLISION!!\n==========" << std::endl;
                i->s.setColor(sf::Color::Red);
                break;  
            }
            else
            {
                i->s.setColor(sf::Color(-1,-1,-1));
            }

        }
    }
}
 

I hope this will be useful for everyone else that encounter this kind of problem.