• 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 Help! How do I prevent splash damage/enemy activation through walls?

S

Sursion

Guest
Hello,

I am creating a top down shooter, and I have objects acting as collision meshes along walls. These prevent actors and projectiles from going through them, however, other things that aren't objects still go through them. For example, my splash damage system, or enemies activating rooms away from the player.

Currently the way the splash damage works: In the destroy event, the object checks for all instances of obj_enemy, then calls for hp -= other.damage from all instances within a set range, say 250.

This works great, except when you put the grenade or explosive next to a collision mesh, it still subtracts HP from enemies on the other side of it. Now, I figure a way around this is to make the explosive fire out actual objects as shrapnel, so that they can be destroyed when they hit the collision mesh. But this seems messy and CPU heavy. I figure there has to be an easier way?

Same goes for enemy activation, they activate when the player is in range, but this works through walls also, making most enemies activate at the beginning of a map rather than room by room. What is a way around this? I am fairly new to programming.
 

FrostyCat

Redemption Seeker
Use collision_line() or an equivalent function to check the line segment from the damage source to the current instance of obj_enemy. Deal the damage only when this line segment is free from splash-blocking things.
 
S

Sursion

Guest
Use collision_line() or an equivalent function to check the line segment from the damage source to the current instance of obj_enemy. Deal the damage only when this line segment is free from splash-blocking things.
Thanks for the quick reply! I will give this a try when I get home tonight.
 
S

Sursion

Guest
So I have tried it out, but it still isn't working. I read the documentation, but I haven't managed to make it work. Here's what I have in the object's destroy event:

totalTargets = instance_number(obj_enemy) - 1;
epicenter_X = x;
epicenter_Y = y;

//Splash Damage Target Finder
for (i = totalTargets; i >= 0; i--)
{
target = instance_find(obj_enemy,i);
distance = point_distance(epicenter_X, epicenter_Y, target.x, target.y)
if (distance < zone_01)
{
if collision_line(epicenter_X,epicenter_Y,target.x,target.y,obj_enemy,false,false)
{
target.hp -= damage;
instance_create_layer(target.x,target.y,"effects",obj_explosion);
}
}
}

I took this to mean that, if the collision_line leaves the grenade (epicenter X and Y), towards target.x and y (the instance found at the start of the code), then the damage should be dealt. But enemies on the other side of the collision mesh, the walls, is still being destroyed. What am I missing??
 

vdweller

Member
Collision line doesn't return true or false but rather the id of the instance the line collided with (or none, if no such instance collided with the line). Same goes with collision_point etc. This actually makes your life easier!
 
Well, TECHNICALLY, that doesn't break it, because GM resolves everything below 0.5 (I think it is) as false and everything above as true and noone actually returns as -4 (meaning noone = false...for now). Though if you do rely on this, your game will get screwed when(if) they introduce true booleans.

However, the code is slightly wrong in general. Something like this should work:
Code:
//Splash Damage Target Finder
for (var i = totalTargets; i >= 0; i--)
{
   var target = instance_find(obj_enemy,i); // I changed it to a var because it's inside a for loop and there's no reason to create an instance variable for this, also it would probably be better to use a with(obj_enemy) statement rather than a for loop and simply check if their distance is within the explosion radius, but whatever
   var distance = point_distance(epicenter_X, epicenter_Y, target.x, target.y)
   if (distance < zone_01)
   {
      if (collision_line(epicenter_X,epicenter_Y,target.x,target.y,obj_COLLISION_OBJECT,false,true) == noone) // obj_COLLISION_OBJECT is a placeholder for whatever object you are using for the walls
      {
         target.hp -= damage;
         instance_create_layer(target.x,target.y,"effects",obj_explosion);
      }
   }
}
The point of this collision_line is to only apply damage if the collision line does NOT find any blocking objects between it and the target. Also, learn to scope your variables, your i variable in the for loop, the target variable AND the distance variable should NOT be instance variables (it will not break it if they are, but it is bad coding form).
 

vdweller

Member
Well, TECHNICALLY, that doesn't break it, because GM resolves everything below 0.5 (I think it is) as false and everything above as true and noone actually returns as -4 (meaning noone = false...for now). Though if you do rely on this, your game will get screwed when(if) they introduce true booleans.

