How can I achieve a snake like motion where the body follows the head?

Started by
11 comments, last by haphazardlynamed 17 years, 10 months ago
Hi everyone, I have this erally annoying problem i can't figure out... I'm trying to achieve a sipmle movement type where I have a "snake" with a head and a body (let's say the body has 8 links), each represnted by a position vector. I want only the head to dictate movement (it moves randomly and chagnes velocity/direction), and the body is supposed to follow it fluently without dropping behind or overlapping itself) Currently I'm using a simple sin/cos on the angle between each link and the one it has to follow - but the results are not good enough as I cannot tell how "fast" the link has to go in order to stay exactly behind the one it's following. here's a screenshot of the problem: Image Hosted by ImageShack.us notice how the "head" has moved faster than the tail and also that the tail links ahve overlapped itself. Any ideas?
Advertisement
If each segment of the tail is simply following in the path of the head, can you simply make each tail segment be a delayed version of the head? So the head is the current head, the first segment in the tail is the head 1 tick ago, the second is the head two ticks ago, etc. So use a queue system that just holds copies of the head for the last X ticks.

If the tail segments are varying from the head's previous positions, then you could probably still do that but just add variations to the delayed head positions.
Tadd- WarbleWare
Well, the each 'Link' needs to be a certain distance from the one in front of it right?
so instead of calculating how 'fast' each link needs to move to catch up, just assume that the connections chaining them together are totally Solid, so it always moves the right distance (dont care about speed).

So, when the head moves, move the 1st link in a straight line towards the head to be the right distance.
then move the 2nd link towards the 1st link to be the right distance
and so on...
one at a time, in order, till you run out of links


Just a series of straight lines.
You don't even need to deal with Sin Cos functions, just use Vectors and the Distance Forumula.


What I mean to say is... Approach the problem Differently.
Rather than think of how Fast they need to move to keep up. Figure out Where they need to end up to maintain the connection.
If you treat each body part as a physical particle, you can connect them with joints and then simulate the system. This will make your snake look a little ropey, but it sounds like that's what you're going for.

Using Verlet integration and infinitely stiff springs would probably take the least amount of time to code up.

Along the same lines, you could use an implicit integration scheme and connect the body parts with nearly infinitely stiff springs, or you could use a physics system that supports hard constraints and connect the parts with pin joints. If you're not into writing physics code, though, you're probably better off using the first option.

John Edwards

EDIT: I just understood haphazardlynamed's suggestion. I like that better. It'll give you a slightly different look than the physics route, but it's far simpler. I guess it's like the Verlet method in the case where each body part is much heavier than the one following it.

[Edited by - PistachioPro on June 12, 2006 4:12:58 PM]
haphazardlynamed's solution is similar to the one I used recently for a snakelike creature, but for mine instead of having hard-joints (which looked robotic) I set a spring force toward the next position. It looks a bit more cartoony, but with tight enough springs it gives a very lifelike snake movement.

Check out my new game Smash and Dash at:

http://www.smashanddashgame.com/

First of all, thanks for taking the time to answer :)
There are several problems with both your aproaches:
Reana's method is the easiest to implement but what happens is that all the links just overlap each other and reach the head after about 10ms.
i cannot simply set the next link's position to it's neighbour's previous position because a link does not jump in whole width jumps (lets say the head is at (100,100) and the 2nd link is at (120,100), after the first iteration the
head would be at (101.5,99.2) and the 2nd link would be at (100,100)...
That quickly lead me to a different approach where i store only the deviation of the head (how much it moved each tick) and then use it to move the 2nd link the same amount in the next tick (and so on for all following links).
This led to another dead end, the effect of this being that the entire snake moves as if it's a solid rectangle made up of one chunk (since if the head moved 2px up, 1 left, this behaviour is then propogated to the 2nd link, then to 3rd and so on, resulting in the described effect).

Hope I'm making sense up till now, as this can be pretty hard to explain without actually seeing it in motion... ;)

on to haphazardlynamed's idea:
Unfortunatly, I think your method won't work as well ( if i understood correctly). It has the same problem I have exprienced so far - distance is not constant between links. let's say the head is moving left, and suddenly shifts direction 180 degrees - if I simply calculate the new position based on a fixed distance i will again end up with a stiff rectangle where all links would move with the head to the right (in effect making the head the tail).

I think a spring system is a good solution - However, it seems awfuly complex for such a simple task :)
Any more ideas?

thanks for everyone's help (and if i didn't make sense before, let me know and I'll try to explain it differently).

Ehud.
A spring system isn't at all complex... Hookes Law:

x = -kv

Where V is the vector between the target position and the current position, and -k is a spring constant.

Not terribly complex, is it? :)

Check out my new game Smash and Dash at:

http://www.smashanddashgame.com/

Quote:Original post by ehudros
Reana's method is the easiest to implement but what happens is that all the links just overlap each other and reach the head after about 10ms.
i cannot simply set the next link's position to it's neighbour's previous position because a link does not jump in whole width jumps (lets say the head is at (100,100) and the 2nd link is at (120,100), after the first iteration the
head would be at (101.5,99.2) and the 2nd link would be at (100,100)...
That quickly lead me to a different approach where i store only the deviation of the head (how much it moved each tick) and then use it to move the 2nd link the same amount in the next tick (and so on for all following links).
This led to another dead end, the effect of this being that the entire snake moves as if it's a solid rectangle made up of one chunk (since if the head moved 2px up, 1 left, this behaviour is then propogated to the 2nd link, then to 3rd and so on, resulting in the described effect).

