Search Unity

Snake tail follow - need an offset

Discussion in 'Scripting' started by LizThrelfo, Jan 15, 2013.

  1. LizThrelfo

    LizThrelfo

    Joined:
    Apr 20, 2012
    Posts:
    24
    Hi there I'm working on a clone of snake and I'm having an issue with the tail.

    At the moment my snake is made up of a series of sprite prefabs (i use 2dtoolkit) which are instantiated when the food is eaten. The issue is with how it is currently moving: the tail segments will follow the head (the head being the first object in the array) correctly except they overlap. This is causing issues in my attempts to add lose conditions should the head collide with the tail.

    With some digging I've noticed that the distance between each sprite will lengthen if I increase the move speed. This isn't what I want. Instead I would like the segments to be a set distance away from one another (my offset value) at all times but maintain the movement behaviour they currently have, ideally with a movespeed which can still be altered as the game progresses. Unfortunately I've hit a wall with my knowledge and can't seem to figure out how to do so.

    I hope that makes sense, let me know if any clarification is needed. Below I've posted the script currently taking care of movement.

    Any help would be greatly appreciated.

    Code (csharp):
    1.  
    2.  
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. public class GameManager : MonoBehaviour {
    7.    
    8.     GameObject Snake;
    9.     Food FoodScript;
    10.     public GameObject SnakePrefab;
    11.     public GameObject FoodPrefab;
    12.    
    13.     public GameObject[] SnakeTail;
    14.    
    15.     float offset = 0.075f; //the ideal distance between each body part
    16.     public float moveSpeed = 0.1f;
    17.    
    18.     bool up = false;
    19.     bool down = false;
    20.     bool right = false;
    21.     bool left = false;
    22.    
    23.    
    24.     void Start () {
    25.        
    26.         //sets starting length of the snake
    27.         SnakeTail = new GameObject[3];
    28.        
    29.         //will spanw a segment for each snake tail object
    30.         for(int i =0; i < SnakeTail.Length; i++)
    31.         {
    32.             Instantiate(SnakePrefab, new Vector3(0, 0,0), Quaternion.identity);
    33.             offset += 0.075f;
    34.            
    35.         }
    36.         SnakeTail = GameObject.FindGameObjectsWithTag("Tail");
    37.        
    38.         //if there is no food make one
    39.         if(GameObject.FindGameObjectWithTag("Food") == null)
    40.             Instantiate(FoodPrefab, new Vector3(Random.Range(-1.3f,1.3f), Random.Range(-0.92f,1f), 0), Quaternion.identity);
    41.     }
    42.    
    43.    
    44.     void Update ()
    45.     {
    46.         PlayerInput();
    47.  
    48.         if(down)
    49.             MoveDown();
    50.         if(up)
    51.             MoveUp();
    52.         if(right)
    53.             MoveRight();
    54.         if(left)
    55.             MoveLeft();
    56.     }
    57.    
    58.    
    59.     //updates the postition of the tail
    60.     void DrawSnake()
    61.     {
    62.        
    63.         for(int i = (SnakeTail.Length-1); i >0; i--)
    64.         {
    65.             SnakeTail[i].transform.position = SnakeTail[i-1].transform.position;
    66.         }
    67.     }
    68.    
    69.     #region Movement Functions
    70.    
    71.     //down
    72.     public void MoveDown()
    73.     {
    74.         SnakeTail = GameObject.FindGameObjectsWithTag("Tail");
    75.         DrawSnake();
    76.         SnakeTail[0].gameObject.transform.position = new Vector3(SnakeTail[0].gameObject.transform.position.x, SnakeTail[0].gameObject.transform.position.y - moveSpeed, SnakeTail[0].gameObject.transform.position.z);
    77.     }
    78.     //up
    79.     public void MoveUp()
    80.     {
    81.         SnakeTail = GameObject.FindGameObjectsWithTag("Tail");
    82.         DrawSnake();
    83.         SnakeTail[0].gameObject.transform.position = new Vector3(SnakeTail[0].gameObject.transform.position.x, SnakeTail[0].gameObject.transform.position.y + moveSpeed, SnakeTail[0].gameObject.transform.position.z);
    84.     }
    85.     //right
    86.     public void MoveRight()
    87.     {
    88.         SnakeTail = GameObject.FindGameObjectsWithTag("Tail");
    89.         DrawSnake();
    90.         SnakeTail[0].gameObject.transform.position = new Vector3(SnakeTail[0].gameObject.transform.position.x + moveSpeed, SnakeTail[0].gameObject.transform.position.y, SnakeTail[0].gameObject.transform.position.z);
    91.     }
    92.     //left
    93.     public void MoveLeft()
    94.     {
    95.         SnakeTail = GameObject.FindGameObjectsWithTag("Tail");
    96.         DrawSnake();
    97.         SnakeTail[0].gameObject.transform.position = new Vector3(SnakeTail[0].gameObject.transform.position.x - moveSpeed, SnakeTail[0].gameObject.transform.position.y, SnakeTail[0].gameObject.transform.position.z);
    98.     }
    99.    
    100.     #endregion
    101.    
    102.     public void PlayerInput()
    103.     {
    104.         //up
    105.         if(Input.GetKeyDown(KeyCode.UpArrow)==true  down == false)
    106.         {
    107.             up = true;
    108.             down = false;
    109.             right = false;
    110.             left = false;
    111.             Debug.Log("up");
    112.         }
    113.         //down
    114.         if(Input.GetKeyDown(KeyCode.DownArrow)==true  up == false)
    115.         {
    116.             up = false;
    117.             down = true;
    118.             right = false;
    119.             left = false;
    120.             Debug.Log("down");
    121.         }
    122.         //left
    123.         if(Input.GetKeyDown(KeyCode.LeftArrow)==true  right == false)
    124.         {
    125.             up = false;
    126.             down = false;
    127.             right = false;
    128.             left = true;
    129.             Debug.Log("left");
    130.         }
    131.         //right
    132.         if(Input.GetKeyDown(KeyCode.RightArrow)==true  left == false)
    133.         {
    134.             up = false;
    135.             down = false;
    136.             right = true;
    137.             left = false;
    138.             Debug.Log("right");
    139.         }
    140.            
    141.     }
    142.    
    143.     //instantiate another tail segment
    144.     public void SpawnNewTail()
    145.     {
    146.         Instantiate(SnakePrefab, new Vector3(0, 0,0), Quaternion.identity);
    147.     }
    148.    
    149.    
    150. }
    151.  
     
  2. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    Well, I checked over some of your code. Though I am kind of miffed at the logic.

    So I decided to just build a new one. It seemed to me that if you have a series of points. Why keep instantiating new objects. A good idea would be to keep a list of these objects and reuse them when needed, and just hide them when you dont.

    There is probably some minor errors in there, but you should be able to work them out.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class Snake : MonoBehaviour {
    7.     public GameObject head; // the actual head
    8.     public GameObject body; // to be instantiated for the body
    9.     public GameObject tail; // for the end
    10.    
    11.     private List<SnakePart> snake; // manages points for the snake
    12.     private List<GameObject> bodyparts; // manages the snake body
    13.    
    14.     Vector3 move = Vector3.zero; // holds the move
    15.    
    16.     float nextTick = 0; // the time we wait til for the next update
    17.     public float updateEvery = 1; // duration of each step
    18.    
    19.     void Start(){
    20.         snake = new List<SnakePart>();
    21.         bodyparts = new List<GameObject>();
    22.     }
    23.    
    24.     void Update(){
    25.         move = new Vector3(Input.GetAxisRaw("Horizontal"),0,Input.GetAxisRaw("Vertical"));
    26.        
    27.         if(Time.timeScale == 0)
    28.         if(updateEvery <= 0) return;
    29.         while(Time.time > nextTick){
    30.             UpdateSnake();
    31.             nextTick += updateEvery;
    32.         }
    33.     }
    34.    
    35.     void UpdateSnake(){
    36.         Vector3 nextPoint = snake[0].position + move;
    37.         snake.Insert(0, new SnakePart(nextPoint, move));
    38.         if(!isEatingFood(nextPoint)){
    39.             snake.RemoveAt(snake.Count - 1);
    40.         }
    41.        
    42.         DrawSnake();
    43.     }
    44.    
    45.     bool isEatingFood(Vector3 point){
    46.         // check to see if food exists in the next spot
    47.         if(Physics.OverlapSphere(point, 0.5f).Length > 0){
    48.             return true;
    49.         }
    50.         return false;
    51.     }
    52.    
    53.     void DrawSnake(){
    54.         for(int i=0; i<snake.Count; i++){
    55.             if(i == 0){
    56.                 head.transform.position = snake[i].position;
    57.                 head.transform.LookAt(head.transform.position + snake[i].direction);
    58.             } else if(i == snake.Count - 1){
    59.                 tail.transform.position = snake[i].position;
    60.                 tail.transform.LookAt(tail.transform.position + snake[i].direction);
    61.             } else {
    62.                 if(i-1 == bodyparts.Count){
    63.                     GameObject obj = (GameObject)Instantiate(body, Vector3.zero, Quaternion.identity);
    64.                     bodyparts.Add(obj);
    65.                 }
    66.                 bodyparts[i-1].active = true;
    67.                 bodyparts[i-1].transform.position = snake[i].position;
    68.                 bodyparts[i-1].transform.LookAt(bodyparts[i-1].transform.position + snake[i].direction);
    69.             }
    70.         }
    71.         for(int i=snake.Count-2; i<bodyparts.Count; i++){
    72.             bodyparts[i].active = false;
    73.         }
    74.     }
    75. }
    76.  
    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class SnakePart{
    5.     public Vector3 position;
    6.     public Vector3 direction;
    7.    
    8.     public SnakePart(Vector3 pos, Vector3 dir){
    9.         position = pos;
    10.         direction = dir;
    11.     }
    12. }
    13.  
     
  3. LizThrelfo

    LizThrelfo

    Joined:
    Apr 20, 2012
    Posts:
    24
    Well i suppose it's never a good sign when your logic is flawed le sigh.

    Thanks for the code but it has me a little confused. I've very limited knowledge in terms of lists so I'm not sure what's happening in the start function: at which point are they initially populated? (i.e. in the similar way that my array is populated by finding the gameobjects with the appropriate tag). It's throwing out an 'OutOfRange' error straight away and I understand why but not how to amend it. Any ideas?
     
  4. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    A list is an array like object. If you use Javascript in web pages, it is much like a javascript array. You can add objects and remove objects from the list. You can also use ListVariable[index] to get items from the list.

    A list is dissimilar in that you can use some really specific functions to add and remove items.

    Here is the general documentation for it:
    http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx

    OK, an OutOfRange means that you are trying to access an element on the list when it is not there. So if you have 5 elements and you try to access the 6th one (ListVar[5], since it starts at zero) you will get that error.

    So, when writing it, I supposedly made it where it would add elements onto the list as needed. However, one of hte things I dont see, is something to start the whole mess.

    In the Start, add:

    Code (csharp):
    1.  
    2. snake.Add(new SnakePart(Vector3.zero, Vector3.up));
    3.  
    That way, you create at least the first part of the snake.
     
  5. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    Ok's after tinkering around with it some. I made the whole thing work just fine.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class Snake : MonoBehaviour {
    7.     public GameObject head; // the actual head
    8.     public GameObject body; // to be instantiated for the body
    9.     public GameObject tail; // for the end
    10.    
    11.     private List<SnakePart> snake; // manages points for the snake
    12.     private List<GameObject> bodyparts; // manages the snake body
    13.    
    14.     Vector3 move = Vector3.zero; // holds the move
    15.    
    16.     float nextTick = 0; // the time we wait til for the next update
    17.     public float updateEvery = 1; // duration of each step
    18.    
    19.     void Start(){
    20.         snake = new List<SnakePart>();
    21.         bodyparts = new List<GameObject>();
    22.         move = Vector3.up;
    23.         snake.Add(new SnakePart(Vector3.zero, Vector3.up));
    24.    
    25.         head.transform.parent = transform;
    26.         tail.transform.parent = transform;
    27.     }
    28.    
    29.     void Update(){
    30.         var newMove = new Vector3(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical"),0);
    31.         if(newMove != Vector3.zero) move = newMove;
    32.        
    33.         if(Time.timeScale == 0)
    34.         if(updateEvery <= 0) return;
    35.         while(Time.time > nextTick){
    36.             UpdateSnake();
    37.             nextTick += updateEvery;
    38.         }
    39.     }
    40.    
    41.     void UpdateSnake(){
    42.         Vector3 nextPoint = snake[0].position + move;
    43.         snake.Insert(0, new SnakePart(nextPoint, move));
    44.         if(!isEatingFood(nextPoint)){
    45.             snake.RemoveAt(snake.Count - 1);
    46.         }
    47.        
    48.         DrawSnake();
    49.     }
    50.    
    51.     bool isEatingFood(Vector3 point){
    52.         // check to see if food exists in the next spot
    53.         Collider[] food = Physics.OverlapSphere(point, 0.4f);
    54.         if(food.Length > 0){
    55.             if(food[0].transform.parent == transform){
    56.                 Destroy(gameObject);
    57.                 return false;
    58.             }
    59.             foreach(Collider morsel in food){
    60.                 Destroy(morsel.gameObject);
    61.             }
    62.             return true;
    63.         }
    64.         return false;
    65.     }
    66.    
    67.     void DrawSnake(){
    68.         for(int i=0; i<snake.Count; i++){
    69.             if(i == 0){
    70.                 head.transform.position = snake[i].position;
    71.                 head.transform.LookAt(head.transform.position + snake[i].direction);
    72.             } else if(i == snake.Count - 1){
    73.                 tail.transform.position = snake[i].position;
    74.                 tail.transform.LookAt(tail.transform.position + snake[i].direction);
    75.             } else {
    76.                 if(i-1 == bodyparts.Count){
    77.                     GameObject obj = (GameObject)Instantiate(body, Vector3.zero, Quaternion.identity);
    78.                     obj.transform.parent = transform;
    79.                     bodyparts.Add(obj);
    80.                 }
    81.                 bodyparts[i-1].active = true;
    82.                 bodyparts[i-1].transform.position = snake[i].position;
    83.                 bodyparts[i-1].transform.LookAt(bodyparts[i-1].transform.position + snake[i].direction);
    84.             }
    85.         }
    86.         if(snake.Count > 2){
    87.             for(int i=snake.Count-2; i<bodyparts.Count; i++){
    88.                 bodyparts[i].active = false;
    89.             }
    90.         }
    91.     }
    92. }
    93.  
    I had to add a start point and a start direction. (move was Vector3.zero, so that is not good) I also refined the "food" section and added in where if you tried to eat yourself, you died.

    This with the "SnakePart" from above works just fine.

    Start, speed and directions are controlled in newMove, the Move in Start and the adding of the first point in the snake in Start.
     
  6. xNightWolfx

    xNightWolfx

    Joined:
    Sep 26, 2013
    Posts:
    4
    you can post the whole code?
     
  7. The_Game_Shukla

    The_Game_Shukla

    Joined:
    Jan 26, 2017
    Posts:
    8
    @bigmisterb how to make the movement smooth the snake runs stuttering the whole time and thanks for the script on how to make snake movement but what to do the snake runs stuttering due to the updateevery and nexttick would u suggest an improvement what to do, it would be helpful.
     
  8. The_Game_Shukla

    The_Game_Shukla

    Joined:
    Jan 26, 2017
    Posts:
    8
    @bigmisterb and also when i assign the script to head object and then put all of them in inspector then game runs but when i make prefab of them and drag and drop it gives me error of "Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption. UnityEngine.Transform:set parent(Transform) " what to do?
     
  9. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Generally speaking you should post your own thread with your question, and details.

    Anyways, it sounds like you need to make sure you're setting the parent to a game object in the scene, and not a prefab.

    Feel free to start a thread and post your code, using code tags, if you are not sure how to do it. Someone can probably help you out. :)

    Consider taking the time to run through some more basic tutorials, if you are very new to Unity. :)
     
  10. The_Game_Shukla

    The_Game_Shukla

    Joined:
    Jan 26, 2017
    Posts:
    8
    Ok Thanks man for the suggestion will do