• 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!

Legacy GM Detecting two collisions at once?

D

Dibidoolandas

Guest
I've come upon this issue a couple of times and always found a workaround, but I feel like there must be an elegant solution. I am working on a platformer, and in this level I have these little rolling hazards that move around and sometimes smash through destructible environmental elements. Here is the code I have on the roller to tell it to smash through destructible terrain:

var hdestroy;
hdestroy = instance_place(x+hsp,y,par_destructible);
if (hdestroy != noone)
{
if (abs(hsp) >= 5) //if the roller is moving fast enough
{
with(hdestroy) hp -= 1; //destroy the terrain
}
}

It's pretty simple, but it works if there's a block in the way. However, if there are two blocks in the way, and it touches both at the same time, it will destroy one and bounce back off the other. Now, elsewhere in my movement code I tell it to bounce off of solid objects, so that's where this behavior comes in. I could post that code, but I don't know that it's relevant and is more complex... Let me know if this doesn't show the whole picture though. I whipped up a quick diagram.roller_bounce.jpg

I think I understand what's happen. As part of the step event, the roller detects it's going to hit the block so it destroys it. However at the same time it detects it's still going to hit that other block, and so it runs its typical bounce-off solid objects code. I need to change the destroy code to basically say if you come into contact with ANY instances of destructible objects in each step, subtract 1 hp from them (destroy them). I'm not sure how to detect that though. Any help would be greatly appreciated. Thanks!
 
D

Dibidoolandas

Guest
Use a do or while loop to repeat the command until there is noting in contact.
I'll admit, I'm pretty dense when it comes to while statements. Whenever I try and use them I crash the game. I tried this, with a resulting crash when the roller hit the block.

Code:
var hdestroy;
hdestroy = instance_place(x+hsp,y,par_destructible);
if (hdestroy != noone)
{
    if (abs(hsp) >= 5)
    {
        while (instance_place(x+hsp,y,par_destructible))
        {
            with(hdestroy) hp -= 1;
        }
    }
}
 

Perseus

Not Medusa
Forum Staff
Moderator
That's so because instance_place returns a single instance ID. You could use this script and iterate through the list to inflict damage on the colliding instances -- don't forget to destroy the list once that is done. A simpler solution will be to detect a collision the other way. So I'd suggest doing something like this:

Code:
var ins = instance_place(x, y, obj_Ball);
if (ins != noone) {
    if (abs(inst.hsp) >= 5) {
        hp -= 1;
    }
}
 

Roderick

Member
I'll admit, I'm pretty dense when it comes to while statements. Whenever I try and use them I crash the game. I tried this, with a resulting crash when the roller hit the block.

Code:
var hdestroy;
hdestroy = instance_place(x+hsp,y,par_destructible);
if (hdestroy != noone)
{
    if (abs(hsp) >= 5)
    {
        while (instance_place(x+hsp,y,par_destructible))
        {
            with(hdestroy) hp -= 1;
        }
    }
}
You got an error because you destroyed hdestroy, then tried to destroy it again without resetting it to a new value.

I just made and tested this code.
Code:
while (instance_place(x, y, par_destructible) != noone)
{
    var hdestroy = instance_place(x, y, par_destructible)
    with hdestroy{instance_destroy();}
}
 
D

Dibidoolandas

Guest
You got an error because you destroyed hdestroy, then tried to destroy it again without resetting it to a new value.

I just made and tested this code.
Code:
while (instance_place(x, y, par_destructible) != noone)
{
    var hdestroy = instance_place(x, y, par_destructible)
    with hdestroy{instance_destroy();}
}
So this is great, thanks for taking the time to test it. It works very well with a slight tweak to add in the "traveling at a certain speed" factor.

The only thing that's weird is that if I try to base this on the hp variable instead of just telling it to instance_destroy(), it crashes the game. Here's the code I'm using:

Code:
if (abs(hsp) >= 5)
{
    while (instance_place(x+hsp,y,obj_destructible) != noone)
    {
        var hdestroy = instance_place(x+hsp,y,obj_destructible)
        with (hdestroy)
        {
            instance_destroy();
        }
    }
}
Now like I said, with this current code, it works perfectly. However if I swap the instance_destroy() out for hp -= 1, it crashes. On the obj_destructible object, its step event tells it that if its hp = 0, to instance_destroy itself. So effectively it should have the same result. The reason I'm making the distinction is because some destructible elements are tougher than others (there are big destructible blocks with 2 hp, etc.).

If I have to go with instance_destroy I could probably make it work, but just was curious if you knew why this would crash it. Is it tripping up on the added step of having to subtract the hp from the object and destroy it on the next step?

Here's the code on the destructible block... nothin' crazy.

Code:
//Destroy if hp reaches 0
if (hp <= 0)
{
    instance_destroy();
    instance_create(x,y,obj_rock_particles);
}
 

Roderick

Member
The only thing that's weird is that if I try to base this on the hp variable instead of just telling it to instance_destroy(), it crashes the game. Here's the code I'm using:
Are you just using hp -= 1? If so, you're trying to reduce the ball's hp, and if you haven't previously set them, it will cause a crash for accessing an undefined variable.

You need to use either hdestroy.hp -= 1 or with (hdestroy) {hp -= 1}
 
D

Dibidoolandas

Guest
Are you just using hp -= 1? If so, you're trying to reduce the ball's hp, and if you haven't previously set them, it will cause a crash for accessing an undefined variable.

