/*******************
Allegro Newbie Tutorial
by
LoomSoft
http://loomsoft.net
email: jay@loomsoft.net
*******************/
/* Allegro Newbie Tutorial : Lesson 8 - Bounding Box Collision Detection
*
* This tutorial will show you how to do a very simple implementation of
* collision detection using bounding boxes. The user can move both images.
*
* Controls:
* Up/Down/Left/Right : move image 1
* A/W/S/D : move image 2
* SPACE: draw bounding boxes.
* ESC: exit
*
* This lesson also covers error checking when creating/loading bitmaps,
* drawing primitive lines, and displaying text. This is something new that
* was skipped in all the previous tutorials for simplicity. It is extremely
* good practice to do error checking for any function that allows you to do so.
*
* We also learn about using the function: allegro_message.
*/
/* The include and timer setup, and allegro initialization. This is routine stuff! */
#include <allegro.h> // Include the allegro header file.
#include <stdlib.h> // Include the stdlib header for the exit() function.
#define LEFT 3
#define RIGHT 4
#define UP 5
#define DOWN 6
#define TOPL 7
#define TOPR 8
#define BOTL 9
#define BOTR 10
#define CROSS1 11
#define CROSS2 12
#define AIR 13
/* Timer stuff */
volatile long speed_counter = 0; //A long integer which will store the value of the
//speed counter.
void increment_speed_counter() // A function to increment the speed counter
{
speed_counter++; // This will just increment the speed counter by one.
}
END_OF_FUNCTION(increment_speed_counter); //Make sure you tell it that it's the end of the
//function (allegro specific)
int main(int argc, char *argv[])
{
allegro_init(); // Initialize Allegro
install_keyboard(); // Initialize keyboard routines
install_timer(); // Initialize the timer routines
LOCK_VARIABLE(speed_counter); //Used to set the timer - which regulates the game's
LOCK_FUNCTION(increment_speed_counter);//speed.
install_int_ex(increment_speed_counter, BPS_TO_TIMER(60));//Set our BPS
set_color_depth(16); // Set the color depth
set_gfx_mode(GFX_AUTODETECT, 640,480,0,0); // Change our graphics mode to 640x480
/* This bitmap is for double buffering. This is covered in a previous tutorial.
* However, this time, we do error checking - just to make sure the buffer was
* actually created properly. This is something you'll always want to do, since
* you can output an error and let the user know what didn't work!
*/
BITMAP *buffer = create_bitmap(640,480); // Create a buffer for smooth animation.
/* We do a little test here to see if the bitmap was created. If the pointer is
* NULL after calling create_bitmap, then that means it's not pointing to anything,
* thus the create_bitmap did not work.
*/
if(buffer == NULL)
{
/* Here's some new stuff. This sets up allegro to go into a text only mode.
* This ensures that the message you want to display will be shown on any platform.
*/
set_gfx_mode(GFX_TEXT,0,0,0,0);
/* This is a new function. It's called allegro_message. Obviously enough,
* it outputs a message.
*/
allegro_message("Error: Could not create buffer!");
/* This function simply will stop the program since the buffer could not
* be created we do not want to continue.
*/
exit(EXIT_FAILURE);
}
/* Here we load the two bitmap images which we want to test collision for.
* We test to see they were loaded correctly. If not, output an error message
* and exit. This is important. If your images can't load, when you try to
* blit them, your program will crash. Safely exiting is the better choice, by far!
*/
BITMAP *image1 = load_bitmap("image1.bmp", NULL);//Load image 1
if(image1 == NULL)
{
set_gfx_mode(GFX_TEXT,0,0,0,0); //Set the screen mode for allegro messages
allegro_message("Error: Could not load image1.bmp");
exit(EXIT_FAILURE);
}
BITMAP *image2 = load_bitmap("image2.bmp", NULL); //Load image 2
if(image2 == NULL)
{
set_gfx_mode(GFX_TEXT,0,0,0,0); //Set the screen mode for allegro messages
allegro_message("Error: Could not load image2.bmp");
exit(EXIT_FAILURE);
}
/* Next we're going to do a bit of variable declaring. First off, we're going
* to want to know the x and y positions of both images.
*/
int image1_x_position = 0; //Set the x position of image 1 to 0.
int image1_y_position = 0; //Set the y position of image 1 to 0.
int image2_x_position = 0; //Set the x position of image 2 to 100.
int image2_y_position = 416; //Set the y position of image 2 to 100.
/* Next, we need to know where the bounding boxes are for these
* images. This is going to be a little complicated:
* A box (2D square) has four sides, so we will need to have 4 variables.
* Two y coordinates, and two x coordinates.
* The two y coordinates will represent the top and bottom "sides" of the box,
* while the x coordinates will represent the left and right "sides" of the box.
*
* For the purposes of this lesson, we will have the bounding box be the same
* size as the image we are going to apply it to. So, lets set these variables.
* We will need a total of 8. 4 for each image.
*
* Note - bb stands for bounding box
* Here we set the left side of the bounding box to Image 1's x position.
* Why do we do this? Well, we want the bounding box to surround the image at all times,
* so we have to set it to the images current position. The x position of the image just
* so happens to be the same x coordinate of the left side of the box.
*/
int image1_bb_left = image1_x_position;
/* The same idea goes for the top "side" */
int image1_bb_top = image1_y_position;
/* Next we need to set the bottom and right "sides" of the box. Since we are going to
* use the bitmap's dimensions for the bounding boxes, this is simple. Since we don't want
* to have to keep track of the actual size of the image any time we change the image itself
* we can use a data member of the BITMAP structure to find out the width or height of
* a bitmap image dynamically! We can do this using the pointer->w or pointer->h data members.
*
* We know the width of the image. That is: (image1->w)
* If we have the left side of the box, well, the right side is just the image width away
* from the left side.
*/
int image1_bb_right = (image1_bb_left + image1->w);
/* Use the same idea for the bottom. */
int image1_bb_bottom = (image1_bb_top + image1->h);
/* Now, we will create the box for image 2 using the same principles. */
int image2_bb_left = image2_x_position;
int image2_bb_top = image2_y_position;
int image2_bb_right = (image2_bb_left + image2->w);
int image2_bb_bottom = (image2_bb_top + image2->h);
/* This is just a variable to hold the value of whether we want to show the bounding boxes
* or not. We'll see more about it later.
* When setting the initial value of this variable we will use an Allegro constant FALSE.
* There is also the constant TRUE. FALSE and TRUE are useful for doing variable testing,
* as they take away the ambiguity of doing something like
*
* if(variable == 0)
*/
int show_bbox = FALSE;
/* This is just a variable to hold the value of whether there is a collision taking
* place or not. We'll see more about it later.
*/
int collision = FALSE;
/* Okay, now that we've set up all our variables, we need to make our loop. You've
* seen this before; it should be familiar.
*/
while(!key[KEY_ESC]) // Keep going until we hit escape.
{
while(speed_counter > 0) // Do the logic loop while the speed counter is > 0.
{
/* Things get a little tricky in here. We want to be able to move both images.
* The controls are described at the beginning of this lesson. Lets make some
* statements to take care of moving the images.
*
* Notice how each test is a standalone "if" statement? This will allow for
* diagonal movement without explicitly coding it in.
*/
/* Key checks for image1 */
if((key[KEY_LEFT] && collision==FALSE) || (key[KEY_LEFT] && (collision==LEFT || collision==UP || collision==DOWN || collision==TOPL || collision==TOPR)))
image1_x_position --; //Move image 1 left.
if(key[KEY_RIGHT] && collision==FALSE || (key[KEY_RIGHT] && (collision==UP || collision==DOWN || collision==LEFT || collision==TOPL || collision==TOPR)))
image1_x_position ++; //Move image 1 right.
if((key[KEY_DOWN] && (collision==DOWN || collision==RIGHT || collision==LEFT)))
image1_y_position ++; //Move image 1 down.
if((key[KEY_UP] && (collision==UP || collision==LEFT || collision==RIGHT || collision==TOPL || collision==TOPR)))
image1_y_position --; //Move image 1 up.
if(collision!=UP || collision!=DOWN || collision!=LEFT || collision!=RIGHT || collision!=TOPL || collision!=TOPR || collision!=BOTL || collision!=BOTR)
image1_y_position ++;
/* Here's where we use show_bbox.
* If the space key is held down, it will show. Otherwise, it wont.
* This only works when the space key is held down.
*/
if(key[KEY_SPACE])
show_bbox = TRUE;
else if(!key[KEY_SPACE])
show_bbox = FALSE;
/* Things get a little tricky here. Since the player can move the images,
* we need to update the locations of the bounding boxes. Lets do it just
* like we did when we initialized them, using the same ideas and principles
*/
/* Update image 1's bounding box */
image1_bb_left = image1_x_position;
image1_bb_top = image1_y_position;
image1_bb_right = (image1_bb_left + image1->w);
image1_bb_bottom = (image1_bb_top + image1->h);
/* Update image 2's bounding box. */
image2_bb_left = image2_x_position;
image2_bb_top = image2_y_position;
image2_bb_right = (image2_bb_left + image2->w);
image2_bb_bottom = (image2_bb_top + image2->h);
/* Alright, so now we finally get to actually detecting collisions.
* This could easily be written to it's own function, but I will flesh
* it out in the logical loop for sake of keeping everything together.
*
* The way we check for collisions is simple. First, we assume there is no collision.
* Then we check to see if any corners of the first bounding box are inside or on the
* boundary of the second box. Next, we do the same for the second box: check to make
* sure none of its corners are inside or on the boundary of the first box. Then we
* must take into account a special case where we have a "cross" pattern, where no
* corners are within another box, but there is still a collision.
*/
collision = FALSE; // Assume that there is no collision
/* Notice the next chunk of if/else if statements. This chunk is
* for checking box 1 against box 2
*/
/* Test the top left corner of the first box to the second
* (Is the corner in or on the second box?)
*/
if(
((image1_bb_left == image2_bb_right) && (image1_bb_top == image2_bb_bottom) )
)
{
collision = LEFT;
}
/* Test the top right corner of the first box to the second
* (Is the corner in or on the second box?)
*/
if(
((image1_bb_right == image2_bb_left) && (image1_bb_top == image2_bb_bottom))
)
{
collision = RIGHT;
}
/* Test the bottom left corner of the first box to the second
* (Is the corner in or on the second box?)
*/
if(
(image1_bb_top == image2_bb_bottom)
)
{
collision = DOWN;
}
/* Test the bottom right corner of the first box to the second
* (Is the corner in or on the second box?)
*/
if(
(image1_bb_bottom == image2_bb_top)
)
{
collision = UP;
}
/* Now check to see if any corners of box two are inside or on the boundary
* of box 1. This is good for cases where box 2 is much smaller than box 1
*/
/* Test the top left corner of the second box to the first box
* (Is the corner in or on the first box?)
*/
if(
(image2_bb_left >= image1_bb_left) && (image2_bb_top >= image1_bb_top) &&
(image2_bb_left <= image1_bb_right) && (image2_bb_top <= image1_bb_bottom)
)
{
collision = TOPL;
}
/* Test the top right corner of the second box to the first box
* (Is the corner in or on the second box?)
*/
else if(
(image2_bb_right <= image1_bb_right) && (image2_bb_right >= image1_bb_left) &&
(image2_bb_top >= image1_bb_top) && (image2_bb_top <= image1_bb_bottom)
)
{
collision = TOPR;
}
/* Test the bottom left corner of the second box to the first box
* (Is the corner in or on the second box?)
*/
else if(
(image2_bb_right <= image1_bb_right) && (image2_bb_right >= image1_bb_left) &&
(image2_bb_bottom <= image1_bb_bottom) && (image2_bb_bottom >= image1_bb_top)
)
{
collision = BOTL;
}
/* Test the bottom right corner of the second box to the first box
* (Is the corner in or on the second box?)
*/
else if(
(image2_bb_right <= image1_bb_right) && (image2_bb_right >= image1_bb_left) &&
(image2_bb_bottom <= image1_bb_bottom) && (image2_bb_bottom >= image1_bb_top)
)
{
collision = BOTR;
}
/* Now we must test for the cross pattern. */
/* Check box 1 for a horizontal cross on box 2
* (This covers a vertical cross for box 2 on box 1)
*/
if(
(image1_bb_top > image2_bb_top) && (image1_bb_bottom < image2_bb_bottom) &&
(image2_bb_left > image1_bb_left) && (image2_bb_right < image1_bb_right)
)
{
collision = CROSS1;
}
/* Check box 2 for a horizontal cross on box 1
* (This covers veritcal cross for box 1 on box 2)
*/
if(
(image2_bb_top > image1_bb_top) && (image2_bb_bottom < image1_bb_bottom) &&
(image1_bb_left > image2_bb_left) && (image1_bb_right < image2_bb_right)
)
{
collision = CROSS2;
}
/* And we're done! */
speed_counter --; // Decrement the speed counter.
} // This is the closing bracket to the (speed_counter > 0) test.
/* Time for the drawing stuff! */
draw_sprite(buffer, image1, image1_x_position, image1_y_position); //Draw image1
draw_sprite(buffer, image2, image2_x_position, image2_y_position); //Draw image2
/* Here we do the drawing of the bounding box if the player is hitting space. */
if(show_bbox == TRUE)
{
/* This uses the simple line drawing function of allegro.
* It should be quite self explanitory.
*
* line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
*
* How we draw the box is quite simple. We draw four lines:
*
* A line from the left coordinate to the right coordinate with it's y
* coordinate corrosponding to the top y coordinate.
*
* Another line with the same idea, except using the bottom y coordinate.
*
* A line from the top coordinate to the bottom coordinate with it's x
* coordinate corrosponding to the left x coordinate.
*
* Another line with the same idea, except using the right x coordinate.
*/
/* Draw image1's bounding box. */
line(buffer, image1_bb_left, image1_bb_top, image1_bb_right, image1_bb_top, makecol(255,0,0));
line(buffer, image1_bb_left, image1_bb_bottom, image1_bb_right, image1_bb_bottom, makecol(255,0,0));
line(buffer, image1_bb_left, image1_bb_top, image1_bb_left, image1_bb_bottom, makecol(255,0,0));
line(buffer, image1_bb_right, image1_bb_top, image1_bb_right, image1_bb_bottom, makecol(255,0,0));
/* Draw image2's bounding box. */
line(buffer, image2_bb_left, image2_bb_top, image2_bb_right, image2_bb_top, makecol(255,0,0));
line(buffer, image2_bb_left, image2_bb_bottom, image2_bb_right, image2_bb_bottom, makecol(255,0,0));
line(buffer, image2_bb_left, image2_bb_top, image2_bb_left, image2_bb_bottom, makecol(255,0,0));
line(buffer, image2_bb_right, image2_bb_top, image2_bb_right, image2_bb_bottom, makecol(255,0,0));
} /* Just the closing bracket... */
/* Now, if there's a collision, we want to state it! */
if(collision == TRUE)
{
/* This is the allegro text printing function. It prints text to the screen.
* It's pretty self explanitory
*
* textprintf_ex(bitmap, font, x_pos, y_pos, color, transparency, "string", formatting stuff); It works just like C style printf.
*/
textprintf_ex(buffer, font, 0,0, makecol(255,255,255), -1, "Collision!");
}
/* Do the normal blitting of the buffer. */
blit(buffer, screen, 0, 0, 0, 0, 640, 480);//Blit the buffer
clear(buffer);//Clear the buffer
release_screen();//Release the screen
}
/* Destroy bitmaps and quit. */
destroy_bitmap(buffer);
destroy_bitmap(image1);
destroy_bitmap(image2);
return 0;
}
END_OF_MAIN()
Allegro collision detection[fixed]
I've been editing the code from this page:
http://www.loomsoft.net/resources/alltut/alltut_lesson8.htm
This is my edited code:
The horizontal collision works fine when I move up against the block manually, but when I try to get the code to force the image down into the block when the image is airborne, the image just moves right through the block.
Any idea how I can fix this? Also, are there any free tutorials that will help me with collision detection in general?
[Edited by - Vegetable411 on August 11, 2005 3:12:10 PM]
Here's a pretty good article on collision detection for 2D games. That should get you started. http://www.gamedev.net/reference/articles/article735.asp
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement