Sign in to follow this  
cwl157

sprite collision detection

Recommended Posts

Hi all, I am just starting with animated sprites and i am trying to make a little "tech demo" as i call it, because its not enough to be a full game but i'm just experimenting. I have several ball sprites that bounce around the screen. The mouse cursor is a target and i want the user to "shoot" the balls. Each time a ball is shot it disappears. I have everything except detecting when the target mouse cursor is over the right sprite to destroy it. It kind of works but sometimes a ball not even close to the target dissapears. There are 3 files, a sprite class, a sprite handler class, and then the main file that holds my program. The sprite and sprite handler classes i got from a game programming book i am reading, they just help me manage the sprite objects. And I am sure these classes are correct and the problem lies inside the main.cpp file, which is the one i made. I am just posting the other classes here as well for reference. Does anyone know how to make the collision detection in this more precise so it only destroys balls that the mouse is on top of? Also, the user has to hit the mouse button in order to "hit" the ball with the target. It is written in C++ and allegro. Thanks. sprite.cpp
#include <allegro.h>
#include "sprite.h"


sprite::sprite() {
    image = NULL;
    alive = 1;
    direction = 0;
    animcolumns = 0;
    animstartx = 0; 
    animstarty = 0;
    x = 0.0f; 
    y = 0.0f;
    width = 0; 
    height = 0;
    xdelay = 0; 
    ydelay = 0;
    xcount = 0; 
    ycount = 0;
    velx = 0.0; 
    vely = 0.0;
    speed = 0.0;
    curframe = 0; 
    totalframes = 1;
    framecount = 0; 
    framedelay = 10;
    animdir = 1;
    faceAngle = 0; 
    moveAngle = 0;
}

sprite::~sprite() {
    //remove bitmap from memory
    if (image != NULL)
        destroy_bitmap(image);
}

int sprite::load(char *filename) {
	image = load_bitmap(filename, NULL);
	if (image == NULL) return 0;
	width = image->w;
	height = image->h;
    return 1;
}

void sprite::draw(BITMAP *dest) 
{
	draw_sprite(dest, image, (int)x, (int)y);
}

void sprite::drawframe(BITMAP *dest)
{
    int fx = animstartx + (curframe % animcolumns) * width;
    int fy = animstarty + (curframe / animcolumns) * height;
    masked_blit(image,dest,fx,fy,(int)x,(int)y,width,height);
}

void sprite::updatePosition()
{
    //update x position
    if (++xcount > xdelay)
    {
        xcount = 0;
        x += velx;
    }

    //update y position
    if (++ycount > ydelay)
    {
        ycount = 0;
        y += vely;
    }
}

void sprite::updateAnimation() 
{
    //update frame based on animdir
    if (++framecount > framedelay)
    {
        framecount = 0;
		curframe += animdir;

		if (curframe < 0) {
            curframe = totalframes-1;
		}
		if (curframe > totalframes-1) {
            curframe = 0;
        }
    }
}

int sprite::inside(int x,int y,int left,int top,int right,int bottom)
{
    if (x > left && x < right && y > top && y < bottom)
        return 1;
    else
        return 0;
}

int sprite::pointInside(int px,int py)
{
	return inside(px, py, (int)x, (int)y, (int)x+width, (int)y+height);
}

int sprite::collided(sprite *other, int shrink)
{
    int wa = (int)x + width;
    int ha = (int)y + height;
    int wb = (int)other->x + other->width;
    int hb = (int)other->y + other->height;

    if (inside((int)x, (int)y, (int)other->x+shrink, (int)other->y+shrink, wb-shrink, hb-shrink) ||
        inside((int)x, ha, (int)other->x+shrink, (int)other->y+shrink, wb-shrink, hb-shrink) ||
        inside(wa, (int)y, (int)other->x+shrink, (int)other->y+shrink, wb-shrink, hb-shrink) ||
        inside(wa, ha, (int)other->x+shrink, (int)other->y+shrink, wb-shrink, hb-shrink))
        return 1;
    else
        return 0;
}


spritehandler.cpp
#include "spritehandler.h"

spritehandler::spritehandler(void)
{
	count = 0;
}

spritehandler::~spritehandler(void)
{
    //delete the sprites
	for (int n = 0; n < count; n++)
		delete sprites[n];
}

void spritehandler::add(sprite *spr) 
{
	if (spr != NULL) {
		sprites[count] = spr;
		count++;
	}
}

void spritehandler::create() 
{
	sprites[count] = new sprite();
	count++;
}

sprite *spritehandler::get(int index)
{
	return sprites[index];
}


main.cpp This is my program
[source="cpp"]
#include "allegro.h"
#include "sprite.h"
#include "spritehandler.h"
#include <ctime>

#define BLACK makecol(0,0,0)
#define WHITE makecol(255,255,255)

#define NUM_BRICKS 100
#define WIDTH 640
#define HEIGHT 480
#define MODE GFX_AUTODETECT_WINDOWED

spritehandler *balls;
int numB = 20;

