GML [SOLVED] Per-pixel Destructible Bases using surfaces - only part of the image gets destroyed

frootloop69

Member
Hi all, hoping someone can help me out here.
I'm making a space invaders type game and I want to add per-pixel destructible bases - something similar to the original Space Invaders game.
Its mostly working, but I run into a strange problem. The bases seem to get destroyed fine up until the last few rows of pixels, and then no further.
I end up with a rectangle that the bullets collide against and are destroyed, but the base remains the same.
I've attached a screenshot to illustrate what I mean .
I'm using precise collision and collision masks for the bullet and the base.

Currently my code is as such:

Base object:
Code:
//Create event
surfBase = noone;
sprite_index = sprite_duplicate(sprShield);

//Collide with bullet event
    if !(surface_exists(surfBase))
    {
    surfBase = surface_create(sprite_width, sprite_height);  
  
    }

    surface_set_target(surfBase); //  surface as draw target
    draw_clear_alpha(c_black,1); // clear the surface
    draw_sprite(sprite_index,0,0,0); //draw the current base
    gpu_set_blendmode(bm_subtract); // get ready to cut out
    draw_set_color(c_black);
    draw_sprite(other.sprite_index,0,  other.x -x,  other.y - y ); // cut out the colliding bullet

    gpu_set_blendmode(bm_normal);
  
    var oldspr = sprite_index;
    sprite_index=sprite_create_from_surface(surfBase,0,0,sprite_width,sprite_height,false,false,0,0); //create a new sprite from the surface after we cut out the bullet shape
    sprite_collision_mask(sprite_index, false,0,0,sprite_width,sprite_height,0,bboxkind_precise,0) // create a new precise collision mask
    sprite_delete(oldspr); // delete the old sprite

    surface_reset_target();
    surface_free(surfBase)
Bullet Object

Code:
//Create event
vspeed = -4.5;
cnt = 0;

//Collide with base event
cnt++; // increment every collision - we need the bullet to penetrate the base before being destroyed
if cnt >= 1
{
    instance_destroy(self);  
  
}
Would appreciate any help or insights. I feel like I'm missing something simple or misunderstanding something fundamental.
Thanks you in advance for your time and expertise!
 

Attachments

Last edited:

frootloop69

Member
Are you sure your base has priority over the bullet? Sounds like the bullet destroys itself before the base detects it. You should have the base destroy the bullet.
When you say the base has priority over the bullet, could you expand on that? Would that be controlled by the instance id? I'm going to try having the base destroy the bullet as you outlined - will post the result. Thank you !
 

frootloop69

Member
Are you sure your base has priority over the bullet? Sounds like the bullet destroys itself before the base detects it. You should have the base destroy the bullet.
Tried having the bullet destroyed in the base's collision event - same result. I think it is something to do with the destruction of the bullet though. If I don't destroy the bullet at all, it destroys the pieces of the base in its path as expected. I've attached another screenshot to demonstrate.
If I have the bullet destroy itself on collision with the base - the bullets never penetrate the base, and I end up with just shallow cutouts on the edge of the base the bullet hits.
When I have the bullet destroy itself the second time or greater the collision activates, the first few bullets seem to destroy the base correctly - to a point, as per my first posting.
Scratching my hair on this one.
Is there a way of drawing the collision mask to debug maybe? I'll google that next. Maybe the collision mask isnt 'precise' even though I'm creating it as such?
 

frootloop69

Member
So added the following code to get a rough idea of the sprite mask:
Code:
//Added to draw event of base object
draw_set_color(c_red);
draw_rectangle(bbox_left, bbox_top, bbox_right, bbox_bottom, true);
What it shows me is that the bounding box, and I'm guessing by extension the collision mask, is remaining the same overall size on the x and y axes, regardless of what is being destroyed (e.g I destroyed the the entire bottom row of the base, or right column of the base). Added a screenshot to demonstrate.
Does this mean I'm doing something wrong when creating the collision mask?
Thanks!
 

Attachments

TheouAegis

Member
The packet even works to begin with suggest that's not the problem, but a simple fix if that is the problem might be to set argument one to true and then everything else is 0. According to the manual, apparently bbox_precise has a value of 0, but maybe you can keep using the constant instead. But typically when you do precise collisions, you want separate masks for each sub image, which seems ridiculous if there's only one subimage but who knows, it could be a glitch in GM. And when doing precise collisions, the size of the Mask itself doesn't even matter, which is why in the example in the manual they just put zero for all four dimensions.
 

frootloop69

Member
The packet even works to begin with suggest that's not the problem, but a simple fix if that is the problem might be to set argument one to true and then everything else is 0. According to the manual, apparently bbox_precise has a value of 0, but maybe you can keep using the constant instead. But typically when you do precise collisions, you want separate masks for each sub image, which seems ridiculous if there's only one subimage but who knows, it could be a glitch in GM. And when doing precise collisions, the size of the Mask itself doesn't even matter, which is why in the example in the manual they just put zero for all four dimensions.
Ah, thanks for clearing that up. Looks like thats not the problem then. Tried setting the subimage to true and the other params to 0 as per your suggestion, and get the same result. Guess I'm back at square one on figuring out whats happening.
 
Last edited:

frootloop69

Member
A bit more investigation and I'm back to to thinking the collision mask (!)
I set the bullet origin to the mouse, just to be able to try different positions :

- If start the bullet on the base I get the expected cut out (see no 4 on the attached screenshot)
- I can destroy any part of the base if I start the bullet there (see no 2 and 4 on attached screenshot)
- If I destroy half the base (see 1 on screenshot) I can see that any shots from outside the base still collide where the bbox is drawn in red, even though the base there is destroyed. SO it looks like the collision mask is not matching the blue area, but the full original rectangular area.

I'm wondering if its something to do with the transparent colour when I create the sprite from surface - collision mask follows every pixel that has transparency above 0. I'm wodnering if my base sprite has no transparency somehow? or if this is being overwritten by the fill when I create the surface?

Going to change the room background colour to contrast and investigate further.
 

Attachments

frootloop69

Member
Think I've solved it! I had the alpha on the draw_clear_alpha set to 1 instead of 0 (transparent). Going to test a bit more and see if this fixes it!
 
Top