GML Follower needs to switch layers/depth when moving up so they aren't being walked on.

In my game, I have npc followers similar to Earthbound. They move great, face the right way, and even stop where they are supposed to. The only problem is that since they are on separate layers if the player walks up, it looks like he is walking on the one behind him (I'll attach an image to show). I need the follower to be "below" the player when he moves down, left, or right, but "above" the player when he moves up, yet I have no idea how to do this. Here is what I have so far. (obj_kris_dw_ow is the player and obj_susie_dw_ow is the follower) Here is my code:

obj_kris_dw_ow create:
GML:
//follower pos record
array_size = 69; //the amount of positions to record
for(var i = array_size-1; i >= 0; i --){
    
follow_pos_x[i] = x;
follow_pos_y[i] = y;
    
toRecordSprite[i] = spr_kris_dw_down_move;   
}

followerExist = false;
obj_kris_dw_ow step:
GML:
//movement 💩💩💩💩
hInput = keyboard_check(vk_right) - keyboard_check(vk_left);
vInput = keyboard_check(vk_down) - keyboard_check(vk_up);

if(hInput != 0 or vInput != 0){
dir = point_direction(0,0,hInput,vInput);
moveX = lengthdir_x(mvspd, dir);
moveY = lengthdir_y(mvspd, dir);


x += moveX;
y += moveY;

switch(dir){
    case 0: sprite_index = spr_kris_dw_right_move; break;
    case 90: sprite_index = spr_kris_dw_up_move; break;
    case 180: sprite_index = spr_kris_dw_left_move; break;
    case 270: sprite_index = spr_kris_dw_down_move; break;
    }
} else {
    image_index = 0
}

//follower following position and which way to turn
if (x != xprevious or y != yprevious){
    
    for(var i = array_size-1; i > 0; i --){
    
    follow_pos_x[i] = follow_pos_x[i-1];
    follow_pos_y[i] = follow_pos_y[i-1];
    
    toRecordSprite[i] = toRecordSprite[i-1];   
    
    }
    
    follow_pos_x[0] = x;
    follow_pos_y[0] = y;
    toRecordSprite[0] = sprite_index;   
}
obj_kris_dw_ow key press S:
GML:
followerExist = !followerExist//changes it from true to false and vice versa

if(followerExist){
    var follower_1 = instance_create_layer(x-20,y-20, "Follower_Below", obj_susie_dw_ow);
follower_1.record = 10;
} else {
    layer_destroy_instances("Follower_Below");
}
obj_susie_dw_ow create:
GML:
lastposrec = 10

s_walk_Sprite_Down = spr_susie_dw_down_move;
s_walk_Sprite_Left = spr_susie_dw_left_move;
s_walk_Sprite_Right = spr_susie_dw_right_move;
s_walk_Sprite_Up = spr_susie_dw_up_move;
obj_susie_dw_ow step:
GML:
switch(obj_kris_dw_ow.toRecordSprite[record]){

    case spr_kris_dw_down_move: sprite_index = s_walk_Sprite_Down; break;
    case spr_kris_dw_up_move: sprite_index = s_walk_Sprite_Up; break;
    case spr_kris_dw_left_move: sprite_index = s_walk_Sprite_Left; break;
    case spr_kris_dw_right_move: sprite_index = s_walk_Sprite_Right; break;
}

x = obj_kris_dw_ow.follow_pos_x[lastposrec];
y = obj_kris_dw_ow.follow_pos_y[lastposrec];

image_index = obj_kris_dw_ow.image_index;
 

Attachments

CMAllen

Member
This is a draw-order issue. Now, as for how to handle this, there's an easy solution that's not at all robust, a semi-easy solution that can handle multiple companions but nothing else, and a less easy solution that's very robust. The easiest solution is to have two different layers for your companion based on the companion's y position relative to the player, moving between the layer above or below the player accordingly. The semi-easy solution is to disable the draw event for your companion objects and have the *player* object call their draw events based on whatever order you want (once again, based on your example image, it would be the player's and companion's y positions). The less-easy solution, which is an expansion on the above concept, is to disable the draw events for *ALL* draw-order-sorted objects, and to create a 'draw-order' sorting object that maintains and sorts a list of all the 'sorted objects' which it iterates through to draw them in the corrected order.

Yes, ideally, you would think that GMS would have some basic depth sorting method like this built right into it for things being draw to the same layer, but it does not.
 
This is a draw-order issue. Now, as for how to handle this, there's an easy solution that's not at all robust, a semi-easy solution that can handle multiple companions but nothing else, and a less easy solution that's very robust. The easiest solution is to have two different layers for your companion based on the companion's y position relative to the player, moving between the layer above or below the player accordingly. The semi-easy solution is to disable the draw event for your companion objects and have the *player* object call their draw events based on whatever order you want (once again, based on your example image, it would be the player's and companion's y positions). The less-easy solution, which is an expansion on the above concept, is to disable the draw events for *ALL* draw-order-sorted objects, and to create a 'draw-order' sorting object that maintains and sorts a list of all the 'sorted objects' which it iterates through to draw them in the corrected order.

Yes, ideally, you would think that GMS would have some basic depth sorting method like this built right into it for things being draw to the same layer, but it does not.
I actually don't have any draw events on the follower. Also, how would I write either the medium or hard solution, since I plan to have multiple followers? I'm pretty new to gamemaker so I have no idea.
 

TheouAegis

Member
You typically don't have Draw events and that's fine. The fourth unmentionable method is to set the player's depth to the negation of his y. E.g., depth=-y. Things get messy for GameMaker when you do this, though.

To disable the Draw event, you either clear the visible flag in the object, or you just put a comment in the Draw event - as long as anything saveable is in the Draw event, it won't run - or you could not give any of the NPCs sprites & save the desired sprite_index value inside a variable/array for each NPC. Then have your control object (e.g., the lead PC, or a persistent control object) draw each of the NPCs following the PC.
 

CMAllen

Member
You typically don't have Draw events and that's fine. The fourth unmentionable method is to set the player's depth to the negation of his y. E.g., depth=-y. Things get messy for GameMaker when you do this, though.

To disable the Draw event, you either clear the visible flag in the object, or you just put a comment in the Draw event - as long as anything saveable is in the Draw event, it won't run - or you could not give any of the NPCs sprites & save the desired sprite_index value inside a variable/array for each NPC. Then have your control object (e.g., the lead PC, or a persistent control object) draw each of the NPCs following the PC.
Yeah, I specifically didn't mention that method because it's currently deprecated, and you shouldn't use deprecated features or functions. They could be removed with any update. And, as you say, it makes a mess of the layer system.

I personally prefer to toggle off visibility for organizational reasons -- it keeps all the drawing code contained to the relevant object, so all that has to happen is telling that object when it should draw itself. Whereas if you comment out an object's draw event, all that code has to be handled by some other object, and that can get messy. You also don't have to deal with the limitations on the no-sprite approach (image_index and image_speed are based on an object's assigned sprite, for example).

I actually don't have any draw events on the follower. Also, how would I write either the medium or hard solution, since I plan to have multiple followers? I'm pretty new to gamemaker so I have no idea.
You haven't *customized* the object's draw event. When you 'add' an event to an object, you're telling GMS that you want to customize that part of the object's behavior. For most events, the default behavior is nothing at all. Every object with a sprite has an automatic, default draw event that consists of draw_self() and nothing else. If you add the draw event to an object and don't include any of the draw_ type functions, the object will never draw anything.
 
Last edited:

rIKmAN

Member
Have I missed something? For the purposes of this thread and desired functionality of the game, what is deprecated, and if you remember, where did you see it?
I was going to ask the same thing, as while it might not be the cleanest or most performant way to do it when there are a lot things that need to be sorted, using depth = -y works fine in many cases and letting GM handle the managed layers isn't an issue at all with regards to performance in those situations.

The manual says you can still set depth manually if required and the code example given at the bottom of the page actually shows setting it to -y.

While the manual isn't always 100% correct, that page has been updated recently with some info regarding Filters & Effects, so it's not that out of date and this is the first time I've heard anything about manually setting depth being deprecated so I'd be interested in a source too if one is available.
 
I believe early on in the GMS2 lifecycle depth = -y was pretty slow because layers weren't managed as well as they are now, so a lot of people were recommending against using it for a period of time. That's the only thing I can think of that might mean "deprecated".
 

CMAllen

Member
I believe early on in the GMS2 lifecycle depth = -y was pretty slow because layers weren't managed as well as they are now, so a lot of people were recommending against using it for a period of time. That's the only thing I can think of that might mean "deprecated".
In a patch note *MANY* months ago (at least a year, probably more), it was specifically mentioned that the 'depth' function was being deprecated, meaning that while it still currently works, its continued functionality and support is not something a developer should rely on going forward, because it may change functionality in the future or be removed entirely. Use managed layers or some other method to sort objects. And honestly, there are plenty of options to use as a replacement for depth.

-edit-
On the other hand, maybe this never went through and they've since backtracked on that decision? Honestly, I'm confused now.
 
Last edited:

chamaeleon

Member
In a patch note *MANY* months ago (at least a year, probably more), it was specifically mentioned that the 'depth' function was being deprecated, meaning that while it still currently works, its continued functionality and support is not something a developer should rely on going forward, because it may change functionality in the future or be removed entirely. Use managed layers or some other method to sort objects. And honestly, there are plenty of options to use as a replacement for depth.
There are not many mentions of depth in the release notes (one in IDE release notes, and 8 for runtime) going back to 2018, none of which indicates deprecation. To me, it seems depth is too useful to solve sorting in an easy manner instead of using many layers to achieve the same thing (or implement a manual/custom system), for it to be deprecated anytime soon, not to mention how much existing code must exist making use of it.
 

CMAllen

Member
There are not many mentions of depth in the release notes (one in IDE release notes, and 8 for runtime) going back to 2018, none of which indicates deprecation. To me, it seems depth is too useful to solve sorting in an easy manner instead of using many layers to achieve the same thing (or implement a manual/custom system), for it to be deprecated anytime soon, not to mention how much existing code must exist making use of it.
Depth *IS* layers -- unmanaged layers (just in fewer manually called functions). That's how it worked after the move from GMS1.4 to GMS2.
 

chamaeleon

Member
Depth *IS* layers -- unmanaged layers (just in fewer manually called functions). That's how it worked after the move from GMS1.4 to GMS2.
Well, yes, they are. Put an instance at a depth and a layer for that depth is created if there is no layer already, remove all instances from a depth and the layer goes away etc. But that does not mean depth as an instance variable is deprecated (that it now maps to layers under the hood does not change that one can use it instead of layer functions). Just for the record, I think I have a pretty good grasp on the technical details of depth and layers. I'm just interested in any specific mention of deprecation.
 

CMAllen

Member
Well, yes, they are. Put an instance at a depth and a layer for that depth is created if there is no layer already, remove all instances from a depth and the layer goes away etc. But that does not mean depth as an instance variable is deprecated (that it now maps to layers under the hood does not change that one can use it instead of layer functions). Just for the record, I think I have a pretty good grasp on the technical details of depth and layers. I'm just interested in any specific mention of deprecation.
Well, if you've gone back as far as 2018 and can't find any mention of it, either I misunderstood, or they decided against it.
 

rIKmAN

Member
Well, if you've gone back as far as 2018 and can't find any mention of it, either I misunderstood, or they decided against it.
I think you've likely misunderstood or confused something along the way, as I also noticed you said "depth function" when we're talking about assigning a value to a built-in instance variable.

There are some old depth based functions that were deprecated, mainly for dealing with the old pre GMS2 "tiles" but also object_get_depth() and object_set_depth() - is it possible you got mixed up regarding these old deprecated functions and thought everything regarding depth other than layers was being deprecated?

As for @CheesusSliced problem, I'd say it's fine to use depth = -y to solve the sorting issue given the simple nature of the problem/project. You could only update it when the character has moved on the y-axis, but even sticking it in a Step Event will show no noticeable performance hit unless the amount of things being sorted this way every frame grows significantly - at which point if profiling showed it to be an issue you'd look into custom sorting. No need to pre-emptively optimise though.

Use the same code to set the depth of static items in their Create Event and make sure your sprite origins are consistant and at the bottom and everything should work without a problem.
 
Last edited:
As for @CheesusSliced problem, I'd say it's fine to use depth = -y to solve the sorting issue given the simple nature of the problem/project. You could only update it when the character has moved on the y-axis, but even sticking it in a Step Event will show no noticeable performance hit unless the amount of things being sorted this way every frame grows significantly - at which point if profiling showed it to be an issue you'd look into custom sorting. No need to pre-emptively optimise though.

Use the same code to set the depth of static items in their Create Event and make sure your sprite origins are consistant and at the bottom and everything should work without a problem.
Would this work for multiple followers? I plan to have some added and removed as the story progresses.
 

CMAllen

Member
Would this work for multiple followers? I plan to have some added and removed as the story progresses.
Yes, it will work with as many followers as you want (note: it won't solve overlap issues along the horizontal axis).

On the other hand, you could also move the layer's depth with layer_depth() function instead of the object itself. If the object is alone on its layer, then it will do the exact same thing without giving up additional features and capabilities you get with managed layers.

Also whenever I add depth = -y, the object disappears completely.
Has it been moved underneath a layer that was below it before?
 

chamaeleon

Member
If there's a slight issue with ordering when moving horizontally, one could always try special-case the player instance.
GML:
depth = -2*y-1;
while NPCs has
GML:
depth = -2*y;
In theory anyway. It implies that everything else that uses the depth approach (in order to walk behind trees properly and whatnot), also uses one or the other. Experimentation may be required to figure out if it works as expected when taking the environment into account.
 

chamaeleon

Member
Nope, I've tried even removing the other layers and it's still gone.
Do you have any drawing code in the draw event? Hopefully you have not made your object have a single line in the draw event that sets the depth. The depth needs to be set in a step event, so that it is set properly before drawing is invoked.
 
Do you have any drawing code in the draw event? Hopefully you have not made your object have a single line in the draw event that sets the depth. The depth needs to be set in a step event, so that it is set properly before drawing is invoked.
This will probably make you facepalm, but here is what I have for both step events:

Player:
GML:
hInput = keyboard_check(vk_right) - keyboard_check(vk_left);
vInput = keyboard_check(vk_down) - keyboard_check(vk_up);

if(hInput != 0 or vInput != 0){
dir = point_direction(0,0,hInput,vInput);
moveX = lengthdir_x(mvspd, dir);
moveY = lengthdir_y(mvspd, dir);


x += moveX;
y += moveY;

switch(dir){
    case 0: sprite_index = spr_kris_dw_right_move; break;
    case 90: sprite_index = spr_kris_dw_up_move; break;
    case 180: sprite_index = spr_kris_dw_left_move; break;
    case 270: sprite_index = spr_kris_dw_down_move; break;
    }
} else {
    image_index = 0
}

//follower following position and which way to turn
if (x != xprevious or y != yprevious){
    
    for(var i = array_size-1; i > 0; i --){
    
    follow_pos_x[i] = follow_pos_x[i-1];
    follow_pos_y[i] = follow_pos_y[i-1];
    
    toRecordSprite[i] = toRecordSprite[i-1];   
    
    }
    
    follow_pos_x[0] = x;
    follow_pos_y[0] = y;
    toRecordSprite[0] = sprite_index;   
}

    
obj_kris_dw_ow.depth = -2*y-1;
Follower:
GML:
switch(obj_kris_dw_ow.toRecordSprite[record]){

    case spr_kris_dw_down_move: sprite_index = s_walk_Sprite_Down; break;
    case spr_kris_dw_up_move: sprite_index = s_walk_Sprite_Up; break;
    case spr_kris_dw_left_move: sprite_index = s_walk_Sprite_Left; break;
    case spr_kris_dw_right_move: sprite_index = s_walk_Sprite_Right; break;
}

x = obj_kris_dw_ow.follow_pos_x[lastposrec];
y = obj_kris_dw_ow.follow_pos_y[lastposrec];

image_index = obj_kris_dw_ow.image_index;

obj_susie_dw_ow.depth = -2*y;
Also I have tried changing it back from object.depth= yada yada but that still didn't work.
 

chamaeleon

Member
When the code is running for an instance, modifying its instance variables does not require the use of the object name (in fact, it is not a good practice at all to include it as a general rule, except when you understand the consequences of doing so, you should use specific instances instead, which is implicit for the instance itself making use of its own instance variables).
GML:
obj_kris_dw_ow.depth = -2*y-1;
and
GML:
obj_susie_dw_ow.depth = -2*y;
should be
GML:
depth = -2*y-1;
and
GML:
depth = -2*y;
But this difference should not be cause for something to disappear. Are you saying that if you comment out the depth assignments temporarily, the sprites for the characters show up where you expect them, not necessarily in the right order but they show up? If so, like mentioned above, it seems like some layer would be just about the only culprit (or other sprites being drawn over, perhaps environmental instances or sprites).

I'd recommend creating a new room for the purpose of exploring the use of the depth with your characters, or maybe even a brand new test project that only does this.
 
But this difference should not be cause for something to disappear. Are you saying that if you comment out the depth assignments temporarily, the sprites for the characters show up where you expect them, not necessarily in the right order but they show up? If so, like mentioned above, it seems like some layer would be just about the only culprit (or other sprites being drawn over, perhaps environmental instances or sprites).

I'd recommend creating a new room for the purpose of exploring the use of the depth with your characters, or maybe even a brand new test project that only does this.
They do show up if the depth code is gone, but the second it's added they disappear. Attached is an image of the layers in said room, with instances being the layer the player is on and follower below is the follower.
 

Attachments

rIKmAN

Member
Do what chamaeleon suggested and make a new room with just the characters in it to get the depth working.
You have too much going on for you to be able to easily work out what might be happening and just saying "it doesn't work" isn't really any help.

Did you try hiding all the other layers so just the characters are visible?
Changing the depth to -y might make them visually disappear / be hidden but they don't cease to exist just because their depth changed, so use some debug messages etc to confirm they are still there, output the x/y location so you know roughly where to look etc - some basic debugging to find out what's hiding them and work outwhat is actually happening.

Using the Draw GUI Event to draw should also draw them on top of everything else for the purposes of "finding" them on the screen.
 
Do what chamaeleon suggested and make a new room with just the characters in it to get the depth working.
You have too much going on for you to be able to easily work out what might be happening and just saying "it doesn't work" isn't really any help.

Did you try hiding all the other layers so just the characters are visible?
Changing the depth to -y might make them visually disappear/be hidden but they don't cease to exist just because their depth changed, so use some debug messages, etc to confirm they are still there, output the x/y location so you know roughly where to look, etc - some basic debugging to find out what's hiding them and work out what is actually happening.

Using the Draw GUI Event to draw should also draw them on top of everything else for the purposes of "finding" them on the screen.
How would I do some of this debugging stuff? I'm fairly new to GMS and haven't really used it that much. Also the image attached is a completely new room with the depth code active.
 

Attachments

rIKmAN

Member
show_debug_message() will print messages to the console - like the x/y position of the character by using show_debug_message(string(x) + ", " + string(y)) which will help you know what area of the screen they should be appearing at. Moving around would change these values as the x/y changed from moving.

Drawing things in the Draw GUI Event uses the GUI layer so they will be drawn "on top" of everything drawn in regular Draw Events meaning you should be able to see them no matter what depth your other layers are. You could add draw_text(x, y, "I'm Here") to it and see where the text appears, or add draw_self() to it and have the characters sprite drawn on the GUI layer which should enable you to see it as it'll be drawn above everything else on regular "Draw" layers.

It seems like you have tried to run before you can walk and got muddled up, so strip things back to basics and get things working one at a time before trying to add in more complexity as not being able to do basic debugging is going to cause you a lot of headaches the further you get into your project.

The picture of a black screen is nice and all, but tells us literally nothing lol.
Have you added the characters object to the new room?
What layers are in the new room?
What are you expecting to appear in the new room?
Have you changed the new room to be the room that is launched by default as per the Room Manager?

Try and give us some information that will allow us to help you as having to guess at the many possible things that might be causing it is not the best way for you to get help or us to give it.

If you wanted to send me a link to your project via PM I'll take a look at it and see if I can see what the issue is.
You can export it using File > Export Project > YYZ and upload that file somewhere for me to download.

EDIT:
Looks like I'm going out for food so I've knocked up a little example showing a character you can move around with the arrow keys who has depth = -y in the Step Event to set it's depth every step as you move it around.

There are some other static objects (wall, character) whose depth = -y is set in the Create Event (as they are static) which you can move the character around and see how it will go infront/behind them based on the depth (y position on screen). It's the same principle we've been talking about here and works fine, so whatever issues you are having seem to be from whatever other things you have going on.

As I suggested above I'd look at stripping back whatever you have, maybe using this example as a base to get the characters working, then start adding in your other elements one by onegetting each functioning correctly as you go, having many things and not being that experienced makes it difficultto track down what is happening and why so start simple and build from there step by step.

I've PM'd you a link to the project as a .yyz file made usingthe latest stable version v2.3.7.603.
Just import it using File > Import in the IDE.
 
Last edited:
Top