Sign in to follow this  

Collision using rgb

This topic is 727 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Guys, we are using processing 3 ( some drawing program using java ) in university for making simple animations, and I've decided that this is the time to make my first Java game, i chose Pacman. I made my check_collision function but the problem is that the map is very complicated. I need to draw 100 rects until I make the whole map( and check collision for all of them ) so I was wondering: Is it possible to scan for a particular color, for example, can I say: If (Pacman touches blue color) { collision is ON, stop all movement }, else if ( color == black ) { movementSpeed == normal }. so that way i can just download the map from google and dont bother making a whole map with drawing all the rects myself.

 

[attachment=30330:pacmanMap.png]

 

 

EDIT: After 2 days of suffering, see my new shiny map ( thanks to Alberth)

[attachment=30346:map ready.png]

Edited by Heelp

Share this post


Link to post
Share on other sites

There are just 6 values in each cell:

 

- wall

- point

- pill

- empty

- ghost door

- starting points (4x ghost, 1x player)

 

Give each value a unique character (say "." for point, and "*" for pill, "#" for wall, and so on).

Open an ascii editor and type 20 lines of 20 such characters (more or less, I didn't count them exactly).

 

Write a program that reads the file.

Let it draw the screen.

Done.

 

 

Bonus: It's trivial to change the level, just change the level file, and read it again :)

(won't be pacman then :p  )

Share this post


Link to post
Share on other sites

I dont understand....Just tell me about the walls, leave these pills and ghost doors, how can I say, When you see blue wall, stop right there

Edited by Heelp

Share this post


Link to post
Share on other sites

Short answer: You don't look at the screen.

 

 

 

Longer answer: You use the x/y position in pixels, and translate that to a 2d array to lookup if there is wall or space there.

 

[attachment=30331:grid.png]

 

I took a part of your image, and added red lines. As you can see, it's a regular grid.

 

Next, I made the same grid, but as a 2d array:

1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1
1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0

"1" means wall here, and "0" means empty. If I didn't make a mistake, there is a 1-to-1 correspondence between cells in the picture, and cells in the 2d array. Each cell in the picture has a 1 or a 0 at the corresponding position in the 2d array.

 

No idea how you make a 2d array in your programming language, but if you have an column and row index, it should return you a 0 or a 1.

 

(I start counting at 0, 0 at the top-left, incrementing towards the right and down)

 

Next, you decide the width and height of a single grid cell in pixels, say 10x10. Also, you define an origin, top-left of the screen is easiest, as most computers use that as (0,0).

 

So if you divide your pixel x/y coordinate by 10, you get the column and cell of the 2d array.

Say at pixel (45, 37). That's cell (4, 3) or (the 5th column, 4th row), which is "0", so there is space there.

Pixel (8, 3) is cell (0, 0) (ie top-left), which is 1, and thus a wall.

 

 

That's how you can easily decide where a wall is and where space is.

 

Share this post


Link to post
Share on other sites

Ok, got it, thanks a lot, Alberth. Now I'm writing the program that reads the text, if I have some problems later on, sorry, but I will have to ask you again. wink.png

Share this post


Link to post
Share on other sites
With a game as simple as this you can even resort to pixel perfect collision.

It would be overkill but from an academic perspective worth investigation and learning.

Basically you do a logic AND operation against each pixel in your pac man sprite against the pixels it's about to overwrite on screen and if any pixels after logic operation are non zero you have a collision.

You can even test the colours after the operation to determine what you collided with but for that, and most other things a simple axis aligned bounding box as explained above is simpler and easier to work with.

Just thought you may be interested in some retro lessons for the day :)

Share this post


Link to post
Share on other sites
Besides the perfect approach Alberth suggested, you could do AABB /sphere collision detection, this comes to pixel perfect and gives you future possibilities to expand into a 3rd dimension (height). If you want to move on to 3d later, it's also good practice to understand this sort of collisions (and expand them later to 3d).

Speaking from expierence (www.crealysm.com, games, BooH).

Share this post


Link to post
Share on other sites

There's some problem with my file reading function that I could not figure out. There's also a problem with deleting the objects, search for codewords 'new' and 'delete' and see if you see something wrong because I tried all day and still wont compile

 
//The header files
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include "SDL/SDL_mixer.h"
#include <string>
#include <iostream>
 
//Screen dimensions
const int SCREEN_WIDTH = 840;
const int SCREEN_HEIGHT = 840;
const int SCREEN_BPP = 32;
 
//The frames per second
const int FRAMES_PER_SECOND = 50;
 
//Pacman stuff
const int PAC_WIDTH = 20;
const int PAC_HEIGHT = 20;
 
//The direction status of the Pac
const int PAC_RIGHT = 1;
const int PAC_DOWN = 2;
const int PAC_LEFT = 3;
const int PAC_UP = 4;
 
//The different tile sprites
const int TILE_BLUE = 0;
const int TILE_BLACK = 1;
 
//The different TYPES of tiles
const int TILE_SPRITES = 2;
 
//Tile dimensions and number
const int TILE_WIDTH = 78;
const int TILE_HEIGHT = 78;
const int TOTAL_TILES = 441;
 
SDL_Surface *screen = NULL;
SDL_Surface *tileSheet = NULL;
 
//In-game sprite surfaces
SDL_Surface *pacRight = NULL;
SDL_Surface *pacDown = NULL;
SDL_Surface *pacLeft = NULL;
SDL_Surface *pacUp = NULL;
 
//The event structure
SDL_Event event;
 
//
SDL_Rect clips[ TILE_SPRITES ];
 
//The font
TTF_Font *font = NULL;
 
//The color of the font
SDL_Color color[2] = { { 255, 255, 255 }, { 255, 0, 0 } };
 
class Tile
{
private:
    //The attributes of the tile
    SDL_Rect tilebox;
 
    //The type of tile
    int type;
 
public:
    //Initialize the variables
    Tile( int x, int y, int tileType );
 
    //Show the tile
    void show();
};
 
bool set_tiles( Tile *tiles[] )
{
    //The tile offsets
    int x = 0, y = 0;
 
    //Opening the text file with the 0s and 1s
    FILE* filePointer;
    filePointer=fopen("Pics/pacmanMap.txt", "r");
    int t = 0, counter = 0, tileType = 0;
 
    //While going through all the characters...
    while( ( t = getc( filePointer ) )!= EOF )
    {
        //If it sees zero, put 1st tile
        if( t == '0' )
        {
            //Make the type of the tile = 0
            tileType = 0;
            tiles[ counter ] = new Tile( x, y, tileType );
            //Increment counter if 0 was found in the text file
            counter ++;
 
            //Move to the next tile coordinates
            x += TILE_WIDTH;
 
            //If you are out of screen
            if( x >= SCREEN_WIDTH )
            {
                x = 0;
                y += TILE_HEIGHT;
            }
        }
 
        else if( t == '1' )
        {
            tileType = 1;
            tiles[ counter ] = new Tile ( x, y, tileType );
            counter ++;
 
            //Move to the next tile coordinates
            x += TILE_WIDTH;
 
            //If you are out of screen
            if( x >= SCREEN_WIDTH )
            {
                x = 0;
                y += TILE_HEIGHT;
            }
        }
    }
    fclose( filePointer );
}
 
class Timer
{
    private:
    //The clock time when the timer started
    int startTicks;
 
    //The timer status
    bool started;
 
    public:
    //Initializes variables
    Timer();
 
    //The various clock actions
    void start();
    void stop();
 
    //Gets the timer's time
    int get_ticks();
 
    //Checks the status of the timer
    bool is_started();
};
 
Timer::Timer()
{
    //Initialize the variables
    startTicks = 0;
    started = false;
}
 
void Timer::start()
{
    //Start the timer
    started = true;
 
    //Get the current clock time
    startTicks = SDL_GetTicks();
}
 
void Timer::stop()
{
    //Stop the timer
    started = false;
}
 
int Timer::get_ticks()
{
    //If the timer is running
    if( started == true )
    {
        //Return the current time minus the start time
        return SDL_GetTicks() - startTicks;
    }
 
    //If the timer isn't running
    return 0;
}
 
void clip_tiles()
{
    //Clip the sprite sheet
    clips[ TILE_BLUE ].x = 0;
    clips[ TILE_BLUE ].y = 0;
    clips[ TILE_BLUE ].w = TILE_WIDTH;
    clips[ TILE_BLUE ].h = TILE_HEIGHT;
 
    clips[ TILE_BLACK ].x = TILE_WIDTH;
    clips[ TILE_BLACK ].y = 0;
    clips[ TILE_BLACK ].w = TILE_WIDTH;
    clips[ TILE_BLACK ].h = TILE_HEIGHT;
 
}
 
bool init()
{
    //Initialize all SDL subsystems
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
    {
        return false;
    }
 
    //Set up the screen
    screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );
 
    //If there was an error in loading the screen
    if( screen == NULL )
    {
        return false;
    }
 
    //Initialize SDL_ttf
    if( TTF_Init() == -1 )
    {
        return false;
    }
 
    //Set the window caption
    SDL_WM_SetCaption( "Pacman", NULL );
 
    //If everything initialized fine
    return true;
}
 
