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

Instance Nearest in a Direction

C

Chatterb0x

Guest
Hey GM community,
Ever played Kung Fury: Street Rage? My aim is to code a similar enemy targeting mechanic for a mobile app.
The player swipes left or right to attack. If the player swipes right, I want the player object to interact with (kill) nearest enemy instance to player in that direction.

How can this be accomplished?
  • instance_nearest() returns instance closest, not necessarily in the direction chosen.
  • (instance_nearest() && obj_enemy.x > x) has similar flaw. The nearest object may fulfill one condition, but not necessarily both.
  • collision_line() looks promising. According to documentation, it
    Checks whether any instances of a given object collide with a given line, and if there is it returns the id of one of those instances.
    Does that mean it returns the id of the instance nearest? If so, great!
    Or, does it return the ids of all instances on line? That's a little messy.
Please add your 2 cents.
Thanks :)
 

TheouAegis

Member
If a line works, great. Otherwise, here's an alternative:
Code:
var DIR = image_xscale;  //or direction, if you're using 360 degrees
var X = x;
// var Y = y;  //only needed if you used direction
var TARG = noone;
var SELF = id;
var DIST = 1024;    //this is the maximum distance to check
var d=0;
with enemyparent {
    if sign(x-X) == DIR {
        d = distance_to_object(ID);
        if d < DIST {
            DIST = d;
            TARG = id;
        }
    }
}
return TARG;
 
C

Chatterb0x

Guest
If a line works, great. Otherwise, here's an alternative:
Thanks, TheouAegis. I may try this.

I've implemented the GMLscript, however, I appear not to understand instance calling. Here's the code in the player's step event.
Code:
//Interacting with enemy based on direction.
if(instance_exists(obj_enemy)){
//Returns the id of the enemy instance closest to the player object on right side.
var enemy_id = scr_collision_line_first(x,y,room_width,y,obj_enemy,false,true);
if((enemy_id).x > x){
x = (enemy_id).x - 50;
with(enemy_id){
freeze = 1;
}
But swiping towards the enemy instance gives this error.
Code:
FATAL ERROR in
action number 1
of  Step Event1
for object obj_player:

Variable <unknown_object>.x(0, -2147483648) not set before reading it.
 at gml_Object_obj_player_StepBeginEvent_1 (line 65) - if((enemy_id).x > x){
How do I interact with the instance instead of an 'unknown_object'?

Update:
The collision_line_first script always returns -4. I must figure out why. :|

If a line works, great. Otherwise, here's an alternative:
Code:
var DIR = image_xscale;  //or direction, if you're using 360 degrees
var X = x;
// var Y = y;  //only needed if you used direction
var TARG = noone;
var SELF = id;
var DIST = 1024;    //this is the maximum distance to check
var d=0;
with enemyparent {
    if sign(x-X) == DIR {
        d = distance_to_object(ID);
        if d < DIST {
            DIST = d;
            TARG = id;
        }
    }
}
return TARG;
Kindly correct me if I'm wrong, but does this code not account for multiple enemy instances in a direction?
Or, does it execute from nearest through farthest enemy instance, adjusting DIST each time?
 
Last edited by a moderator:

TheouAegis

Member
It literally finds the nearest enemy in the direction you are facing. Minor edits may be needed to work within your code.
 
C

Chatterb0x

Guest
@TheouAegis
The script keeps returning the number three :|
Code:
Unable to find any instance for object index '3' name 'obj_title'
at gml_Object_obj_player_StepBeginEvent_1 (line 61) - if(target.x > x){
Object index 3 must be my title object. It disappears after game starts.
The script has been modified as such.

scr_target
Code:
var dir = image_xscale;
var initial_x = x;
var target = noone;
var self_id = id;
//Maximum distance to check.
var distance = room_width;  
var d=0;
with obj_enemy{
    if sign(x-initial_x) == dir {
        d = distance_to_object(self_id);
        if d < distance {
            distance = d;
            target = id;
        }
    }
}
return target;
 

hippyman

Member
You're using the collision line first script all wrong.


This only checks from the player to the right of the player with the length of the room width.
Sorry I was wrong. With this line you're checking from the player to the end of the room. You need
to make sure that the x2,y2 parameters are absolute values not relative to x1,y1.
Code:
var enemy_id = scr_collision_line_first(x,y,room_width,y,obj_enemy,false,true);

What you want to do is figure out which way you're facing and multiply that by the length you want to check for.
Code:
var lookDir = sign(xspeed) // -1 to 1
var lookLen = 32; //will check 32 pixels away from player
var enemy_id = scr_collision_line_first(x,y,x+lookDir * lookLen,y,obj_enemy,false,true);
You shouldn't need to do anything after that check. It will either be an ID or it will be noone. No unnecessary if statements after that.
 
C

Chatterb0x

Guest
@hippyman
Okay, because here is the modified code in the player object's step event. Direction has already been determined and room width is 3000. The player has swiped right.
Note: Renamed scr_collision_line_first to scr_target and enemy_id to target. It was an easier naming convention. :)

Code:
//*TARGET SYSTEM*
//Interacting with enemy based on direction.
if(instance_exists(obj_enemy)){
var target = scr_target(x,y,3000,y,obj_enemy,false,true);
//If the instance can be targeted.
if(target.targetable = 1){
x = target.x - 50;
with(target){
//Can no longer be targeted by player attacks.
targetable = 0;
freeze = 1;
}
But it returns a CODE ERROR
Code:
Variable <unknown_object>.<unknown variable>(100036, -2147483648) not set before reading it.
 at gml_Object_obj_player_StepBeginEvent_1 (line 64) - if(target.targetable = 1){
Waaah?
 

TheouAegis

Member
That's saying the variable targetable wasn't found in the instance that was found.

And by the looks of it, you're using Studio 2, so there is a possibility of a bug in S2 at play here. They really need to fix that <unknown> error that's plaguing it.

@Chatterb0x what was your Begin Step event code for when you used my script? Is it practically the same as what you have there? (Although there's no reason I can see to have "if target.x > x" anywhere with my code.) I mean, if you were getting targeting issues with my code, just let me know and I can fix that. But my script does not return 3. It cannot return 3 unless there is a bug elsewhere in your code or in S2.

Do me a quick favor: List all of the Scripts in your project in exact order as they appear in the resource tree.
 
C

Chatterb0x

Guest
That's saying the variable targetable wasn't found in the instance that was found.

And by the looks of it, you're using Studio 2, so there is a possibility of a bug in S2 at play here. They really need to fix that <unknown> error that's plaguing it.

@Chatterb0x what was your Begin Step event code for when you used my script? Is it practically the same as what you have there? (Although there's no reason I can see to have "if target.x > x" anywhere with my code.) I mean, if you were getting targeting issues with my code, just let me know and I can fix that. But my script does not return 3. It cannot return 3 unless there is a bug elsewhere in your code or in S2.

Do me a quick favor: List all of the Scripts in your project in exact order as they appear in the resource tree.
There is no reason the variable targetable should not be found. It is assigned a value of 1 in obj_enemy CREATE event.

I am using GMS 1.4.1763 Master Collection.

The code was practically identical. I have since removed "if(target.x>x)", however.

Script resource tree:
  1. scr_sprite_dissolve
  2. scr_particle
  3. scr_target
 

TheouAegis

Member
Did you accidentally put var targetable in the enemy's create event?


I don't get <unknown> errors in my GMS1.4.1763....
 

TheouAegis

Member
var removes targetable from the object once the Create event is over. So there is no variable called targetable in the Step event.

As for what happened with my code (which should be faster and more flexible than collision_line_first) is that when you used my code, you forgot to put the parentheses after the script call, so all you did was set target to the id of the script.

var target = scr_target;

If scr_target is the 4th script in your resource tree (it must have been at one point), then target will be set to 3. Then when you try to use target.x, it treats it as (3).x, which points to obj_title because any value less than 100001 is an object_index.
 
C

Chatterb0x

Guest
var removes targetable from the object once the Create event is over. So there is no variable called targetable in the Step event.

As for what happened with my code (which should be faster and more flexible than collision_line_first) is that when you used my code, you forgot to put the parentheses after the script call, so all you did was set target to the id of the script.

var target = scr_target;

If scr_target is the 4th script in your resource tree (it must have been at one point), then target will be set to 3. Then when you try to use target.x, it treats it as (3).x, which points to obj_title because any value less than 100001 is an object_index.
Just double-checked. I didn't make it a local variable. I'm not sure why I told you I did. :p
I'll retry your script!
 

TheouAegis

Member
If you didn't make it a local variable, I am assuming that obj_enemy is a parent object. Make sure the child object either does not have a create event as well or that if it does it is calling event_inherited().

Also make sure you spelled it the same way.
 
C

Chatterb0x

Guest
If you didn't make it a local variable, I am assuming that obj_enemy is a parent object. Make sure the child object either does not have a create event as well or that if it does it is calling event_inherited().

Also make sure you spelled it the same way.
There is only one enemy object. Inheritance is irrelevant.
targetable is an instance variable.
 
C

Chatterb0x

Guest
@TheouAegis
It works! You are truly a sovereign among mankind.
I'm re-adding the if(target.x > x) qualifier. In the completed game, the enemy object kills the player when too close. As it stands, the enemy instance can run by the player. If the player then slashes in the opposite direction, it cannot find the object/variable.

For students of fine games that want to study my work in the future, here is TheouAegis' code.
Code:
var dir = image_xscale;
var initial_x = x;
var target = noone;
var self_id = id;
//Maximum distance to check.
var distance = room_width;
var d=0;
with obj_enemy{
    if sign(x-initial_x) == dir {
        d = distance_to_object(self_id);
        if d < distance {
            distance = d;
            target = id;
        }
    }
}
return target;
 
C

Chatterb0x

Guest
@TheouAegis
One small quirk. Suppose there are 2 enemies approaching from a direction. The player kills the first instance yet briefly cannot target the second instance. You will recall that the dying instance freezes briefly, collapses, and fades away. Though the player cannot interact with it, your script targets instance uno until it's destroyed. Any suggestions?
 

TheouAegis

Member
Remember how I said my script was more flexible? All you need to do is add another condition. Make sure the enemy can also be wounded. so I guess, make sure he is not frozen.

with enemies {
d = distance
if d < max_dist
if d > min_dist
if condition 1
if condition 2
if condition 3
...
if condition n {
target = id
max_dist = d
} }
 
C

Chatterb0x

Guest
@TheouAegis
Solved that, now a similar problem.

The player can immediately kill multiple enemy instances in a direction. But suppose an instance spawns in a direction, is killed, and an instance spawns opposite. The player cannot target the second instance until the first has faded away. Any idea why? Here is the script and code.

scr_target()
Code:
var dir = image_xscale;
var initial_x = x;
var target = noone;
var self_id = id;
//Maximum distance to check.
var distance = room_width; 
var d=0;
with obj_enemy{
    if((sign(x-initial_x) == dir) && (targetable)) {
        d = distance_to_object(self_id);
        if (d < distance){
            distance = d;
            target = id;
        }
    }
    else{
    target = noone;
    }
}

return target;
Begin Step, obj_player
note: abridged for this post

Code:
//*TARGET SYSTEM*
//Interacting with enemy based on direction.
if(instance_exists(obj_enemy) && (obj_enemy.x > x)){
var target = scr_target();
if((target != noone) && (target.targetable = 1)){
x = target.x - 50;
with(target){
targetable = 0;
freeze = 1;
}} else{
break;}
}
I can make a gif to illustrate.
 
C

Chatterb0x

Guest
@TheouAegis
I've gotten rid of
Code:
else{
target = noone;
}
and

Code:
else{
break;
}
from the obj_player Begin Step.
While redundant, removing them has not fixed the problem.
 

TheouAegis

Member
But suppose an instance spawns in a direction, is killed, and an instance spawns opposite.
Code:
if(instance_exists(obj_enemy) && (obj_enemy.x > x))
Two reasons:
1) I use with, which renders the first check redundant.
2) The second check is actually breaking the code because it's an unrealistic condition.

If you're worried about enemies being too close to the player, as I said, check if d is greater than a minimum distance, like 8 or something.
 
C

Chatterb0x

Guest
Works like a charm. Sometimes a coder can't see the forest for the trees. Much appreciated.
 
Top