Snake tail follow smoothly

Updated on February 22, 2018 in Unity
Share on Facebook0Tweet about this on TwitterShare on Google+0Share on Reddit0
16 on February 12, 2018

I want to make a snake tail follow the head properly and smoothly position by position, i tried a lot of logic and tired out looking on the web but got no help, the problem is when i try to make the tail follow, either it is not smooth but following properly or it is smooth but not following properly.

I dont know where i am missing things, the last thing i tried is something like i made 2 scripts.

  1. Snake (That contains a list of Vector2 and in the update function every position of the current tail object is being stored in the list)
  2. SnakeManager(This checks if there is a gap between two tails and iterate every tail to its head’s list positions)
  • Liked by
Reply
9 on February 12, 2018

Do you want the tail to keep the sharp turns like in Snake?

Also, what is the tail made up of?

on February 12, 2018

Yea sharp turn just like in the game Snake Vs Blocks, and currently tail is made up of circle sprite.

 

Wise
on February 12, 2018
public class SnakeBodyPart : MonoBehaviour
{
    public GameObject parentBodyPart;
    public Vector3 lastPosition;
    public Vector3 nextPosition;
    
    private float _timer = 0;
    
    
    void Update()
    {
        if(parentBodyPart == null) return; // prevents null refrence errors
        
        _timer = Mathf.Clamp(_timer + Time.deltaTime, 0, 1); // this will prevent us from over shooting
        
        float distanceToNextPosition = Vector3.Distance(transform.position, nextPosition);
        
        if(distanceToNextPosition > 0) // we are further than 0 away from where we want to be
        {
            transform.position = Vector3.Lerp(lastPosition, nextPosition, _timer);
        }
        
        else // we're not further than 0, so we must be at our destination
        {
            lastPosition = nextPosition;
            nextPosition = parentBodyPart.transform.position;
            _timer = 0;
        }
    }
}

 

I’ll explain all this in the next comment.

Wise
on February 12, 2018

real quick first, you can just replace Vector3 with Vector2. The math is still the same.

 

 

So here is a class that will control each body part separately.

all you need to do, is give it it’s “parentBodyPart” a.k.a. the part in front of it in the snake.

 

_timer is very important here, and it’s even more important that it doesn’t go higher than 1. That’s because were using Lerp to move to points between lastPosition and NextPosition. _timer is where on the line between the 2 points we want to be. With 0 putting up at the beginning, and 1 putting us at the end.

 

So, once timer has reached 1, we’re at the point we set as nextPosition. We can store that point as our lastPosition, and get our next nextPosiion by grabbing  it from wherever out parentBodyPart is.

 

You’ll probably still want some kind of SnakeHead script to manage the creation of all other SnakeBodyParts, add then to some uniform collection, a List will work fine; and for plugging in the parentBodyParts of all the body parts.

on February 12, 2018

I tried this out, this works like the following is correct but there are huge gaps between each body part and it doesnt actually follow the path of the head, like in snake vs blocks when you move the snake in a zigzag way even the last tail part will follow every position where the had been while being in a zigzag fashion, this is quite similar to my very first approach, what i did is calculated a vector between current and head and started lerping my body part to head and it will follow and take the shortest path possible but i need the tail to follow every position of head because that brings the slingy snake motion in the picture.

Wise
on February 12, 2018

Make _timer a static float.

And, can the snake ever not be moving forward?

on February 12, 2018

The snake will be moving forward and can move left and right but cannot be moved backwards, 

https://www.youtube.com/watch?v=-3DDs1hjxZM

 

here is a link to the game i am talking about

on February 12, 2018

And there is a lot of delay in getting all the snakebodyparts into place.

Wise
on February 12, 2018

How many units is the snake moving per second?

Because the above code doesn’t have anything to make the nodes move faster or slower. That may be whats causing the gaps. Try multiplying Time.deltaTime by something like 10 (or however fast your snake is moving)

on February 12, 2018