SDL_Surface * load_image( std::string filename )
{
    //The image that's loaded
    SDL_Surface* loadedImage = NULL;
 
    //The optimized surface
    SDL_Surface* optimizedImage = NULL;
 
    //Load the image
    loadedImage = IMG_Load( filename.c_str() );
 
    //If the image loaded
    if( loadedImage != NULL )
    {
        //Create an optimized surface
        optimizedImage = SDL_DisplayFormat( loadedImage );
 
        //Free the old surface
        SDL_FreeSurface( loadedImage );
 
        //If the surface was optimized
        if( optimizedImage != NULL )
        {
            SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( optimizedImage->format, 0, 0, 0 ) );
        }
    }
    //Return the optimized surface
    return optimizedImage;
}
 
bool load_files()
{
    //Load the font
    font = TTF_OpenFont( "lazy.ttf", 38 );
 
    //Load the background
 
    //Load the tiles sheet
    tileSheet = load_image( "Pics/black_white_tiles.jpg");
 
    //Load the pacman images
    pacRight = load_image( "Pics/pac_right.png" );
    pacDown = load_image( "Pics/pac_down.png" );
    pacLeft = load_image( "Pics/pac_left.png" );
    pacUp = load_image( "Pics/pac_up.png" );
 
    //If there was a problem in loading the sprites
 
 
    if( pacRight == NULL || pacDown == NULL || pacLeft == NULL || pacUp == NULL )
    {
        return false;
    }
 
    if( tileSheet == NULL )
    {
        return false;
    }
 
    //If everything loaded fine
    return true;
}
 