You need to use either hdestroy.hp -= 1 or with (hdestroy) {hp -= 1}
No, sry I wasn't more clear. I'm using hp -= 1 in place of instance_destroy in that same block of code, like this:

Code:
if (abs(hsp) >= 5)
{
   while (instance_place(x+hsp,y,obj_destructible) != noone)
   {
       var hdestroy = instance_place(x+hsp,y,obj_destructible)
       with (hdestroy)
       {
           hp -= 1;
       }
   }
}
For the record this crashes the game if the roller enemy touches just one destructible block (and is going over 5 hsp, obviously).

I was wondering if it was possibly getting stuck processing my other code for bouncing off of solid walls elsewhere in the script, so I tried to embed them together and it still crashes in the same way. Hopefully this isn't too confusing... I've captured the bit I think is essential. With this code you'll see the logic for colliding with a wall, checking whether it's destructible or not, and then either destroying it or bouncing off of it.

Code:
if (place_meeting(x+hsp_final,y-incline,par_collide)) //If we're horizontally colliding with a wall
        {
            if (place_meeting(x+hsp_final,y-incline,par_destructible)) //If the wall we're colliding with is destructible
            {
                if (abs(hsp_final) >= 5) //If we're going fast enough
                {
                    while (instance_place(x+hsp_final,y-incline,par_destructible) != noone) //While we're colliding with destructible walls
                    {
                        var hdestroy = instance_place(x+hsp_final,y-incline,par_destructible)
                        with (hdestroy)
                        {
                            //hp -= 1;
                            instance_destroy(); //Swapping this code for the line above crashes the game upon touching the destructible wall;
                        }
                    }
                }
            }
            else //If the wall is not destructible
            {
                while(!place_meeting(x+sign(hsp_final),y-incline,par_collide))
                {
                    x += sign(hsp_final);
                }
                var oldhsp; //Capture the roller's speed as it hits the wall
                oldhsp = hsp;
                if (place_meeting(x,y+35,obj_slope)) //If we're on an incline or decline
                {
                    hsp_final = 0;
                    hsp = 0;
                    hsp = oldhsp * -0.5; //Reverse our direction and cut our speed in half
                }
                else //If we're on flat ground
                {
                    hsp_final = 0;
                    hsp = 0;
                    hsp = oldhsp * -1; //Reverse direction and maintain speed
                }
            }
        }
 

Roderick

Member
My code uses obj_destructible, because that's the standard I'm used to, and I forgot to adjust for your use of par_destructible. You're crashing because obj_destructible isn't a valid object index.

In the future, please post the text of any error messages you get, it makes troubleshooting MUCH easier.
 
Last edited:
D

Dibidoolandas

Guest
My code uses obj_destructible, because that's the standard I'm used to, and I forgot to adjust for your use of par_destructible. You're crashing because obj_destructible isn't a valid object index.

In the future, please post the text of any error messages you get, it makes troubleshooting MUCH easier.
Thanks for the heads up. Curious, I actually don't get any error messages, it literally just locks up/freezes and I have to quit out of Game Maker before I can do anything. I tried running in debug and didn't see any error messages either, am I doing something wrong here?

As far as obj_destructible vs. par_destructible goes, the same thing happens either way. Basically par_destructible is the parent object under which all destructible terrain falls, so it's more flexible because I have multiple destructible environmental blocks. For testing purposes I've switched back and forth between obj_ and par_destructible, since the blocks I'm using in the test are obj_destructible. I tried testing with two destructible blocks under the par_destructible parent - hp -= 1 crashes, instance_destroy will destroy both blocks.

Sorry this is a frustrating problem, I appreciate you taking the time to answer.
 

Roderick

Member
Thanks for the heads up. Curious, I actually don't get any error messages, it literally just locks up/freezes and I have to quit out of Game Maker before I can do anything. I tried running in debug and didn't see any error messages either, am I doing something wrong here?
I see what's going on. You're stuck in an infinite loop.

Because you aren't using instance_destroy() any more, the object stays there, so next time around the loop, it's STILL being collided with, so it loses another hp, and repeats, ad infinitum.

Try this:
Code:
if (abs(hsp) >= 5)
{
 with (par_destructible)
 {
  if place_meeting(x - other.hsp, y, other)
  {
   hp -= 1;
  }
 }
}
In a with() statement, other means "The object running the code outside the with block". So when the ball is moving fast enough, it checks every destructible to see if they're about to collide, and damages them. The downside is that it checks EVERY destructible in the level, even if they're a mile away and off screen. If you have too many objects, this could create some noticeable slowdown.
 
D

Dibidoolandas

Guest
I see what's going on. You're stuck in an infinite loop.

Because you aren't using instance_destroy() any more, the object stays there, so next time around the loop, it's STILL being collided with, so it loses another hp, and repeats, ad infinitum.

Try this:
Code:
if (abs(hsp) >= 5)
{
 with (par_destructible)
 {
  if place_meeting(x - other.hsp, y, other)
  {
   hp -= 1;
  }
 }
}
In a with() statement, other means "The object running the code outside the with block". So when the ball is moving fast enough, it checks every destructible to see if they're about to collide, and damages them. The downside is that it checks EVERY destructible in the level, even if they're a mile away and off screen. If you have too many objects, this could create some noticeable slowdown.
This works! Thanks so much for taking the time. I think there are a few areas where I can use this in my code, so I'll try and learn from it.

As far as using up resources goes - would it still check against the destructible objects if I deactivated them? I have code to activate enemies when they're within view, maybe I could do a similar thing for destructible terrain, so it only checks 'other' against the terrain in the view?
 
Top