However, the code is slightly wrong in general. Something like this should work:
Code:
//Splash Damage Target Finder
for (var i = totalTargets; i >= 0; i--)
{
   var target = instance_find(obj_enemy,i); // I changed it to a var because it's inside a for loop and there's no reason to create an instance variable for this, also it would probably be better to use a with(obj_enemy) statement rather than a for loop and simply check if their distance is within the explosion radius, but whatever
   var distance = point_distance(epicenter_X, epicenter_Y, target.x, target.y)
   if (distance < zone_01)
   {
      if (collision_line(epicenter_X,epicenter_Y,target.x,target.y,obj_COLLISION_OBJECT,false,true) == noone) // obj_COLLISION_OBJECT is a placeholder for whatever object you are using for the walls
      {
         target.hp -= damage;
         instance_create_layer(target.x,target.y,"effects",obj_explosion);
      }
   }
}
The point of this collision_line is to only apply damage if the collision line does NOT find any blocking objects between it and the target. Also, learn to scope your variables, your i variable in the for loop, the target variable AND the distance variable should NOT be instance variables (it will not break it if they are, but it is bad coding form).
It is important for someone to know what a function returns, because although GM automagically resolves certain stuff to true or false, if you believe this function returns a boolean and go with this,
if (collision_line(foo)==true)
Then things won't work.

I believe that, there is a serious chance to change the way booleans are represented, as you correctly said. However when it comes to function returns I wouldn't say that it will screw a project, as one can easily add a helper function eg collision_lineX to adapt the outcome to whatever the game already uses. Evaluating general expressions though would be a bit problematic.

Also the "bad form" thing is pretty unnecessary and scary for newcomers, these forums are flooded with sanctimonious crusades about the zen of coding, often giving wrong advice. Just let people find their own voice, no one codes now the way they coded 15 years ago and it takes a natural, flowing conscious effort to improve instead of reading forum reprimands largely unrelated to their inquiries.

EDIT: I forgot to say good job on finding the issue, I believe it is as you replied!
 
Last edited:
Not understanding variable scope can result in things breaking. It is a necessary skill to learn. Pointing that out is -helpful-. If you don't, people will run into a million more problems than otherwise. I (and many others on this forum) have responded to variable scoping issues a million times. If I bring it up now, it is to prevent that from happening in the future. Helping/tutoring includes pointing out when things are wrong.

What if they have an instance variable called distance they are using in the step event? Their incorrect variable scoping will overwrite that variable and they will not have a clue as to why.

Also, I was simply pointing out the -reason- why the function could potentially still seem like it was working ok while the user is treating it as a boolean rather than what it actually returns. And sure, you can fix it later if they introduce true booleans? Did I say you couldn't? Doesn't mean that it won't break things until you do. I'm not entirely sure what the entire goal of your post was? Wake up on the wrong side of the bed?
 

FrostyCat

Redemption Seeker
Also the "bad form" thing is pretty unnecessary and scary for newcomers, these forums are flooded with sanctimonious crusades about the zen of coding, often giving wrong advice. Just let people find their own voice, no one codes now the way they coded 15 years ago and it takes a natural, flowing conscious effort to improve instead of reading forum reprimands largely unrelated to their inquiries.
Genuine bad forms in programming are considered bad because they have a propensity to cause real problems. Knowing when and how to avoid them is essential at every learning stage. Everything is "scary" when you're new, but if the damage has to be done, I'd rather it be advice for avoiding mistakes than real mistakes.

Sure, some "bad forms" are just people being closed-minded (e.g. Allman users calling K&R "bad form"), but other legitimate ones are purely practical. The one RefresherTowel mentioned about using local scope over instance scope for non-permanent variables has practical consequences. I have verifiable counter-examples for when people fail to use local variables for temporary values, and conflict at the instance scope was the single reason why their code didn't work.

Yes, the only real logical problem with the code in post #4 is using collision_line() to check for collisions with the enemy (incorrect), instead of with splash blockers (correct). But if the original poster ever starts having scalability problems with instance_find()-based loops (another bad form, this time for performance instead of correctness), he needs to know how to use local scope so that the with block replacing it won't be plastered with other. references nor contain accidental instance variables declared with the wrong owner.
 
Top