bool check_collision( SDL_Rect A, SDL_Rect B )
{
    //The sides of the rectangles
    int leftA, leftB;
    int rightA, rightB;
    int topA, topB;
    int bottomA, bottomB;
 
    //Calculate the sides of rect A
    leftA = A.x;
    rightA = A.x + A.w;
    topA = A.y;
    bottomA = A.y + A.h;
 
    //Calculate the sides of rect B
    leftB = B.x;
    rightB = B.x + B.w;
    topB = B.y;
    bottomB = B.y + B.h;
 
    //If any of the sides from A are outside of B
    if( bottomA <= topB )
    {
        return false;
    }
 
    if( topA >= bottomB )
    {
        return false;
    }
 
    if( rightA <= leftB )
    {
        return false;
    }
 
    if( leftA >= rightB )
    {
        return false;
    }
 
    //If none of the sides from A are outside B
    return true;
}
 
void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL )
{
    //Holds offsets
    SDL_Rect offset;
 
    //Get offsets
    offset.x = x;
    offset.y = y;
 
    //Blit
    SDL_BlitSurface( source, clip, destination, &offset );
}
 
 
void close_all( Tile *tiles[] )
{
    //Free the pacman surfaces
    SDL_FreeSurface( pacRight );
    SDL_FreeSurface( pacDown );
    SDL_FreeSurface( pacLeft );
    SDL_FreeSurface( pacUp );
 
    //Free the background and tiles surfaces
    SDL_FreeSurface( tileSheet );
 
    //Free the tiles
    for( int t = 0; t < TOTAL_TILES; t++ )
    {
        delete tiles[ t ];
    }
    //Close Font
    TTF_CloseFont( font );
 
    //Quit SDL_ttf
    TTF_Quit();
 
    //Quit SDL
    SDL_Quit();
 
}
 
 
Tile::Tile( int x, int y, int tileType )
{
    //Get the offsets
    tilebox.x = x;
    tilebox.y = y;
 
    //Set the collision box
    tilebox.w = TILE_WIDTH;
    tilebox.h = TILE_HEIGHT;
 
    //Get the tile type
    type = tileType;
}
 
