• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GameMaker How can I do chain explosions on a grid? [SOLVED]

A

AChion

Guest
So, I have this tetris like game that I hope to get ready for sale at some point. And it's perfectly functional to one point. if you complete a line, all the blocks placed in that line are cleared.

Then I added this red block that, when it is part of a line that gets cleared, it blows up an area of 3x3 (for now). My question is, is it possible to have it so that if, say there is another red block in this space, can it trigger the 3x3 explosion in tandem?

Down here is a image of the relevant part of the script that runs every time a piece is placed and much below is a small video of an example of how it plays.

Reff code.jpg


Reference video: https://youtu.be/ZI4egG8WfcM

Thank you very much
 

Rob

Member
The video's unavailable but if you're doing a grid based game then I'm assuming every grid cell knows what type of block is inside it?

All you'd need to do is use a for loop to check if any of the 8 surrounding cells have a red block and if so, trigger the explosion in that block too.

You actually already have the for loop code in place - for (Boom_C = -1; Boom_C <= 1; Boom_C ++) - but you just need to tell any red blocks found to do their own explosions.

I don't see the actual "explosion" code in your SS so that's my best answer for now.
 
A simple solution (not saying it's the best though) would be to have the exploding object create an invisible object in it's destroy event, that has a sprite set to the dimensions of the explosive area. Have it check for a collision with other explosive objects, and then either
(a) destroy it and cause the object it collided with to destroy - which repeats the creation process, and chains it until there are no more in range
or
(b) there is no other object in range to hit, and it just destroys itself. I would think an alarm set to a short time in it's create event (maybe 1 step) would still allow for it to register any collisions and do the required action, whilst still deleting itself not long after.
 
A

AChion

Guest
The video's unavailable but if you're doing a grid based game then I'm assuming every grid cell knows what type of block is inside it?

All you'd need to do is use a for loop to check if any of the 8 surrounding cells have a red block and if so, trigger the explosion in that block too.

You actually already have the for loop code in place - for (Boom_C = -1; Boom_C <= 1; Boom_C ++) - but you just need to tell any red blocks found to do their own explosions.

I don't see the actual "explosion" code in your SS so that's my best answer for now.
Are you sure you can't see it? i clicked on the link there and it took me to the vid. Hm, Can you try it again now? Hopefully it'll work. Yes, there is an empty grid space and a block that it's assigned to that space.

The issue that I have is that as it is written right now, it'll probably ignore any other red blocks in the area. And if i make it to prioritize the newest block to finish the explosion, it might break the process it has going for it.

Also, there is no explosion code in the SS, much later in the same script, there is another loop that checks every space and goes and nukes the blocks from the spaces that have "myblockismarked" as true.
 
A

AChion

Guest
A simple solution (not saying it's the best though) would be to have the exploding object create an invisible object in it's destroy event, that has a sprite set to the dimensions of the explosive area. Have it check for a collision with other explosive objects, and then either
(a) destroy it and cause the object it collided with to destroy - which repeats the creation process, and chains it until there are no more in range
or
(b) there is no other object in range to hit, and it just destroys itself. I would think an alarm set to a short time in it's create event (maybe 1 step) would still allow for it to register any collisions and do the required action, whilst still deleting itself not long after.
So I could just go around create a separate script that nukes all marked and then have an actual collision explosion that changes things to marked. The script is called and if one of the marked happens to be a red block then call it again? Something like that?
 
EDIT EDIT: Doh! You're not actually using objects? I have misunderstood what you're doing....

So I could just go around create a separate script that nukes all marked and then have an actual collision explosion that changes things to marked. The script is called and if one of the marked happens to be a red block then call it again? Something like that?
Just do a collision event between the invisible object and the explosive object. The invisible one has a sprite set to the size of the explosion area, but has visibility set to false. Even when not drawn it can still be registered in collision checking, as long it has a sprite and collision mask.

EDIT:
explosive object destroy event:
Code:
instance_create(x, y, obj_invisible)
create event obj_invisible:
Code:
alarm[0] =1;
alarm[0] obj_invisible
Code:
instance_destroy()
collision event in obj_invisible, with explosive object
Code:
with (other)
{instance_destroy();}
I think would do it :)
 
Last edited:
A

AChion

Guest
EDIT EDIT: Doh! You're not actually using objects? I have misunderstood what you're doing....


Just do a collision event between the invisible object and the explosive object. The invisible one has a sprite set to the size of the explosion area, but has visibility set to false. Even when not drawn it can still be registered in collision checking, as long it has a sprite and collision mask.

EDIT:
explosive object destroy event:
Code:
instance_create(x, y, obj_invisible)
create event obj_invisible:
Code:
alarm[0] =1;
alarm[0] obj_invisible
Code:
instance_destroy()
collision event in obj_invisible, with explosive object
Code:
with (other)
{instance_destroy();}
I think would do it :)