void bounceSprite(sprite *spr)
{
     if (spr->x < 0)
     {
        spr->x = 0;
        spr->velx = rand() % 2 + 4;
        spr->animdir *= -1;
     } // end if
     
     else if (spr->x > WIDTH-spr->width)
     {
        spr->x = WIDTH - spr->width;
        spr->velx = rand() %2 - 6;
        spr->animdir *= -1;
     } // end else if
     
     else if (spr->y < 0)
     {
        spr->y = 0;
        spr->vely = rand() %2 +4;
        spr->animdir *= -1;
     } // end else if
     
     else if (spr->y > HEIGHT - spr->height)
     {
        spr->y = HEIGHT - spr->height;
        spr->vely = rand() % 2 - 6;
        spr->animdir *= -1;
     } // end else if
} // end bouncingSprite

void checkCollisions(sprite *spr, spritehandler *sprh)
{
     int cx1, cy1, cx2, cy2;
     for (int i = 0; i < numB; i++)
     {
         if (spr->collided(sprh->get(i), 5))
         {
            numB--;
            break;
         } // end if
     } // end for
} // end checkCollisions

int main(void)
{
    
    balls = new spritehandler();
    BITMAP *buffer;
    BITMAP *bg;
    allegro_init();
    set_color_depth(32);
    set_gfx_mode(MODE, WIDTH, HEIGHT, 0, 0);
    install_keyboard();
    install_timer();
    install_mouse();
    srand(time(NULL));
    bg = load_bmp("bg.bmp", NULL);
    //create back buffer
    buffer = create_bitmap(WIDTH, HEIGHT);
    int mb;
    
    for (int i = 0; i < numB; i++)
    {
       sprite *ball = new sprite();
       ball->load("sphere.bmp");
       //set sprite properties
       ball->x = rand() % (WIDTH-64);
       ball->y = rand() % (HEIGHT-64);
       ball->width = 64;
       ball->height = 64;
       ball->velx = rand() %2 + 4;
       ball->vely = rand() %2 + 4;
	   ball->animcolumns = 8;
       ball->curframe = 1;
	   ball->totalframes = 32;
       ball->animdir = 1;
       ball-> framedelay = 2;
    
       balls->add(ball);
    } // end for
    
    //load the mouse cursor
    sprite *mouse = new sprite();
    mouse->load("crosshair.bmp"); 
    scare_mouse();
    set_mouse_sprite(mouse->image);
    set_mouse_sprite_focus(15,15);
    show_mouse(buffer); 
    
    while (!key[KEY_ESC])
    {
            mb = (mouse_b & 1);
            
            blit(bg, buffer, 0, 0, 0, 0, WIDTH, HEIGHT);

            for (int i = 0; i < numB; i++)
            {
               balls->get(i)->updatePosition();
               balls->get(i)->updateAnimation();
		       bounceSprite(balls->get(i));
            } // end for
             
            mouse->x = mouse_x;
            mouse->y = mouse_y;

            if (mb)
            {
                checkCollisions(mouse, balls);
            } // end if
        //draw the sprites
        for (int i = 0; i < numB; i++)
           balls->get(i)->drawframe(buffer);
           
        show_mouse(buffer);
        

        //update the screen
        acquire_screen();
        blit(buffer,screen,0,0,0,0,WIDTH,HEIGHT);
        release_screen();
        rest(10);
    } // end while
    allegro_exit();
    return 0;
} // end main
END_OF_MAIN()

Share this post


Link to post
Share on other sites
Your checkCollisions code, on detecting a collision, just decrements numB. Wouldn't that just cause your sprite handler to drop whichever ball was the last ball in its storage? You want to get rid of a specific ball -- the one that the user clicked on.

Share this post


Link to post
Share on other sites
so then i would keep the checkCollisions function the same but instead of just saying numB-- when there is a collision i would save the index that collision happened at and then destroy the sprite that is stored at that position. So would calling the sprite's destructor do that correctly? The sprite class doesn't have an erase function, or do i just set the sprite's image to NULL and then it won't be drawn. Won't i somehow have to decrement numB anyway because thats the total number of balls so if i destroy one it will still try and draw all of them, would that cause an error? Thanks.

Share this post


Link to post
Share on other sites
Quote:
Original post by cwl157
so then i would keep the checkCollisions function the same but instead of just saying numB-- when there is a collision i would save the index that collision happened at and then destroy the sprite that is stored at that position. So would calling the sprite's destructor do that correctly? The sprite class doesn't have an erase function, or do i just set the sprite's image to NULL and then it won't be drawn. Won't i somehow have to decrement numB anyway because thats the total number of balls so if i destroy one it will still try and draw all of them, would that cause an error? Thanks.


I am not sure about how you will deconstruct the sprite because I dont use C++ but setting the Image to NULL would stop the image from being drawn but the sprite would still "be there" so it would be bad for performance.

If you are using an Array you could just set the sprite at that current position to NULL, and in your code just check if the sprite is null, if it is, dont do anything with it.

This may not be the best advice but I am just letting you know what I thought of at the top of my head as I read your post.

Hope it helps a bit.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this