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

Author Topic: [SOLVED] Collision by differentiating colors  (Read 4559 times)

0 Members and 1 Guest are viewing this topic.

Blingu

  • Newbie
  • *
  • Posts: 15
    • View Profile
[SOLVED] Collision by differentiating colors
« on: August 29, 2018, 03:17:11 pm »
Hi, I'm writing a maze game where you need to go through tight tunnels. Once you touch the wall, you lose and need to start from the beginning. I know that I can basically draw rectangles and iterate through them to check if the player sprite intersects any of them, but it would be very time consuming to make several levels. I made the background (tunnels) in GIMP (picture below), and would like the collision to be detected if the player intersects a pixel with the color of the wall (here it's turquoise). Unfortunately I haven't got an idea what classes, functions and anything like that I could use to do so.

(click to show/hide)
« Last Edit: September 17, 2018, 08:16:33 pm by Blingu »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Collision by differentiating colors
« Reply #1 on: August 29, 2018, 03:29:04 pm »
Assuming that your level doesn't change, you can retrieve the colour of a pixel of an image easily.

The texture loading shortcut step will have to be skipped though like so:
sf::Image image;
if (!image.loadFromFile("image.png"))
    return EXIT_FAILURE;
sf::Texture texture;
if (!texture.loadFromImage(image))
    return EXIT_FAILURE;
sf::Sprite sprite(texture);

Then, you can check any pixel of that image at any time:
const sf::Color colorAtXy = image.getPixel(x, y);
See: https://www.sfml-dev.org/documentation/2.5.0/classsf_1_1Image.php#acf278760458433b2c3626a6980388a95
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Blingu

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Collision by differentiating colors
« Reply #2 on: August 29, 2018, 05:14:27 pm »
Assuming that your level doesn't change, you can retrieve the colour of a pixel of an image easily.

The texture loading shortcut step will have to be skipped though like so:
sf::Image image;
if (!image.loadFromFile("image.png"))
    return EXIT_FAILURE;
sf::Texture texture;
if (!texture.loadFromImage(image))
    return EXIT_FAILURE;
sf::Sprite sprite(texture);

Then, you can check any pixel of that image at any time:
const sf::Color colorAtXy = image.getPixel(x, y);
See: https://www.sfml-dev.org/documentation/2.5.0/classsf_1_1Image.php#acf278760458433b2c3626a6980388a95

Thanks for replying, I understand what you mean. The main problem is I don't really know how I can check the collision if the player gets on that certain color (I can't make an if statement if it intersects any of the walls' pixels, it would be very much to check). Normally to check if the player's colliding with one of the walls (in this example rectangles) I'd write it as the following:

bool Player::checkCollision(Level level)
{

        for (int i = 0; i <= 6; i++)
        {
                if (level.getRects(i).getGlobalBounds().intersects(player.getGlobalBounds()))
                {
                        return true;
                }
        }
        return false;
       
}

But now I need to figure out how to check if the player intersects this certain (here turquoise) color.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Collision by differentiating colors
« Reply #3 on: August 31, 2018, 11:06:50 pm »
Since you know the co-ordinate of that pixel as it's 1 image pixel to 1 actual pixel (right?), you can create a rectangle around that pixel and check for intersection with that rectangle.

The rectangle around pixel (15, 12), for example, would be:
const sf::FloatRect pixelRect({ 15.f, 12.f }, { 1.f, 1.f });
because a pixel's size is, of course, 1x1.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Tigre Pablito

  • Full Member
  • ***
  • Posts: 226
    • View Profile
    • Email
Re: Collision by differentiating colors
« Reply #4 on: September 01, 2018, 12:56:18 am »
Hi Blingu
Hi Hapax

I have an idea about how to approach this, suppose the maze image is 800x600 and the player is 20x20, and suppose the players's Position is (posX, posY), then to know if the player has touched at least 1 pixel of the wall would be (Please someone translate to C++ - Thanks!)
(I think the function should be part of the player class, and recieve the maze image as an argument)

public bool CollideWithWall(Image image)
{
    int a, b;
    for (a = 0; a < 20; a++)
        for (b = 0; b< 20; b++)
            if (image.GetPixel(posX + a, posY + b) == wallColor) // define wallColor
                return true;
    return false;
}
 

The function above checks one by one all pixels from the maze Image that are occupied by the player, and returns true when it encounters the first (just one pixel is needed to collide). If it doesn't find anyone, then returns false.

We also suppose the player's Sprite's Origin has its default value (0, 0).

Finally, as I was taught in this Forums, there shouldn't be magic numbers in the code, so we can replace 800 and 600 by MAZE_WIDTH and MAZE_HEIGHT, and 20 and 20 by PLAYER_WIDTH and PLAYER_HEIGHT

Hope this helps
Pablo

Blingu

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Collision by differentiating colors
« Reply #5 on: September 01, 2018, 01:43:15 pm »
Thanks for help @Hapax @Tigre Pablito, I'll try out what Pablito suggested.

Blingu

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Collision by differentiating colors
« Reply #6 on: September 01, 2018, 02:57:25 pm »
It's working well, though it throws an exception once the hits the bottom of the screen. No compiler errors.

(click to show/hide)

bool Player::checkCollision(Level level)
{
        const sf::Color wallcolor(85, 255, 234);
        const sf::Image image = level.getImage();

        int a, b;

        for (a = 0; a < 20; a++)
                for (b = 0; b < 20; b++)
                        if (image.getPixel(player.getPosition().x + a, player.getPosition().y + b) == wallcolor)
                                return true;
        return false;
               
}

« Last Edit: September 01, 2018, 03:06:11 pm by Blingu »

Tigre Pablito

  • Full Member
  • ***
  • Posts: 226
    • View Profile
    • Email
Re: Collision by differentiating colors
« Reply #7 on: September 01, 2018, 07:12:50 pm »
Hi

It's a run time error (no compiler error because the compiler can't guess that maybe the player will atempt to access out of the maze image bounds)  ;) . Usually they are the most difficult to detect because of that, there is no message.

I had thought about this possibility before, but then I believed that the maze image borderlines would be 'wall' color so that you would lose before step outside the image bounds

To prevent this you could check, before the player scrolls to another position, if it would step outside (for example, if the maze image is 800x600, no part of the player can be at (805, 347). No part, not only the (left, top),  because the collision function will be accessing the image's region that is occupied by all the player's area, and if in a call to GetPixel() X or Y are < 0 or > (800, 600) it will throw an "Out of range" Exception)

public bool CheckStepOutside(newPosX, newPosY)
{
    if (newPosX < 0 || newPosY < 0 || newPosX + 20 > 800 || newPosY + 20 > 600)
        return true;
    return false;
}
 

You can get the (x, y) where the player will be on next frame, and just allow it to scroll if the function (passing x and y to it) returns false.