void Tile::show()
{
    //Show the tile
    apply_surface( tilebox.x, tilebox.y, tileSheet, screen, &clips[ type ] );
}
 
int main( int argc, char* args[] )
{
 
    bool quit = false;
 
 
    //The tiles that will be used
    Tile *tiles[ TOTAL_TILES ];
 
    //Initialize the SDL stuff
    if( init() == false )
    {
        return 1;
    }
 
    //Load the files
    if( load_files() == false )
    {
        return 1;
    }
 
    //Clip the tile sheet
    clip_tiles();
 
    //Set the tiles
    if( set_tiles( tiles ) == false )
    {
        return 1;
    }
 
    Timer fps;
 
    //The pacman
 
    //While the user hasn't quit
    while( quit == false )
    {
        //While there's events to handle
        while( SDL_PollEvent( &event ) )
        {
            //If the user has pressed Esc or X
            if( event.type == SDL_QUIT || event.key.keysym.sym == SDLK_ESCAPE )
            {
                //Quit the program
                quit = true;
            }
        }
 
        //Apply the background
 
        //Show the foos on the screen
 
        //Show the tiles
        for( int t = 0; t < TOTAL_TILES; t++ )
        {
            tiles[ t ]->show();
        }
 
        //Update the screen
        if( SDL_Flip( screen ) == -1 )
        {
            return 1;
        }
 
        //Cap the frame rate
        if( fps.get_ticks() < 1000 / FRAMES_PER_SECOND )
        {
            SDL_Delay( 1000 / FRAMES_PER_SECOND  - fps.get_ticks() - 5 );
        }
    }
 
    //Clean up
    close_all( tiles );
 
    return 0;
}
 
Edited by Heelp

Share this post


Link to post
Share on other sites

I just saw that the code is more than expected, so if you want a cleaner version of the program, I can delete some stuff and cut the program by half

Edited by Heelp

Share this post


Link to post
Share on other sites


There's also a problem with deleting the objects, search for codewords 'new' and 'delete' and see if you see something wrong because I tried all day and still wont compile

What compiler errors are you getting? Also, I'm confused about what the issue is with the file loading. It sounded like a bug, but then you say the code doesn't compile which implies you haven't run the code. If it is a bug, what is the behavior you are seeing?

Share this post


Link to post
Share on other sites

Oh, sorry, I'm new and I confuse the terms quite often. My mistake, sry. It compiles but it returns 1. The thing is that all my functions return 1 when there is an error, I wanted to make a different comment that is printed on screen for every error in every function but someone told me I needed SDL 2.0 for this, and I'm using SDL 1.2, so now when something is wrong, I made all the functions to return 1, But I don't know exactly what function went wrong because all of them return 1 without any comment.

