GameMaker: Studio

GameMaker: Studio

View Stats:
[SOLVED] I'm having problems with my worm.
Doing something wrong here. I'm goofing around with a snake / worm game. One of those ones where you start out as just a head and as you eat food on the map you keep gaining segments.

I have two objects. The head and the body.

In the Step Event for my head object there is this code which creates the body segments...
// Adding segments if segments_to_add > 0 { var last_segment = ds_list_find_value(segment_list, ds_list_size(segment_list) - 1); wbody_x = last_segment.x; wbody_y = last_segment.y; wbody = instance_create(wbody_x,wbody_y,obj_player_body); ds_list_add(segment_list,wbody.id); wbody.segment_followed = last_segment; wbody.direction = direction; wbody.image_angle = wbody.direction; segments_to_add -= 1; }

In the Step Event for the body objects there is this code to make them follow the piece in from of them...
// Movement if instance_exists(segment_followed) { // Follow the segment in front of it angle_clamp = segment_followed.angle_clamp - (segment_followed.angle_clamp / 50); var podir = point_direction(segment_followed.x, segment_followed.y, x, y); var angdiff = clamp(angle_difference(podir,segment_followed.direction), - angle_clamp, angle_clamp); direction = segment_followed.direction + angdiff; x = segment_followed.x + lengthdir_x(sprite_width, direction); y = segment_followed.y + lengthdir_y(sprite_width, direction); } // Direction image_angle = direction;


What happens when running the program with those two code segments is that the head, controlled by the player, moves around just fine and can eat food just fine.

In the case of the player always eats food by travelling exactly to the right of the screen (angle 0) and the head having a sprite image aligned to face right (angle 0) the follow happens when I eat food and it adds on segments...

The first body segment when added is offset a bit from the head to the right of it and is facing away from it at a 90 degree angle.

The second segment is to the right of the previous segment at a 90 degree angle. Its facing is directly opposite of the head.

The third, and every segment beyond that, is trailing, identical to the one before it just slightly further back. If not for them all facing opposite of the direction the head is facing it would be the intended result.

Despite the incorrect positioning and facing of the first segment and the incorrect facings of the second(+) segments the actual movement and clamping to the angle of the segments they are following seem to be correct.


I'm suspecting that my issue is somewhere in one of those two sections of code, but I haven't been able to track it down. I've gotten some pretty psychodelic results though. Anyone see what my problem is? Besides me?


EDIT - UPDATE - I fixed my offset problem. That was due to me forgetting to set my sprite origins for the body segment when I made it.

Having tested again I see whats happening, but I don't know why thats what is happening.

Instead of the segments being created behind my head and trailing along with it they are behind added onto the front of the head and continuing off that way.

So the reason the first segment is always at 90 degrees from my head is that my head is pushing that segment out of the way to move and when it hits 90 angle clamping it just gets stuck that way.

All the other segments look correct because they are stuck to the front of the piece behind them and then just dragging along in a semi correct looking fashion.

So the segments aren't really facing the wrong direction... they are being dragged as I push that first segment along.

Still no idea why exactly they are being spawned in front of each other and using that as the pivot point instead of spawning behind and using that as a the pivot point.
Last edited by BOYCOTT S-T-E-A-M!; Apr 22, 2016 @ 1:41am
< >
Showing 1-15 of 16 comments
Still haven't been able to figure out whats going on here and how to fix it.

There has to be someone out there who has made a worm, snake, or chain of some kind.

Anyone able to see where my problem lies?
NGMZeroX Apr 19, 2016 @ 6:00am 
Is this a traditional snake game, where the head can only move in the main 4 directions or does the head turn smoothly.

at the head step event, is it intended for wbody.direction to equal the head direction not the last segment direction?

does the snake start with a head only, or a head and a single body segment ?
Its a non-traditional one. The head can rotate freely and move anywhere. Arrows keys control turning, acceleration and deceleration. Its kinda like driving a tank from an overhead view.


Good eye on the direction. I hadn't noticed that. It should be being set to the direction of the last segment. I'll give that a shot here then.

The snake starts with a head only. Everytime you collide with a food object it increases the heads "segments_to_add" variable by 1 which is then checked and handled in the step event of the head posted above.
Changed that error with the direction in the step event. No changes with my issues though sadly. Still ending up with the body segments being added onto the front of the head and being dragged along instead of following behind.
NGMZeroX Apr 19, 2016 @ 4:42pm 
I don't understand what most the code in the step events of the body object do. I copyed your code, I got a moveing snake where it 1st segment always stuck on it face. I sat angle_clamp inm the head to 180, and it seem fixed in my case

