collision_line only trigger once

S

ssssss

Guest
Hello everyone,
I'm trying to use collision_line to detect a collison

current_target = collision_line(x-attack_area_x, y, x+attack_area_x, y, obj_enemy, false, true);

But it seems that if an instance is detected to be collided in one step,
the collision_line function can not detect collison with it any more.

I guess one instance can ony be collided once, what can we do to fix it?
 

Simon Gust

Member
Hi
Your guess is correct, collision_line can only detect 1 instance at a time.
To fix this you would have to check in every instance of obj_enemy to be colliding with the one that originally called on collision_line.
http://www.gmlscripts.com/script/collision_line_list
On gmlscripts there are many of these examples.

In this one, the script returns a list with all IDs of obj_enemy.
So you could do this:
Code:
var current_target_list =collision_line_list(x-attack_area_x, y, x+attack_area_x, y,obj_enemy,false,true);
if (ds_exists(current_target_list,ds_type_list))
{ 
   //you probably want to iterate through all the list entries
   var size = ds_list_size(current_target_list);
   for (var i = 0; i < size; i++)
   {
     //INSERT COLLISION CODE HERE
   }  

   //DELETE LIST
   ds_list_destroy(current_target);
}
 
S

ssssss

Guest
Hi
Your guess is correct, collision_line can only detect 1 instance at a time.
To fix this you would have to check in every instance of obj_enemy to be colliding with the one that originally called on collision_line.
http://www.gmlscripts.com/script/collision_line_list
On gmlscripts there are many of these examples.

In this one, the script returns a list with all IDs of obj_enemy.
So you could do this:
Code:
var current_target_list =collision_line_list(x-attack_area_x, y, x+attack_area_x, y,obj_enemy,false,true);
if (ds_exists(current_target_list,ds_type_list))
{
   //you probably want to iterate through all the list entries
   var size = ds_list_size(current_target_list);
   for (var i = 0; i < size; i++)
   {
     //INSERT COLLISION CODE HERE
   } 

   //DELETE LIST
   ds_list_destroy(current_target);
}
Thanks for ur reply !
Is there any way to detect 1 instace for multi times?
In my case, an enemy walk into the hero's attack area, and it's detected by the collision method,
But if it walk outside the attack area, and walk in again, the enemy can't be detect because the collison can only trigger once.
 

Simon Gust

Member
You could have this code in an alarm event and refresh it like every 12 frames or so. But I can't really help you further without seeing your code.
Maybe you could post it here if it is more complicated than I think it is.
 

sp202

Member
Run the collision_line from within the enemy.
Code:
//CREATE EVENT
targets=ds_list_create()

//STEP EVENT
ds_list_clear(targets)
var list=targets
with objEnemy()
{
inst=collision_line //whatever the coordinates are, replace objEnemy with self
if inst!=noone
{
ds_list_add(list,inst)
}
}
 
S

ssssss

Guest
You could have this code in an alarm event and refresh it like every 12 frames or so. But I can't really help you further without seeing your code.
Maybe you could post it here if it is more complicated than I think it is.
Code:
if (instance_exists(current_target)) {
    //show_debug_message("find enemy!");
    if (can_attack) {
        alarm[0] = room_speed * attack_speed;   //alarm to set can_attack flag
        speed = 0;
        hurt = true;
        image_speed = 0.4;
        can_attack = false;
    }
    if (hurt && image_index > 3) {
        damage_target(current_target, attack);  //hurt target here
        hurt = false;
    }
} else {
    current_target = collision_line(x-attack_area_x, y, x+attack_area_x, y, obj_enemy, false, true);
    if (current_target == noone) {
        x += move_speed;
    } else {
    }      
}
This is the code in step event of the obj_hero;
If two enemy are collided by collision_line in one time, the current_target will be one of them;
After this target is destroyed, the hero will not attack the rest one because collision_line will always return noone;

So I want to find out how to collide with one instance multi times.
 

Simon Gust