And when I compile, it returns 1 on console, that's the problem.

Edited by Heelp

Share this post


Link to post
Share on other sites

A very simple thing you can do that should work is return different numbers for the different errors instead of using the same number. Also, you should learn how to use a debugger. Vimeo is blocked at work so I can't check how useful it is, but Google suggested this video.

Share this post


Link to post
Share on other sites

Yes, I did it now. I have mistakes in set_tiles() function, and in the int main() when showing the tiles on screen, and maybe in my show() function.

Edited by Heelp

Share this post


Link to post
Share on other sites

nobodynews Thanks so much, man. I found my mistake, I'm showing too much tiles on screen, TOTAL_TILES is set to 441, and the maximum number of tiles which can be shown without getting error is 121, if I try to show 122 tiles, it returns 3, If i try with anything lower than 122, it works. Maybe my memory is not big enough or something, I don't know. But thanks again.

Share this post


Link to post
Share on other sites

I had a quick look and below are my first findings.

In general, code looks quite neat.

 

 

I got it to compile, but it gave me a warning (always turn on as many warnings in the compiler as possible, anything it find saves you the trouble of debugging it)

prog.cpp: In function 'bool set_tiles(Tile**)':
prog.cpp:129:1: warning: no return statement in function returning non-void [-Wreturn-type]
 }

This means 'set_tiles' may return any value, including '0' (false) even if loading went well.

Easily solved with "return true;" at the end of the function.

 

Running it crashed immediately. It happened in "tiles[t]->show();" and this takes some steps to explain. Just going from top to bottom through that routine.

     FILE* filePointer;
-    filePointer=fopen("Pics/pacmanMap.txt", "r");
+    filePointer = fopen("Pics/pacmanMap.txt", "rt");
+    if (filePointer == nullptr) return false;

I used 'diff', a program to output changes between files. Lines starting with a '-' are in your file but I removed them, lines starting with '+' were added by me.

A changed line gets a -, and then a +, so you can easily compare both versions.

 

So above I edited the "filePointer=fopen" line, adding some spaces around the =, and a "t" with the "r" (since text files are always opened with a "t" modifier).

I also added a line that checks the return value of the 'fopen' call (always check things for errors, it will save you hours of debugging).

             counter ++;
+// You don't check whether tiles[counter] exists. That can cause memory
+// corruption!
+            if (counter == TOTAL_TILES) break; // Abort reading if all tiles read.

If you reach the end of an array,  tiles[counter] will appear to continue to work, but actually corrupts other values. Better check boundaries :)

 

This solves the problem of reading too many tiles (if your 0/1 input file is too big for some reason). My crash was however reading too few digits. That is solved as follows

     Tile *tiles[ TOTAL_TILES ];
 
+    // Here 'tiles' is random garbage, it's better to initialize it.
+    // (I may be wrong here, maybe C++ does initialize it. However, better safe than sorry.)
+    for (int t = 0; t < TOTAL_TILES; t++) tiles[t] = nullptr;