i am using transform.Translate(Vector2.up * Time.deltaTime); on head 

Show more replies
  • Liked by
Reply
Cancel
2 on February 12, 2018

 

 

Wise
on February 12, 2018

I think I have something that works a lot like what you want.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Snake : MonoBehaviour
{
    public List<Vector2> path;
    public List<GameObject> tail;
    private float _timer = 0;
    private void Start()
    {
        path = new List<Vector2>();
    }
    void Update()
    {
       transform.Translate(Vector2.up * Time.deltaTime);
       transform.Translate(new Vector2(Input.GetAxis("Horizontal"), 0) * Time.deltaTime);
        _timer += Time.deltaTime;
        if (_timer >= 0.1f)
        {
            path.Add(transform.position);
            _timer = 0;
        }
        if(path.Count > tail.Count)
        {
            path.RemoveAt(0);
        }
        for(int i = 0; i < path.Count; i++)
        {
            tail[i].transform.position = path[i];
        }
    }
}

 

I think you had the right idea the first time with storing the position in a list. Now every 1/10 a second we record the position (you can increase/decrease that number to adjust the spacing as you see fit). This should help with the spacing issue by keeping some distance between each of the recorded points. We then have another list of body parts that we want to be following the path were drawing as we go. We don’t want to keep track of every position ever though. So, when we add a new position we check to see if we even have enough body parts for it. If not, we ditch the oldest one (the [0] position).

on February 13, 2018

This works way better but still not as i wanted, this follows perfectly every point, but this is a bit jerky and i dont get how to maintain distance between each block of snake, plus when i move the snake with a greater force in left or right direction i can see the snake body parts being very far from each other, is there some other way of doing such a smooth movement or am i on the right track? It feels like the snake in the game Snake Vs BLocks is a single body and it never breaks and jerks and it completely follows every turn it takes, can we do this by using something else, like joints or something?

Sometimes i feel like the problem isnt big enough to go into such deep detail and then later on we figure out a very easy solution, i dont know if i am right or wrong, but yea any help will be appreciated and thank you  for helping!

 

Show more replies
  • Liked by
Reply
Cancel
0 on February 14, 2018

I wrote this code, this is following completely perfectly but i want this to transition from one point to other smoothly, right now it feels super jerky to me and if i move the snake very fast every bodypart collapses into each other to a single point.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Snake : MonoBehaviour {
public List<Vector2> snakeBodyList = new List<Vector2>();
public GameObject headOfCurrent;
private float timer = 0;
private Snake sn;
private bool lerp = false;
private Vector2 targetPos = new Vector2();
public float minDistance = 1f;
// Use this for initialization
 void Start ()
 {
 if (headOfCurrent != null)
 {
 sn = headOfCurrent.GetComponent<Snake>();
 }
 }
// Update is called once per frame
 void Update()
 {
 timer += Time.deltaTime;
//if (timer > .5f)
 {
 snakeBodyList.Add(transform.position);
if (headOfCurrent != null && sn.snakeBodyList.Count > 0 && Vector2.Distance(transform.position, headOfCurrent.transform.position) > minDistance)
 {
 while (headOfCurrent != null && sn.snakeBodyList.Count > 0 && Vector2.Distance(transform.position, headOfCurrent.transform.position) > minDistance)
 {
 //lerp = true;
 transform.position = sn.snakeBodyList[0];
 //targetPos = sn.snakeBodyList[0];
 sn.snakeBodyList.RemoveAt(0);
 }
 }
timer = 0;
 }
//if (lerp && (Vector2)transform.position != targetPos)
 //{
 // transform.position = Vector2.MoveTowards(transform.position, targetPos, Time.deltaTime * 20f);
 //}
//else
 //{
 // lerp = false;
 //}
 }
}

 

  • Liked by
Reply
Cancel
0 on February 15, 2018

I got to know people are using iTween for this perfectly smooth snake movement but i can’t get how do they use it?

  • Liked by
Reply
Cancel