edit:
obj_player_head: create
segment_list = ds_list_create();
ds_list_add(segment_list, id);
segments_to_add = 0;
angle_clamp = 180;

the rest is a copy paste of your code
Last edited by NGMZeroX; Apr 19, 2016 @ 4:47pm
Hmmm... interesting.

I tried what you posted with changing the angle_clamp to 180 instead of 90 in the head create event.

That did allow the body segments to trail behind it in a way that seemed right.

However, there is still wonky stuff going on with it.

The reason they look like they are trailing properly with 180 is because aren't being locked to only varying by 90 degrees of the heads direction. They are still being placed at the front of the snake, but with 180 as the clamp they can drag behind in a more visually appealing way.

The image_angles of the body segments are also still wrong. Despite them being tied to the direction of the body itself, which should be facing within a clamped arc of the segment ahead of them, they are still facing backwards.



For reference, I based my code on this tutorial - Dividable Segmented Worm Tutorial

In that tutorial the worm is entirely computer controlled. It chooses a random destination, lunges towards it, and then chooses a new destination.

The worms sprites is a rectangle with a red line to show facing and they were made with the sprite facing upwards in the sprite editor so all the code relating to angles has to alter the direction by 90 since Gamemaker assumes sprites are facing right in the editor for angle 0.

The worm segments are added immediately upon creation of the worm. The head makes the first segment and they get set as parent and child. Each segment after that does the same where its creating segment is its parent and the segment it creates is its child.



I made the tutorial program and it works exactly as shown in the video. No problems at all.

I wanted to expand upon the idea and fix some things that bothered me so I uesd that tutorials code as a basis to make my own. The differences between mine and the one in the video is...

* In mine the players controls the head. I doubt this is related to my issues at all.

* The head creates all of the segments and segments are added over time. The segments aren't using the one before it as a parent directly but are stored on a ds_list where they pull the information from the segment before it. I did it this way so the head would know all the segments at that below to it to later allow me to expand by doing more interesting collisions and things. Its possible that this is related to my issues, but the segments seem to be getting created and associated with the segment ahead of them fairly well.

* I made my sprites facing to the right in the editor. I figure it would be more proper and would help with me not needing to account for altering the direction by 90 to compensate. I kind of suspect that this might be a large part of my issue. Even though I, to my knowledge at least, changed the code to compensate for the difference in facing.
I suppose with it being a small program I can just post up the entire thing in code. Maybe someone will see something that way or maybe someone will want to experiment with it in some fashion. I could just upload the GM project file to an upload site, but those always feel shady to me.

Sprites
I have three sprites.

spr_food - this sprites appearance and size is almost entirely irrelevant. It exists solely to collide with so anything you use for it should be fine. So long as its at least 16 x 16 in size it should work out just fine.

spr_player_head - 32 x 16 in size. It looks and was made exactly like in the video linked in previous posting aside from being made to face to the right in the sprite editor instead of being 16 x 32 in size and facing up like in the tutorial video.

spr_player_body - exact copy of spr_player_head aside from coloring. I have the head colored blue with a red facing line and the body colored white with a red facing line.

The origin point for the food is centered.

The origin point for the head is X = 0 Y = 8
The origin point for the body is X = 32 Y = 8

I've tried using using varying combinations of X = 0 or X = 32 for the head and body. It produces different looking results, but my overall problem is still the same where the body segments are added onto the front of the snake instead of the back.

Objects
I have four objects total. obj_controller, obj_food, obj_player_head, and obj_player_body.

obj_controller
Simply used to generate a piece of food and to allow me to exit the game with ESC key. The follow code is just copied out of the "show information" button on the object window.
Information about object: obj_controller Sprite: Solid: false Visible: true Depth: 0 Persistent: false Parent: Children: Mask: No Physics Object Create Event: execute code: randomize(); Step Event: execute code: // If there is no food on the screen add another piece of food. If player is present. if instance_exists(obj_player_head) { if !instance_exists(obj_food) { instance_create(irandom(room_width),irandom(room_height),obj_food); } } Key Press Event for <Escape> Key: end the game

obj_food
This object consists solely of a collision check with obj_player_head and sets it up to add another segment.
Information about object: obj_food Sprite: spr_food Solid: false Visible: true Depth: 0 Persistent: false Parent: Children: Mask: No Physics Object Collision Event with object obj_player_head: execute code: with(other) { segments_to_add += 1; } instance_destroy();