Member
So you say you have two (or more) enemies right next to each other and when both are defeated only then you can move on.
In that case, the collision_list_list is still useful.
Code:
if (current_target_list != noone) {    
    if (hurt && image_index > 3) { //I rearanged this so that is more of a switch                
        if (targets_defeated >= ds_list_size(current_target_list)) {
            ds_list_destroy(current_target_list); //we have defeated all enemies. To avoid a memory leak, we destroy this list
            current_target_list = noone; //And set it to noone (-4) again so that our first if statements fails and we can check again for enemies
        } else {
            var current_target = ds_list_find_value(current_target_list,targets_defeated); //we get the current entry of the ds_list 
            damage_target(current_target, attack);  //hurt target here
            hurt = false;
            /*
            I Don't know how damage_target(); looks like but you have to increase the variable
            "targets_defeated" by 1 if you defeat one of the enemies!
            targets_defeated += 1;
            */
        }
    } else {
        if (can_attack) {          
            alarm[0] = room_speed * attack_speed;   //alarm to set can_attack flag
            speed = 0;
            hurt = true;
            image_speed = 0.4;
            can_attack = false;
        }
    }
} else {
    //GET SOME ENEMY IDS IN THE LIST
    current_target_list = collision_line_list(x-attack_area_x, y, x+attack_area_x, y, obj_enemy, false, true);
    if (current_target_list != noone) {
        list_size = ds_list_size(current_target_list); //this is our list full of enemy IDs
        targets_defeated = 0; //this will keep track of how many enemies you have slain
    } else { //the list returns noone if no enemy is found
        x += move_speed;
    }
}
The code is commented so you understand it better.
I barely understand this myself and haven't tested it. Which means you have to give me a notice if there any problems.
 

sp202

Member
Is the issue that collision_line is only able to return the id of one of the objects being collided with? Or is there more to it?
 

Simon Gust

Member
Is the issue that collision_line is only able to return the id of one of the objects being collided with? Or is there more to it?
I think at this point we're talking more about collision_line_list. But you're right about the collision_line function

Honestly speaking collision line is one of the slower functions and if your bullets or melee attacks go straight in front of you, I suggest using collision_rectangle.
 
S

ssssss

Guest
So you say you have two (or more) enemies right next to each other and when both are defeated only then you can move on.
In that case, the collision_list_list is still useful.
Code:
if (current_target_list != noone) {  
    if (hurt && image_index > 3) { //I rearanged this so that is more of a switch              
        if (targets_defeated >= ds_list_size(current_target_list)) {
            ds_list_destroy(current_target_list); //we have defeated all enemies. To avoid a memory leak, we destroy this list
            current_target_list = noone; //And set it to noone (-4) again so that our first if statements fails and we can check again for enemies
        } else {
            var current_target = ds_list_find_value(current_target_list,targets_defeated); //we get the current entry of the ds_list
            damage_target(current_target, attack);  //hurt target here
            hurt = false;
            /*
            I Don't know how damage_target(); looks like but you have to increase the variable
            "targets_defeated" by 1 if you defeat one of the enemies!
            targets_defeated += 1;
            */
        }
    } else {
        if (can_attack) {        
            alarm[0] = room_speed * attack_speed;   //alarm to set can_attack flag
            speed = 0;
            hurt = true;
            image_speed = 0.4;
            can_attack = false;
        }
    }
} else {
    //GET SOME ENEMY IDS IN THE LIST
    current_target_list = collision_line_list(x-attack_area_x, y, x+attack_area_x, y, obj_enemy, false, true);
    if (current_target_list != noone) {
        list_size = ds_list_size(current_target_list); //this is our list full of enemy IDs
        targets_defeated = 0; //this will keep track of how many enemies you have slain
    } else { //the list returns noone if no enemy is found
        x += move_speed;
    }
}
The code is commented so you understand it better.
I barely understand this myself and haven't tested it. Which means you have to give me a notice if there any problems.
I think I've understand it and It's really a good solution.
But in some case it not works because the enemy is not always inside the attack area, so when it run outside the attack area, I need to remove it from the current_target_list.
And next time I can not add it into the list again because the collision_line_list will not find it.

So I think the best solution is to find a way to detect collison multi times. If I can't do that, I could only try to avoid such cases...

Thanks for your help again.
 

Simon Gust

Member
I think I've understand it and It's really a good solution.
But in some case it not works because the enemy is not always inside the attack area, so when it run outside the attack area, I need to remove it from the current_target_list.
And next time I can not add it into the list again because the collision_line_list will not find it.