Does that mean it cant work? Cause i get the feeling that there's no way to keep it in the script.

Objects might be worth trying, though Im worrying of getting into troubles by not triggering things when they should
 
It can be done in script, but I can only think of lots and lots of repetition to do it.

Checking to see if a cell is "explosive", then looping through the area it covers when triggered. If any of those cells are explosive you log them, and then loop through the areas they cover one by one. If any of those cells are explosive you repeat the process, and so on, and so on, until none of the checked cells are explosive.

I'm not sure my suggestion of using objects really translates into how you're doing this. But I would hazard a guess that collision checks aren't any more expensive performance wise than the "serious coding" method, and may even be cheaper. If they are more expensive, but you have plenty of overhead for the extra cost, then I would go for the simpler solution (if it works within how you have arranged things) just to avoid the hassle.
 

Bentley

Member
Here's an idea. I did not test it so there my be an infinite loop : (
Pseudo code
Code:
// "j" is the the row you cleared

// You just cleared a row
if (clear_row)
{
    // Loop through each column in the row
    for (var i = 0; i < horz_cells; i++)
    {
        // If there's an exploding cell
        if (grid[# i, j] == exploder)
        {
            // Recursively look for explosive cells
            explosion(i, j);
        }
    }
}

/// @function explosion(x, y)
/// @arg xx
/// @arg yy

xx = arg0;
yy = arg1;

// I think this should prevent the explosion function from re-exploding an exploded cell
grid[# xx, yy] = empty;

// Check 3 away in all directions
for (var i = -3; i <= 3; i++)
{
    for (var j = -3; j <= 3; j++)
    {
        // Ignore the calling exploder and cells outside the grid
        if ((i == 0 && j == 0) || !point_in_rectangle(xx + i, yy + j, 0, 0, horz_cells - 1, vert_cells - 1))
        {
            continue;
        }
               
        // Blow up non exploders
        if (grid[xx + i, yy + j] != exploder)
        {
            grid[xx + i, yy + j] = boom;
        }
   
        // You found another exploder, have it call this script using its coordinates
        else //else if (grid[xx + i, yy + j] == exploder)
        {
            explosion(xx + i, yy + j);        
        }
    }
}
Sorry if its crap, wrote it on the way out. Idea might help though.
 
Last edited:
A

AChion

Guest
Here's an idea. I did not test it so there my be an infinite loop : (
Pseudo code
Code:
// "j" is the the row you cleared

// You just cleared a row
if (clear_row)
{
    // Loop through each column in the row
    for (var i = 0; i < horz_cells; i++)
    {
        // If there's an exploding cell
        if (grid[i, j] == exploder)
        {
            // Recursively look for explosive cells
            explosion(i, j);
        }
    }
}

/// @function explosion(x, y)
/// @arg xx
/// @arg yy

xx = arg0;
yy = arg1;

// I think this should prevent the explosion function from re-exploding an exploded cell
grid[xx, yy] = empty;

// Check 3 away in all directions
for (var i = -3; i <= 3; i++)
{
    for (var j = -3; j <= 3; j++)
    {
        // Ignore the calling exploder and cells outside the grid
        if ((i == 0 && j == 0) || !point_in_rectangle(xx + i, yy + j, 0, 0, horz_cells - 1, vert_cells - 1))
        {
            continue;  
        }
                 
        // Blow up non exploders
        if (grid[xx + i, yy + j] != exploder)
        {
            grid[xx + i, yy + j] = boom;  
        }
     
        // You found another exploder, have it call this script using its coordinates
        else if (grid[xx + i, yy + j] == exploder)
        {
            explosion(xx + i, yy + j);          
        }
    }
}
Sorry if its crap, wrote it on the way out. Idea might help though.
Thank you very much. This feels like the closest to a solution. Call in the variable within a variable. I just have to think up of a way of keeping the two explosions triggering each other indefinitely. Perhaps with a boolean?
 

Bentley

Member
Thank you very much. This feels like the closest to a solution. Call in the variable within a variable. I just have to think up of a way of keeping the two explosions triggering each other indefinitely. Perhaps with a boolean?
by setting grid[# xx, yy] to empty. That should prevent that explosion from re-triggering.
 

TheouAegis

Member
You could do it all with just the one grid of data. Just set one of the unused bits. If you need the explosions to happen over multiple steps, use multiple bits. I doubt grid[xx,yy] is using even 5 bits right now as it is. I'm just saying, maybe he might want to have more than just exploders later, then he'd need another grid, and maybe another, and another. Or he'd be cross-referencing grids over and over.
Just something.
 
A

AChion

Guest
A small quick addition. I added the following
Code:
    /// clear lines
   ///// Check last column.
   for (L = 0; L < Gr_Height; L++)
   {
       if (ds_grid_get(Game_Field, Gr_Width, L) == 1)
       {
           for (C = 0; C < Gr_Width; C++)
           {
               var space = ds_grid_get(Game_Field,C,L)
           
               if (space.IsOcupied)
               {
                    //boom block
                   if (space.Myblock.AssignedVal== BlockQuality.Explosive)
                   {
                       /// first tag the bomb so that it's not       
                       space.Myblockismarked = true
                       Scr_GridExploder(space)
                       

                   }
                   
                   else
                   {
                       space.Myblockismarked = true
                   }
               }
           
           }   
           SpcBlockBombCounter++;
       
       }
   }

and this is Scr_GridExploder(space)

Code:
var Centerspace = argument0;
randomize();
var range =1 //random_range(1,2)

var space
for (Boom_C = -(range); Boom_C <=(range); Boom_C ++)
{
   for (Boom_L = -(range); Boom_L <=(range); Boom_L ++)
   {
       if ((Centerspace.ID_x +Boom_C>=0) && (Centerspace.ID_x+Boom_C < Gr_Width))&&
           ((Centerspace.ID_y +Boom_L>=0) && (Centerspace.ID_y +Boom_L < Gr_Height))
       {
           space = ds_grid_get(Game_Field, C + Boom_C, L + Boom_L)
           
           if (space.IsOcupied)
           {
               
               if (space.Myblock.AssignedVal = BlockQuality.Explosive) && (space.Myblockismarked = false)
               {
                   space.Myblockismarked = true;
                   Scr_GridExploder(space)
               
               }
               else
               {
                   space.Myblockismarked = true;
               }
               
           }
           
                               
       }
                               
   }
}
Scr_Add_Time(10)
Scr_Add_Score(20)
The chain doesnt trigger even though it calls the function again and I feel like im missing something obvious, but after staring at this lines for a while, I had to take a break to refocus.
 

TheouAegis

Member
What code is setting the explosion in the mission? If you have verified that it is indeed marking all explosives within range of the first explosive as being exploded, that means your issue is likely somewhere with how you actually make the explosion animation happen. You should essentially have two loops. The first loop goes through the row and marks all cells in that row, and if it finds an explosive it should change through all the explosives that are nearby that explosive and any normal cells that are also within range of those explosives. Your second separate loop then loops through every cell and if that cell is marked it destroys it, and if the cell is also an explosive, it creates the explosion animation.
 
A

AChion

Guest
The code here worked, I just had to change

space = ds_grid_get(Game_Field, C + Boom_C, L + Boom_L)
to
space = ds_grid_get(Game_Field, Centerspace.ID_x + Boom_C, Centerspace.ID_y + Boom_L)


Code:
var Centerspace = argument0;
randomize();
var range =1 //random_range(1,2)

var space
for (Boom_C = -(range); Boom_C <=(range); Boom_C ++)
{
   for (Boom_L = -(range); Boom_L <=(range); Boom_L ++)
   {
       if ((Centerspace.ID_x +Boom_C>=0) && (Centerspace.ID_x+Boom_C < Gr_Width))&&
           ((Centerspace.ID_y +Boom_L>=0) && (Centerspace.ID_y +Boom_L < Gr_Height))
       {
           space = ds_grid_get(Game_Field, C + Boom_C, L + Boom_L)
         
           if (space.IsOcupied)
           {
             
               if (space.Myblock.AssignedVal = BlockQuality.Explosive) && (space.Myblockismarked = false)
               {
                   space.Myblockismarked = true;
                   Scr_GridExploder(space)
             
               }
               else
               {
                   space.Myblockismarked = true;
               }
             
           }
         
                             
       }
                             
   }
}
Scr_Add_Time(10)
Scr_Add_Score(20)
 
Top