Make sure tiles[] is fully initialized. Also

         for( int t = 0; t < TOTAL_TILES; t++ )
         {
-            tiles[ t ]->show();
+            // And here is another problem, what if you didn't load enough tiles??
+            // Either check 'counter' after fully loading the file, or be more careful here:
+            if (tiles[t] != nullptr) tiles[t]->show();

Just skip if you run into a 'nullptr'. Alternatively, you can just 'break', and terminate the loop, since there are not going to be any more tiles to show.

 

After this, the window stayed up, but crashed again when typing ESC. Perhaps some other unchecked call????

 

 

Other stuff I ran into:

 

I hate '=' in a condition (I once spend 45 minutes finding one), so I do:

-    //While going through all the characters...
-    while( ( t = getc( filePointer ) )!= EOF )
+//    //While going through all the characters...
+//    while( ( t = getc( filePointer ) )!= EOF )
+
+// I don't like assignments inside conditions, so I do the following instead:
+    while (true)
     {
+        t = getc(filePointer);
+        if (t == EOF) break;

The '0' case and the '1' case while loading look very similar, you can merge them with

-        if( t == '0' )
+        if( t == '0' || t == '1' )
         {
             //Make the type of the tile = 0
-            tileType = 0;
+            tileType = t - '0';
             tiles[ counter ] = new Tile( x, y, tileType );

and deleting the '1' case.

 

About your error reporting. You can use a string as error message, for example


-bool init()
+// You can also pass a constant string back to the caller indicating what went wrong:
+const char *init()
 {
     //Initialize all SDL subsystems
     if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
     {
-        return false;
+        return "SDL_init failed";
     }

     ......

     //If everything initialized fine
-    return true;
+    // C++11 and higher uses 'nullptr' instead of NULL.
+    return nullptr;
 }

The function returns a "const char *" instead of a boolean, and you can catch and print it with

-    if( init() == false )
-    {
+    const char *return_value = init();
+    if (return_value != nullptr) {
+        // Make sure you add a \n at the end of the string that you print otherwise you don't get a line.
+        // I use 'stderr', as it is for error messages.
+        // The normal 'printf' uses stdout, which is for regular output.
+        fprintf(stderr, "Error: %s\n", return_value);
         return 1;
     }

However, something like below should also work:

     //Load the files
     if( load_files() == false )
     {
+        printf("load_files() failed\n"); // <-- should also work
         return 1;
     }

I don't what platform you use, but Windows is notorious for not showing the stdout and stderr streams in the GUI.

 

std::string is passed differently, normally:

+// std::strings are normally passed by const reference, to avoid copying the object:
- SDL_Surface * load_image( std::string filename )
+// SDL_Surface * load_image( const std::string &filename )
     //While the user hasn't quit
+    // Booleans can also be tested directly instead of comparing with false or true,
+    // like "while (!quit)"
     while( quit == false )
         {
             SDL_Delay( 1000 / FRAMES_PER_SECOND  - fps.get_ticks() - 5 );
         }
+        // Does the "- 5" miss in the condition above? (which was chopped off by diff, sorry about that)
+        // Perhaps compute the value instead, and then test?
+        // int delay = 1000 / FRAMES_PER_SECOND  - fps.get_ticks() - 5;
+        // if (delay > 0) SDL_Delay(delay);

Last but not least,

Tile *tiles[ TOTAL_TILES ];   // Lots of very small allocations, why not do

Tile tiles[TOTAL_TILES]; // One big array containing all 'Tile' objects.
//Tile::Tile() can be empty, move initialization eg to Tile::load(x, y, tiletype), so you can do

tiles[counter].load(x, y, tileType);

Hope this helps.
 

Share this post


Link to post
Share on other sites

Alberth, Thank you so much. You don't know how much stuff you did for me here, thanks a lot. The funny thing is that I pay 9000 pounds per semester and when I ask my professor to see my code, he just tells me how he did it and shows me HIS code, not actually seeing what are my mistakes in MY code. And you here, took the time to actually see all my code and point all my mistakes, and you did it for free. Thanks again, so much, everything is working now. There's no way I could have fixed all this by myself, especially the part where you check the tiles[t] for nullptr before showing it on screen, and actually this was the reason why return 3 was showing on console.

for( int i=0; i<=1billion; i++)
{
   printf("THANKS MAN \n" );
}

Edited by Heelp

Share this post


Link to post
Share on other sites

You're welcome :)

 

Peer review is a very powerful way to learn. Find one or more other persons eg fellow students, and read and comment on each other code, point out other (better?) ways to solve a problem. Different people have a different views, and different ideas how to do things, and usually they are all valid. Above all however, have fun doing it :)

Share this post


Link to post
Share on other sites

//Post deleted
                                    Edited by Heelp

Share this post


Link to post
Share on other sites
Sign in to follow this