obj_player_head
This is the head of the snake / worm and is able to be driven around by the player. Going off the screen wraps to the opposite side. Collecting food makes you longer. The draw even just adds some text for debugging that shows the number of segments waiting to be added and the id of the last instance stored in the ds_list of body worm segments.
Information about object: obj_player_head Sprite: spr_player_head Solid: false Visible: true Depth: 0 Persistent: false Parent: Children: Mask: No Physics Object Create Event: execute code: segments_to_add = 0; // This tracks how many segments need added to the worm angle_clamp = 90; // This is the angle at which body segments will be clamped segment_list = ds_list_create(); // A list to store the ids of all this worms segments ds_list_add(segment_list, id); Step Event: execute code: // Movement if keyboard_check(vk_left) direction += 5; if keyboard_check(vk_right) direction -= 5; if keyboard_check(vk_up) speed += 0.5; if keyboard_check(vk_down) speed -= 0.5; if speed > 10 then speed = 10; if speed > 0 then speed -= 0.05; if speed < 0 then speed = 0; image_angle = direction; move_wrap(true,true,16); // Adding segments if segments_to_add > 0 { var last_segment = ds_list_find_value(segment_list, ds_list_size(segment_list) - 1); var wbody_x = last_segment.x; var wbody_y = last_segment.y; var wbody = instance_create(wbody_x,wbody_y,obj_player_body); ds_list_add(segment_list,wbody.id); wbody.segment_followed = last_segment; wbody.direction = last_segment.direction; wbody.image_angle = wbody.direction; segments_to_add -= 1; } Draw Event: execute code: draw_self(); // Draws the head // Debugging text draw_text(10,10, segments_to_add); draw_text(10,20, string(ds_list_find_value(segment_list, ds_list_size(segment_list) - 1)));

obj_player_body
Nothing fancy to say about this. It only has a create and step event. Create sets up some variables to a default setting just to ensure they exist before trying to use or change them. Step event is just supposed to make it follow the segment before it.
Information about object: obj_player_body Sprite: spr_player_body Solid: false Visible: true Depth: 0 Persistent: false Parent: Children: Mask: No Physics Object Create Event: execute code: segment_followed = 0; // This will hold the id of the segment this one is following. angle_clamp = 0; Step Event: execute code: // Movement if instance_exists(segment_followed) { // Follow the segment in front of it angle_clamp = segment_followed.angle_clamp - (segment_followed.angle_clamp / 50); var podir = point_direction(segment_followed.x, segment_followed.y, x, y); var angdiff = clamp(angle_difference(podir,segment_followed.direction), - angle_clamp, angle_clamp); direction = segment_followed.direction + angdiff; x = segment_followed.x + lengthdir_x(sprite_width, direction); y = segment_followed.y + lengthdir_y(sprite_width, direction); } // Direction image_angle = direction;

Rooms

I've only got one room and its quite simple.

rm_player_snake - I don't see a way to copy / paste the room information, but its pretty basic.

850 x 480 sized room. 60 room speed. No background, just filled with the color white. The only two instances are 1 obj_controller and 1 obj_player_head. The controller comes first in instance order.


That is everything to create the project I'm trying to get working currently. It wouldn't take more than a couple minutes for anyone to copy and paste everything to re-create this and experiment with it themselves. Seeing the problem in action and being able to directly mess with it is probably the only real way to solve it I'd guess. Unless something is blatantly wrong with my math / angle formulas.
Last edited by BOYCOTT S-T-E-A-M!; Apr 19, 2016 @ 6:20pm
To further experiment and find a cause I went back to the project I had that was just following the tutorial video exactly and had an AI controlled worm lunging at random points.

I wanted to rule out my ds_list stuff as being the issue so I just took the AI controlled head and added in movement controls to it and was able to duplicate the issue I'm seeing in my experimental player controlled project.

The culprit is how its handling direction and image_angle. I have no idea how to fix it but here is what I found.

In the obj_head create event it has this...
// Set target target_x = irandom(room_width); target_y = irandom(room_height);
Pretty self explainatory. Its just picking a random x/y coordinate in the room to be its destination.

In the step event it has this controlling the heads movement...
// Movement if x != target_x {x -= (x-target_x) / 30;} if y != target_y {y -= (y-target_y) / 30;}