it sounds like you are setting your link's position to the guy before it Too Quickly. Try instead of moving the link to it's predecessors previous position, move it to the predecessors 10th previous position. Or something with a bigger delay.
Or since it sounds like your snakehead is analog motion... one of the other methods might help...


Quote:Original post by ehudros
on to haphazardlynamed's idea:
Unfortunatly, I think your method won't work as well ( if i understood correctly). It has the same problem I have exprienced so far - distance is not constant between links. let's say the head is moving left, and suddenly shifts direction 180 degrees - if I simply calculate the new position based on a fixed distance i will again end up with a stiff rectangle where all links would move with the head to the right (in effect making the head the tail).

I dont think you got it....
Links do not inherit the same delta motion as the one before them, they simply move Towards the guy in front until they are the correct distance from it(and no closer than that)
When the head suddenly changes direction, the 1st link will move towards it, but not Onto it, since it is meant to stop a distance away before overlapping.
Thus the 2nd link will head towards the 1st, which is Not the same spot as the Head. and as you go farther down the line each one is going to lag behind to an even further degree. The key being the Minimal Distance condition to keep links from overlapping, and introduce Lag so it acts 'snakey'.

Quote:Original post by ehudros
Any more ideas?

I think the ideas present so far are all very good, and that you should spend a little time trying them out and doing some debugging work on them before moving on.



Quote:Original post by ehudros
Reana's method is the easiest to implement but what happens is that all the links just overlap each other and reach the head after about 10ms.
i cannot simply set the next link's position to it's neighbour's previous position because a link does not jump in whole width jumps (lets say the head is at (100,100) and the 2nd link is at (120,100), after the first iteration the
head would be at (101.5,99.2) and the 2nd link would be at (100,100)...
That quickly lead me to a different approach where i store only the deviation of the head (how much it moved each tick) and then use it to move the 2nd link the same amount in the next tick (and so on for all following links).
This led to another dead end, the effect of this being that the entire snake moves as if it's a solid rectangle made up of one chunk (since if the head moved 2px up, 1 left, this behaviour is then propogated to the 2nd link, then to 3rd and so on, resulting in the described effect).

I guess what I'm picturing is like this: Imagine if you just have the head on the screen moving around (e.g. like your mouse cursor does). Then imagine if you never clear the old images of the head as it moves. You'd have a trail of heads (like drawing in a paint tool). Then imagine if you started deleting old heads after X of them have been drawn. You'd end up with a trail of X heads. Where the end of the trail is deleting itself as the head moves on. So that's what I had in mind. I assume though that you are drawing the head smoothly moving across the screen and want to have X distinct links (rather than a solid trail of overlapping head images). So then you could do something like only keep every nth head image to space it out more also, and you could change how fast you delete them or how spaced out they are based on the speed of the head. I think that's how mouse cursor trails work, which would seem to sort of be what you want.

I'm not sure how you're implementing it, but it sounds like you have all the links drawn to start with. So you are still treating them as X objects that each need to move around. But I was thinking more of just a list of all head positions. So there's just one object and you are displaying it's location (with some chosen rate and number of images shown). If it's not an issue, you can just start the snake with no length until the head first moves far enough. Then start displaying the head and the nth image in the past. Then as it moves further also show the 2*nth, then 3*nth, etc. Assuming constant velocity that would have them evenly spaced following the head. If you want to keep them evenly spaced with varying head speeds you'll have to select which past images to save based on speed. If the snake does needs to be drawn completely at initialization, you'll just need to create a fake past list of positions that displays it like you want at the start.

The spring solution should work too, and that may be more of what you want if you need to keep the snake links as independent objects. As I think haphazardlynamed said in the last post, each link moves relative to the one in front of it. For the quick 180 deg change, the problem is that for springs you are now compressing the spring (imagine a slinky or real spring - if you are pulling one direction, then instantly switch to the opposite, the spring can't loop around to where the head was - it simply compresses now).

I'm not sure how springs are implemented if you want change to direction like that. I think you'd have to have each link approach the point that the direction change occurred (imagining a slinky, you'd have to somehow make the slinky loop over an object at the end point so that the tail was forced to go through that position also rather than getting compressed). So for your snake example, I'd imagine you'd have to remember the right-most position of the head and link 1 would spring towards that point until it reached it and then change direction itself and follow the head's new direction. Link 2 would do the same and approach link 1's rightmost position. So if you are wanting to turn in angles greater than 90 deg, I think you'd need some special logic to handle those cases. If you change direction alot, you'll have to be remembering lots of endpoints. If you go with the spring solution, work on forward (< 90 deg) motion only to start with, then try to implement the reversals. Maybe there's a generic solution for handling springs changing direction like you want, but I don't think that kind of motion is spring-like anymore. Maybe simply limit head turning to 90 deg if that's an option.

My list of head postions solution shouldn't have direction issues, it's just a matter of figuring out how to choose which images to display based on the speed of the head. Maybe some interpolation between images would be needed too to handle speed changes, but if you save a fine enough list of positions I don't think that would be needed.
Tadd- WarbleWare
Quote:Original post by reana1
The spring solution should work too, and that may be more of what you want if you need to keep the snake links as independent objects. As I think haphazardlynamed said in the last post, each link moves relative to the one in front of it. For the quick 180 deg change, the problem is that for springs you are now compressing the spring (imagine a slinky or real spring - if you are pulling one direction, then instantly switch to the opposite, the spring can't loop around to where the head was - it simply compresses now).


My solution for that was to put minimum-distance springs between each body part and the other body parts, so that if they snake turns quickly, it actually would push it's tail outward. It looks pretty realistic.

Check out my new game Smash and Dash at:

http://www.smashanddashgame.com/

This topic is closed to new replies.

Advertisement