# 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.

## 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)

Edited by Heelp

##### 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 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 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 on other sites

100 AABBs is not complex. Have you actually seen performance issues, or are you just assuming that they will happen?

##### 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.

##### 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 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 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


#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( 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 optimized surface
SDL_Surface* optimizedImage = NULL;

{
//Create an optimized surface

//Free the old surface

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

{
font = TTF_OpenFont( "lazy.ttf", 38 );

if( pacRight == NULL || pacDown == NULL || pacLeft == NULL || pacUp == NULL )
{
return false;
}

if( tileSheet == NULL )
{
return false;
}

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

{
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 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 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 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 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 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 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 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
{
+        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 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 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 :)

//Post deleted