sprite collision detection

Started by
5 comments, last by cwl157 14 years, 9 months ago
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()

Advertisement
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.
Jetblade: an open-source 2D platforming game in the style of Metroid and Castlevania, with procedurally-generated levels
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.
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.
Hi. I made a Sprite class my self. If you want the source code, send me a PM. It's in SDL but it's fairly easy to be changed.
Swattkid,
That sounds like it will work. I will try it tonight and let you know.

Thanks.
Yep that worked like a charm. Thanks.

This topic is closed to new replies.

Advertisement