GML How to make a piercing bullet collide only once per enemy?

G

Garriott

Guest
I am currently programming a run n' gun sidescroller that has single hit and piercing multihit projectiles. For single projectiles, they get destroyed on contact, and multihit ones have a timer on each enemy that resets before being able to be hit by multihit projectiles again, which also doesn't really work completely because there will be two players firing these projectiles on screen and only one can damage at a time doing it the way I am doing it, but it works for the time being.

However, for certain piercing projectiles, I want them to only be able to collide with each enemy only once and then never hit them again despite coming into contact with them again. How would I go about programming something like that?

In addition, I have an arm object set up that snap rotates instantly to where the player is currently aiming when they use the dpad. In this object, I have code for a set point on the object where the bullets come from, and it looks like this.

xx = x + lengthdir_x(20, image_angle );
yy = y + lengthdir_y(20, image_angle );

However, if I use xx and yy as the object spawn point for bullets, they come out of where they're supposed to, but it seems the additional calculation that the game has to do compared to just using plain old x or y makes the bullets come out at the wrong angle when the player rapidly aims up and horizontal.

For example, if the player rapidly aims up and horizontal, sometimes, the bullets will come out of the arm tip while it is aimed upward and they will go horizontally as if the aim was horizontal, but it hasn't gotten there yet, and the same thing happens with aiming upward where the bullets will come out of the tip of the arm while it is horizontal, but they will shoot vertically instead.

Without the xx and yy and just using x and y, the bullets come out properly for each angle, but then they come out of the center of the player rather than the tip of the gun, and that isn't desirable. For some of the bullets I can set the origin point of the sprite to be further away so it looks like they shoot out of the tip of the gun, but that creates problems for some of them rotating because the axis won't be centered.

How can I have the xx and yy code function without the extra delay it seems to add to shooting the bullets?
 

Kyrieru

Member
Personally I make the object create a list.
When the projectile hits, it's ID added to the list.
Then when checking for a collision, it checks if the ID is in the list already.
 
S

spoonsinbunnies

Guest
your problem of the bullet spawining in the right spot but going the wrong direction sounds like the bullet is spawned before changing image angle maybe?
 
G

Garriott

Guest
Personally I make the object create a list.
When the projectile hits, it's ID added to the list.
Then when checking for a collision, it checks if the ID is in the list already.
Ah, I get the rough idea of what I am supposed to do, but how would I set the code up for that? Have never worked with lists before.

I have the list being created as a variable in the projectile's create event, I have the list being destroyed when the object is destroyed, but how do I set it up to add instances to the list on collision and damage them the first time they collide and filter them out so they don't collide again?
 

Alexx

Member
Just loop through the list of ids. If it is on the list don't do anything.
Otherwise add to list and perform damage (reduce hp, create effect, etc.)
 

Kyrieru

Member
Code:
//defender creates a ds_list in create event.
hit_list = ds_list_create()


///in step of ATTACKER/bullet

with defender_ob      ////run code from defender so we have access to attacker's variables
{
if ds_list_find_index(hit_list,other.id) =-1    ////if defender has never been hit by attacker
{
//defender gets hit. Add the attacker to the list of things that have hit this object
ds_list_add(hit_list,other.id) ////add
}

}

You can do the same concept in reverse if you want the bullet to contain the list. This can be useful if you want something like a boomerang to reset it's hit_list once it reverses direction.
Make sure to destroy the list when the object is destroyed or the room ends.

to reverse it, just make it
other.hit_list,id
 
Last edited:

Kyrieru

Member
Why do people use a list for this instead of a map?
Because It's just a list of IDs, which are just numbers.

You could use pretty much anything if it's faster, but at a certain scale it's just easier for people to use something that seems logical and easier to learn.
 

Simon Gust

Member
Because It's just a list of IDs, which are just numbers.

You could use pretty much anything if it's faster, but at a certain scale it's just easier for people to use something that seems logical and is therefore easier to learn.
Not gonna say anything against that.
 

Kyrieru

Member
How would you use a map here? Wouldn't handling the keys for unknown amount of entries be hard and inefficient?
I seem to recall that maps are faster. So it might be more efficient in some situations.
I guess you would add the id as the keys and then the value would be 0? Dunno...

edit: Nvm I guess lists are faster....I dunno why you'd use em...beyond the scope of my use cases. Maybe it takes less memory to have a global hit_map rather than a list per object. I suppose you could also have a map that contains arrays that change over time.
 
Last edited:

Simon Gust

Member
Yeah the values dont hold anything worth putting. You just check for entry existence. The id of the instance being the key of the entry.
 
Well, I'm not sure how simon is talking about setting it up, but you could always string the instance ID for the key and then simply use ds_map_exists to see if the instance ID has been added or not. I mean, this'll have some overhead because of the stringing operation (you shouldn't really use raw integers as a key for a map, as this can lead to some funny behaviour in certain circumstances) but for stuff like bullets, this sort of operation is small peanuts, as long as you're not stringing 1000 instances per step there'll be no problem with it.
 
I seem to recall that maps are faster. So it might be more efficient in some situations.
Maps are not inherently faster. It all depends on the length of the list. A short list will be faster than an equally sized map, whereas a long list will be slower than an equally sized map (for varying definitions of large).
 

FrostyCat

Redemption Seeker
How would you use a map here? Wouldn't handling the keys for unknown amount of entries be hard and inefficient?
I seem to recall that maps are faster. So it might be more efficient in some situations.
You two need to read up on what lies underneath maps. Contrary to common rookie belief, maps are not implemented as naive entry-by-entry lookup tables.

When the number of entries grows such that the initial hash's computational costs become insignificant, searching, checking, inserting and deleting arbitrary single entries is faster on maps, but looping through all entries is faster on arrays and lists.

As for how it's done using a map:
Insert elements into the set using set[? thing] = true;. Then ds_map_exists(set, thing) would give you whether something exists in the set, and ds_map_delete(set, thing) would allow you to remove from the set.
 
Top