So I think the best solution is to find a way to detect collison multi times. If I can't do that, I could only try to avoid such cases...

Thanks for your help again.
What if when an enemy runs away you reset your list so that the first if-statement will fail again:
Code:
if (current_target_list != noone) {   
   if (hurt && image_index > 3) { //I rearanged this so that is more of a switch               
       if (targets_defeated >= ds_list_size(current_target_list)) {
           ds_list_destroy(current_target_list); //we have defeated all enemies. To avoid a memory leak, we destroy this list
           current_target_list = noone; //And set it to noone (-4) again so that our first if statements fails and we can check again for enemies
       } else {
           var current_target = ds_list_find_value(current_target_list,targets_defeated); //we get the current entry of the ds_list
           damage_target(current_target, attack);  //hurt target here
           hurt = false;
           /*
           I Don't know how damage_target(); looks like but you have to increase the variable
           "targets_defeated" by 1 if you defeat one of the enemies!
           targets_defeated += 1;

           You can check collision again in your damage_target() script but this time collision_line() should suffice.
           If it isn't there delete the list so it's reset and we don't have to deal with adding IDs to the list.
           */
           var make_sure = collision_line(x-attack_area_x, y, x+attack_area_x, y, obj_enemy, false, true);
           if (make_sure == noone) {
               ds_list_destroy(current_target_list);
               current_target_list = noone;
           }
       }
   } else {
       if (can_attack) {         
           alarm[0] = room_speed * attack_speed;   //alarm to set can_attack flag
           speed = 0;
           hurt = true;
           image_speed = 0.4;
           can_attack = false;
       }
   }
} else {
   //GET SOME ENEMY IDS IN THE LIST
   current_target_list = collision_line_list(x-attack_area_x, y, x+attack_area_x, y, obj_enemy, false, true);
   if (current_target_list != noone) {
       list_size = ds_list_size(current_target_list); //this is our list full of enemy IDs
       targets_defeated = 0; //this will keep track of how many enemies you have slain
   } else { //the list returns noone if no enemy is found
       x += move_speed;
   }
}
Maybe this will work.
 

sp202

Member
Are you only dealing damage each step that the enemy is within the attack area? If so, you should be clearing the list each step. I'm not sure what you mean by collision_line_list won't find it, I don't see why it wouldn't because as far as I can tell, each time it's called it rebuilds its list.
 

Simon Gust

Member
Are you only dealing damage each step that the enemy is within the attack area? If so, you should be clearing the list each step. I'm not sure what you mean by collision_line_list won't find it, I don't see why it wouldn't because as far as I can tell, each time it's called it rebuilds its list.
We have a cooldown via attack speed.
Also collision_line_list is a custom script found on http://www.gmlscripts.com/script/collision_line_list

We keep track of how many enemies have been defeated and compare it to how many enemies there were to begin with.
If the variable enemies_defeated exceeds the ds_list_size(); we know that we defeated all enemies and also those entries would give us instances that don't exist.
So we delete the list and set it back to noone so that the first if-statement will fail.
 
S

ssssss

Guest
"collision_line_list won't find it" because an instance can only be collided once.
If I clear my list each step, the collision_line_list will return noone even if the enemy walk into the attack area again.
 

Simon Gust

Member
"collision_line_list won't find it" because an instance can only be collided once.
If I clear my list each step, the collision_line_list will return noone even if the enemy walk into the attack area again.
I'm not sure but I think what you mean is that when you have 2 enemies at the same time and one leaves, the list is reset and only 1 enemy ID will be renewed in that list. So if the other enemy that left comes back
Before the enemy that's still there dies, then it won't make a new list. Until that said enemy dies.
Is that the case?
 
S

ssssss

Guest
I'm not sure but I think what you mean is that when you have 2 enemies at the same time and one leaves, the list is reset and only 1 enemy ID will be renewed in that list. So if the other enemy that left comes back
Before the enemy that's still there dies, then it won't make a new list. Until that said enemy dies.
Is that the case?
No, maybe is just a silly question...
But how can we renew the current_target_list?
As we know the collision_line can only trigger once to an instance(enemy), and the enemies inside the first list cann't be collided any more.
So if I use collision_line_list it will return noone when I want to rebuild it.
 
Top