Also in the step event is this code to control the direction and facing...
dir = point_direction(target_x,target_y,x,y); image_angle = dir + 90;
It doesn't use the instance built-in direction variable at all. It just points in the direction of the target x/y location. The image angle is then set based on the point direction angle and modified by 90 since the sprite was created facing up instead of to the right.

If I replace the movement code with my movement controls code and replace the line "dir = point_direction(target_x,target_y,x,y);" with "dir = direction" the end result is almost the same as what I'm seeing in my messed up player controlled worm.

The worm moves the same as in my project where the head pushes the first body segment and drags the rest instead of correctly dragging them all behind it. The difference is that the facing of them is correct this time.

From what I'm guessing at this point, the AI controlled worm that looks right is actually travelling in the opposite direction of what the direction variable holds. Which would make sense as to why my worms using the direction variable seem to be moving and facing the wrong way.

So I think I see whats going on at this point... but I'll be damned if I know what to do to make it work as desired.

EDIT - UPDATE - Tested my theory that the AI version is travelling in the opposite direction and it seems correct. By replacing the line "dir = point_direction(target_x,target_y,x,y);" with "dir = direction + 180" the worm "appears" correct.

It looks like its traveling in the right direction, but that is only because its traveling in reverse with the facing of the sprite set backwards.

Knowing whats going on is helpful, but I still don't know how to solve my issue. I'm not sure how to go about having the body segments be made to properly follow behind my head object.
Last edited by BOYCOTT S-T-E-A-M!; Apr 19, 2016 @ 8:11pm
Leon Fook Apr 19, 2016 @ 8:08pm 
Turn out you just need to reverse the speed of the head
//YOUR CODE if keyboard_check(vk_up) speed += 0.5; if keyboard_check(vk_down) speed -= 0.5; if speed > 10 then speed = 10; if speed > 0 then speed -= 0.05; if speed < 0 then speed = 0; //MY CODE if keyboard_check(vk_up) speed -= 0.5; if keyboard_check(vk_down) speed += 0.5; if speed < -10 then speed = -10; if speed < 0 then speed += 0.05; if speed > 0 then speed = 0;

nothing change drasticly, it just now your snake head is moving backward and the segment is following correctly.

The problem? Here's the thing:
Your snake is moving toward the direction it's pointing, and the body is moving away the direction it's pointing. The head is moving foward and the body is moving backward, and the segmented is programed to angled toward the direction 0.

i'll explain more of it with picture if you're blur with my half ass explanation.
Ha ha. Thanks for the response. I was just updated my post above about having found that same thing.

Changing the speeds to travel backwards is a workaround to get the appropriate looking results.

Though that would cause issues when using the direction for things like firing off a projectile or whatever as I would always need to compensate by 180 to the direction since the worm is actually travelling backwards.


Any idea on how I'd do it so that the pieces are working as intended? So that the head would travel in the proper direction. The body segments would follow the segment ahead of them with their direction before being turned clamped within a 90 degree tolerance of the segment ahead of them.
Leon Fook Apr 19, 2016 @ 8:30pm 
Originally posted by BOYCOTT S-T-E-A-M!:
Any idea on how I'd do it so that the pieces are working as intended? So that the head would travel in the proper direction. The body segments would follow the segment ahead of them with their direction before being turned clamped within a 90 degree tolerance of the segment ahead of them.
i might have to try modifying your body segment code for that, so i think that would take a bit time to understand the segment stuff as i've never dealt with anything like this.

from what i can see, if you wanna shoot a bullet you can have a bullet shooting variable for that, something like:
bullet_shoot_dir = direction - 180; if(bullet_shoot_dir < 0){ bullet_shoot_dir += 360; } if(bullet_shoot_dir >360){ bullet_shoot_dir -= 360; }
then just pretend that bullet_shoot_dir is the direction you're moving toward, and use that variable to do every direction check.
it's a workaround for your current problem for now.
Thanks again for the response.

Ideally, I'd prefer to avoid having my code necessitate the use of workarounds.


I appreciate the assistance on this. Its been troubling me for several days now. It doesn't seem like it should be that hard, but anything with math, such as angles and all that, doesn't process so well in my head so I end up with a lot of problems.


I've also never done anything like this before. The video I used as a tutorial wasn't really much of a tutorial. They didn't really explain anything about what their lines of code were doing or the reasons behind why they handled things the way they did.

After making the project they showed in the video and messing around with it a bunch I decided to adapt it to my own variant where the player would control the head, but it resulted in all this.

