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

GameMaker Making an object follow another object at a constant speed and distance

K

Kay

Guest
Not quite sure how to title my issue.

So, I made a segmented snake-type enemy and I want the body segments to follow the path of the snake head exactly. Right now it looks like this though.
The first few segments follow the motion of the head a little bit, but the ones after that just follow smoothly. This would be fine in some cases, but it's not the effect I want. I want a part to follow the part before it at a constant distance on a constant path.

This is the code I use inside the step event of the snake head object:
Code:
//following the mouse + snake motion
x = lerp(x, mouse_x, 0.02) + lengthdir_x(sin(t/8)*2, direction + 90); //t is a variable that increments every step
y = lerp(y, mouse_y, 0.02) + lengthdir_y(sin(t/8)*2, direction + 90);

//the following part spawns one segment and passes some variables,
//the most important one being the id of the object that spawned it

if ((tail_parts > 0) && (checkonce = false))
{
   tail_parts -= 1;
   checkonce = true;

   if tail_parts = 0
   {
       with instance_create_layer(x, y, "Instances", o_snakebody)
       {
           tail_parts = other.tail_parts;
           followthisone = other.takethis_id;
           takethis_id = id;
           checkonce = false;
           sprite_index = spr_snakeend;
       }
   }
   else
   {
       with instance_create_layer(x, y, "Instances", o_snakebody)
       {
           tail_parts = other.tail_parts;
           followthisone = other.takethis_id;
           takethis_id = id;
           checkonce = false;
           sprite_index = spr_snakebody;
       }   
   }
}

Now the step event of o_snakebody:
Code:
//this is obviously the issue
x = lerp (x, followthisone.x, 0.1);
y = lerp (y, followthisone.y, 0.1);
//afterwards it executes the second half of the code of o_snakehead
Lerp-ing towards a moving object on every frame obviously changes the distance constantly, which is my issue.
I previously fiddled around with it a bit and got it to look like this.
The path looks to be correct, but the segments don't actually move.

Here's what I changed:
Code:
//added this to both objects
//also passed those variables
if t mod 10 = 0
{
    delayed_x = x;
    delayed_y = y;
}

//changed the movement of the segments:
x = followthisone.delayed_x;
y = followthisone.delayed_y;
I also tried this code for the movement of the segments:
Code:
move_towards_point(followthisone.delayed_x, followthisone.delayed_y, 3);

//instead of
//x = followthisone.delayed_x;
//y = followthisone.delayed_y;
Which resulted in this. This seems to be very close, but the movement is very jittery.
The solution seems to be embarassingly simple, but I just don't know how to do it.
 

NightFrost

Member
I've done a segmented snake before. I had the head controlling everything. First, it creates a ds list to store previous positions with the length of (number of body parts * step delay between body parts) and fills the list with its current position (an array with length of two). As the body parts needs to know their position in order the head creates them in a loop and saved the instance IDs to an array. When the head moves, in step event it puts its new position at the end of the list and deletes the oldest at list position zero. Next it loops from one to number of body parts, reads a list position that is ((number of body parts - loop counter) * step delay between body parts) and assigns those coordinates to a body part fetched from the array that stores their IDs, from array position equal to loop number.

In pseudocodish (all untested and from memory):
Code:
// CREATE
Parts_Count = 20;
Parts_Delay = 5;
Parts_ID = array_create(Parts_Count -1);
Position_List = ds_list_create(Parts_Count * Parts_Delay);

Loop from 0 to Parts_Count -1
    Parts_ID[counter] = create instance of body part

Loop from 0 to (Parts_Count * Parts_Delay) - 1
    var This_Position = [head_x, head_y]
    ds_list_add(Position_List, This_Position)

// STEP
if(head has moved){
    var New_Position = [head_x, head_y]
    ds_list_add(Position_List, New_Position);
    ds_list_delete(Position_List, 0);
    for(var i = 1; i <= Parts_Count; i++)
        var This_Part = Parts_ID[i - 1];
        var This_Position = Position_List[| (Parts_Count - i) * Parts_Delay];
        This_Part.x = This_Position[0];
        This_Part.y = This_Position[1];
    }
}
If you have to set image angle for body part, read the Position_List just above it and get point_direction between body part and that position.

EDIT - I just realized this code is for a snake that is in constant motion while yours doesn't, so ignore the Step part that requires the head to be in motion. It will however make all the body parts ultimately end up at the same position the head is sitting at.
 
Last edited:
Top