SOLVED Make projectile unable to hit the instance that spawned it

Hey guys,

I just started with GameMaker and encountered an issue I don't know how to solve.
I hope I worded my question well. I have multiple tanks, which consist of multiple objects (turret + hull) and share their ammo between tanks. Currently the tanks are able to hit themselves when the shell spawns in the turret and hits it's own hull.
tank.PNG
The tank.


tank2.PNG
The turret setting it's position to it's hull. I'll have to solve this at some point with Instance ID's, since multiple copies of multiple tanks can be on the battlefield, but not that important for now.



tank3.PNG
Turret rotation and spawning the shell.



tank4.PNG
Shell checking for collision with a unit and assigning damage.

What would be a good way to solve this?
 

Nidoking

Member
First, code should be in CODE blocks so it's readable and copyable.

Second, if you already have a concept of instance IDs, you're most of the way there. When you check collision, also check that it's not the ID of the instance you don't want to collide with. Store that ID when you spawn the bullet, along with the other variables you're initializing. It might be a good idea to use instance_place_list rather than instance_place, in case multiple obj_unit are close enough that it collides with two of them.
 
Thanks for the quick answer. Does the use of an array instead of a variable change anything significant for me now in the case of instance_place_list? Never used them.
 
Alright, here's my try for a solution, but I only get bugs no matter what I try. I am passing the ID through, but it isn't able to find a armor_type, probably because it is checking the turret which it shouldn't.
I confused arrays with lists, my bad!

The hull spawning the turret and defining creator as ID:

GML:
var turret = instance_create_layer(x,y,"layer_player",obj_tank_tiger_turret);

turret.creator = self.id;

The turret spawning the shell and carrying forth the ID as creator variable.
GML:
if (mouse_check_button(mb_left)) && (current_cooldown <= 0)
{
    var bullet
    bullet = instance_create_layer (x,y,"layer_shell",ammo);
    bullet.direction = self.image_angle;
    bullet.image_angle = bullet.direction
    bullet.creator = self.creator
    current_cooldown = reload_cooldown;
}
The bullet checking wether collision is with a unit and wether it's the creator.

GML:
var col = instance_place(x,y,obj_unit)
if col.id != creator
    {
    if col.armor_type = 1
        {
            col.hp = col.hp - self.damage_hard;
        }
    else
        {
            col.hp = col.hp     - self.damage_soft;
        }
    instance_destroy()   
    }
 
If it can't find armor_type that means it's not been set so the variable hasn't been initialized. Make sure the hull object is actually setting it's armor type in the creation event because GML has no failsafe to make variables that haven't been created automatically turn back false such as you might see in some languages.

So it will just throw up an error that it tried to read an unknown variable and crash
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Moderator
A simple solution to the issue in the original post is to use an alarm... Based on your initial code, add an alarm to the shell object and simply add a comment to it (no code required). Then in the create event set the alarm to something like 3 or 4 or something low. Finally in the collision event, wrap everything in a check:
GML:
if alarm[0] < 0
{
// collision code here
}
This way the alarm prevents any collisions being detected until a few steps have passed, at which point the shell will no longer be in contact with the object that spawned it. :)

Alternatively, give the shell a "parent" variable that holds the spawner id, then check THAT in the collision event, eg (pseudo code!):
GML:
// CODE TO SPAWN SHELL
with (instance_create_layer(yadda)) // create the shell
{
parent = other.id;
}

// COLLISION CODE
if other.id != parent
{
//collision!
}
 
Did you remember to check that the result of instance_place is not noone?
Tried that just now, didn't change the bug.
GML:
var col = instance_place(x,y,obj_unit)
if col.id != creator && col != noone
If it can't find armor_type that means it's not been set so the variable hasn't been initialized. Make sure the hull object is actually setting it's armor type in the creation event because GML has no failsafe to make variables that haven't been created automatically turn back false such as you might see in some languages.

So it will just throw up an error that it tried to read an unknown variable and crash
It does, in the Create event of the hull. I just copied the variables that create the bug when changed.

GML:
friction = .2;
max_speed = 1.5;
armor_type = 1;
hp = 500;
var turret = instance_create_layer(x,y,"layer_player",obj_tank_tiger_turret);
turret.creator = self.id;
A simple solution to the issue in the original post is to use an alarm... Based on your initial code, add an alarm to the shell object and simply add a comment to it (no code required). Then in the create event set the alarm to something like 3 or 4 or something low. Finally in the collision event, wrap everything in a check:
GML:
if alarm[0] < 0
{
// collision code here
}
This way the alarm prevents any collisions being detected until a few steps have passed, at which point the shell will no longer be in contact with the object that spawned it. :)

Alternatively, give the shell a "parent" variable that holds the spawner id, then check THAT in the collision event, eg (pseudo code!):
GML:
// CODE TO SPAWN SHELL
with (instance_create_layer(yadda)) // create the shell
{
parent = other.id;
}

// COLLISION CODE
if other.id != parent
{
//collision!
}
Isn't that what I am already doing?
In my case, the variable for parent is just called creator, and is passed from hull to turret and from turret to the shell?
Or is it different because it's a when action?

But I might just go the alarm route.
 
Found the bug!


GML:
var col = instance_place(x,y,obj_unit)
if col.id != creator && col != noone
I need to check in two steps, since the it tries to check for both at the same time, and it obviously doesn't have a col.id to find!

It works if I do it this way:
GML:
var col = instance_place(x,y,obj_unit)
if col != noone
    if col.id != creator
        {
        if col.armor_type = 1
            {
            col.hp = col.hp - self.damage_hard;
            }
        else
            {
            col.hp = col.hp - self.damage_soft;
            }
        instance_destroy()

    }
Thanks guys!
 

Nidoking

Member
I believe GML shortcuts boolean operations, so if col != noone && col.id != creator should work. You need to make sure the widest check is performed first. Nesting them as you did also does that explicitly.
 
Top