I did my best to pick apart all of their lines of code relating to direction and movement. I looked up everything in the manual to try to ensure I was getting how it was working properly. I've used point_direction and lengthdir myself in my own projects so I figured it wouldn't be that difficult to change over, but I was proven wrong.

I'm currently picking all those lines of code apart once again and trying to mess them to see if I can track down whats going now that understand that the AI version is travelling in the opposite direction all the time.
Alright. I believe I got it working correctly.

I set the origin for both the head and body sprites to be X = 0 and Y = 8.

Then I changed these lines of code in the bodies step event...
// OLD CODE var podir = point_direction(segment_followed.x, segment_followed.y, x, y); x = segment_followed.x + lengthdir_x(sprite_width, direction); y = segment_followed.y + lengthdir_y(sprite_width, direction); // NEW CODE var podir = point_direction(x,y, segment_followed.x, segment_followed.y); x = segment_followed.x - lengthdir_x(sprite_width, direction); y = segment_followed.y - lengthdir_y(sprite_width, direction);

So basically just swapping the order of the x/y coordinates listed in the point_direction line and changed it from adding to subtracting lengthdir.

I'll need to go back over those lines math-wise to try to understand exactly what was going on there since I now know that those were responsible for my ass-backwards worm issue.


The only thing I'm noticing now that its traveling in the correct direction is that there is a bit of distance between the head and the first body segment. The body segments follow each other very closely as if connected, but the first body segment has a bit of a gap between it and the head when the worm is in motion.

Its really minor issue that I might be able to fix by altering the origin points a bit to give more overlap. Its not a critical problem by any means though I am curious why there is the gap when the movement code is the same for all body segments.

Thanks again for the assistance. When I code myself into a corner its always hard to dig my way back out without having someone bounce ideas off of and either point out a flaw in my code or at least make me look at things from a different perspective.

If you try that out and happen to see whats causing the head / body gap thats occuring I'd love to know.
Last edited by BOYCOTT S-T-E-A-M!; Apr 19, 2016 @ 9:12pm
Leon Fook Apr 19, 2016 @ 11:22pm 
Originally posted by BOYCOTT S-T-E-A-M!:
// NEW CODE var podir = point_direction(x,y, segment_followed.x, segment_followed.y); x = segment_followed.x - lengthdir_x(sprite_width, direction); y = segment_followed.y - lengthdir_y(sprite_width, direction);
Turn out changing the point_direction position doesn't works for me, but just change the x and y will do.
Also the gap is caused by the code execution order, so by changing the Step Event to End Step will make sure the position change of the head object will execute first.

Also:
https://dl.dropboxusercontent.com/u/41066092/GIF.gif
Last edited by Leon Fook; Apr 19, 2016 @ 11:23pm
Strange. Swapping the x/y from the x2/y2 spot to the x1/y1 spot in that function in addition to changing the + in the lengthdir formula to - didn't allow traveling in the correct direction on your end?

For me if I did only one or the other the result would be that my body segments would rapidly alternate between sticking off the left or right side at a 90 degree angle to the head. They were swapping so quickly that they appeared to be in both places at once and transparent.

Since I noticed that either change on its own produced that same result, curiousity begged that I try changing them both which turned out to solve my direction issue.

I still need to go back and do "the maths" to see how exactly those two lines change direction of travel.


As for the gap... thanks a ton for that information. I've never messed with the Start / End Step Event types before. I knew that they would change the order that step events would be executed, but I've never found a use for them myself and everything I've ever read has never really pointed out a use for them.

I would have never figured out to use that to prevent the gap on my own. Execution order would not have crossed my mind. If anything I'd have likely assumed it was related to the speed or acceleration rates of the body segments and tried to mess with them to match speeds with the head. Likely this would have been a disaster.

I'll have to try changing the Step Event to an End Step when I get a chance.



In regards to your image, thats how my brain feels when I try to process math. It was at once horrifying and hypnotizing. I'd say it almost qualifies as art.

I'm under the assumption that is your worm with debugging text showing things like the x/y coordinates and I assume direction, image_angle, etc?

Thanks again for all the assistance by the way. You've been a great help.
Last edited by BOYCOTT S-T-E-A-M!; Apr 20, 2016 @ 6:22pm
< >
Showing 1-15 of 16 comments
Per page: 1530 50

Date Posted: Apr 16, 2016 @ 2:24pm
Posts: 16