Search Unity

Snake Game - Instantiate Problem

Discussion in 'Scripting' started by Deleted User, Aug 29, 2018.

  1. Deleted User

    Deleted User

    Guest

    Hi,

    I'm working on a small 'snake' game, based on the tutorials I've went through, I've currently got the movement down that I'd like and the instantiate works.

    However, when I instantiate a segment it does so at the 'head' of my snake, I'd love some advice on how to instantiate the extra 'segment' at the tail end instead ? Code :
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerControl : MonoBehaviour {
    6.  
    7.     [Header("Snake Segments")]
    8.     public List<GameObject> Segments = new List<GameObject>();
    9.  
    10.     [Header("Snake Segments At Start")]
    11.     public int SnakeSize;
    12.  
    13.     [Header("Snake Body - Prefab")]
    14.     public GameObject SnakeBodyPrefab;
    15.  
    16.     [Header("Snake - Direction")]
    17.     public string PlayerDirection = "Up";
    18.  
    19.     [Header("Snake - Speed")]
    20.     public int TicTimeLimit = 4;
    21.     private int TicTime;
    22.  
    23.     private Vector3 PlayerPosition;
    24.  
    25.     void Start ()
    26.     {
    27.         PlayerPosition = transform.position;
    28.  
    29.         for (int i = 0; i < SnakeSize; i++)
    30.         {
    31.             AppendToSnake();
    32.         }
    33.     }
    34.  
    35.     void Update ()
    36.     {
    37.         Inputs ();
    38.         UpdateSnake ();
    39.  
    40.         // Test purposes - Add segment when space is pressed
    41.         if (Input.GetKeyDown(KeyCode.Space))
    42.         {
    43.             AppendToSnake();
    44.         }
    45.     }
    46.  
    47.     void Inputs ()
    48.     {
    49.         if (Input.GetKeyDown (KeyCode.UpArrow))
    50.         {
    51.             PlayerDirection = "Up";
    52.             TicTime = TicTimeLimit;
    53.         }
    54.         if (Input.GetKeyDown (KeyCode.DownArrow))
    55.         {
    56.             PlayerDirection = "Down";
    57.             TicTime = TicTimeLimit;
    58.         }
    59.         if (Input.GetKeyDown (KeyCode.LeftArrow))
    60.         {
    61.             PlayerDirection = "Left";
    62.             TicTime = TicTimeLimit;
    63.         }
    64.         if (Input.GetKeyDown (KeyCode.RightArrow))
    65.         {
    66.             PlayerDirection = "Right";
    67.             TicTime = TicTimeLimit;
    68.         }
    69.     }
    70.  
    71.     void AppendToSnake ()
    72.     {
    73.         GameObject SnakeBody = Instantiate(SnakeBodyPrefab as GameObject, PlayerPosition, Quaternion.identity);
    74.  
    75.         if (!Segments.Contains(SnakeBody))
    76.         {
    77.             Segments.Add (SnakeBody);
    78.         }
    79.     }
    80.  
    81.     void UpdateSnake()
    82.     {
    83.         TicTime++;
    84.         if (TicTime >= TicTimeLimit)
    85.         {
    86.             //////////////////////////////////////
    87.  
    88.             for (int j = Segments.Count - 1; j > 0; j--)
    89.             {
    90.                 if (j > 0)
    91.                 {
    92.                     Segments[j].transform.position = Segments[j - 1].transform.position;
    93.                 }
    94.             }
    95.  
    96.             Segments[0].transform.position = transform.position;
    97.  
    98.             //////////////////////////////////////
    99.  
    100.             switch (PlayerDirection)
    101.             {
    102.                 case "Up" :
    103.                     PlayerPosition.z++;
    104.                     break;
    105.                 case "Down" :
    106.                     PlayerPosition.z--;
    107.                     break;
    108.                 case "Left" :
    109.                     PlayerPosition.x--;
    110.                     break;
    111.                 case "Right" :
    112.                     PlayerPosition.x++;
    113.                     break;
    114.             }
    115.             TicTime = 0;
    116.         }
    117.         transform.position = PlayerPosition;
    118.     }
    119. }
     
  2. tonemcbride

    tonemcbride

    Joined:
    Sep 7, 2010
    Posts:
    1,089
    That's not quite the way Snake works - when you want to grow the snake you move all your segments forward as normal but then add a segment onto the end that was in the same place as the last segment before you moved everything.

    You could do it like this:

    1) Change 'AppendToSnake' function to just set a boolean value when you want to add a segment
    Code (CSharp):
    1. bool addSegment = false;
    2.  
    3. void AppendToSnake ()
    4. {
    5. addSegment = true;
    6. }
    2) Change your 'UpdateSnake' function to check if the 'addSegment' flag is set once it's already moving all the segments forward by one block. If it is then move all the segments forward and add a new segment where the last one previously was.
    Code (CSharp):
    1. void UpdateSnake()
    2. {
    3.             TicTime++;
    4.             if (TicTime >= TicTimeLimit)
    5.             {
    6.                     // Take a note of the current 'last segment' position
    7.                     Vector3 lastSegmentPosition;
    8.                     if( Segments.Count > 0 )
    9.                     {
    10.                         lastSegmentPosition = Segments[ Segments.Count - 1 ].transform.position;
    11.                     }
    12.                    
    13.                     //////////////////////////////////////
    14.  
    15.                     for (int j = Segments.Count - 1; j > 0; j--)
    16.                     {
    17.                             if (j > 0)
    18.                             {
    19.                                     Segments[j].transform.position = Segments[j - 1].transform.position;
    20.                             }
    21.                     }
    22.  
    23.                     Segments[0].transform.position = transform.position;
    24.                    
    25.                     // Add a new segment if we need to
    26.                     if( addSegment == true )
    27.                     {
    28.                         addSegment = false;
    29.                         GameObject SnakeTail = Instantiate(SnakeBodyPrefab as GameObject, lastSegmentPosition, Quaternion.identity);
    30.                         Segments.Add( SnakeTail );
    31.                     }
    32.  
    33.                     //////////////////////////////////////
    34.  
    35.                     switch (PlayerDirection)
    36.                     {
    37.                             case "Up" :
    38.                                     PlayerPosition.z++;
    39.                                     break;
    40.                             case "Down" :
    41.                                     PlayerPosition.z--;
    42.                                     break;
    43.                             case "Left" :
    44.                                     PlayerPosition.x--;
    45.                                     break;
    46.                             case "Right" :
    47.                                     PlayerPosition.x++;
    48.                                     break;
    49.                     }
    50.                     TicTime = 0;
    51.             }
    52.             transform.position = PlayerPosition;
    53.     }
    54. }
     
    Deleted User likes this.
  3. Deleted User

    Deleted User

    Guest

    @tonemcbride

    Thanks for all your help and such an in depth answer to my query. I appreciate it a lot.

    I implemented your logic, and apart from a small change, lastSegmentPosition was throwing up an error about a being a local variable, so I had to declare it at the start of my script for it to accept it. Everything is now working as it should, I still have yet to add more logic to my script before it is complete.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PlayerControl : MonoBehaviour {
    6.  
    7.     [Header("Snake Segments")]
    8.     public List<GameObject> Segments = new List<GameObject>();
    9.  
    10.     [Header("Snake Segments At Start")]
    11.     public int SnakeSize;
    12.  
    13.     [Header("Snake Body - Prefab")]
    14.     public GameObject SnakeBodyPrefab;
    15.  
    16.     [Header("Snake - Direction")]
    17.     public string PlayerDirection = "Up";
    18.  
    19.     [Header("Snake - Speed")]
    20.     public int TicTimeLimit = 4;
    21.     private int TicTime;
    22.  
    23.     private Vector3 PlayerPosition;
    24.     private Vector3 lastSegmentPosition;
    25.  
    26.     private bool AddSegment = false;
    27.  
    28.     void Start ()
    29.     {
    30.         PlayerPosition = transform.position;
    31.  
    32.         for (int i = 0; i < SnakeSize; i++)
    33.         {
    34.             AppendToSnake();
    35.         }
    36.     }
    37.  
    38.     void Update ()
    39.     {
    40.         Inputs ();
    41.         UpdateSnake ();
    42.  
    43.         // Test purposes - Add segment when space is pressed
    44.         if (Input.GetKeyDown(KeyCode.Space))
    45.         {
    46.             AppendToSnake();
    47.         }
    48.     }
    49.  
    50.     void Inputs ()
    51.     {
    52.         if (Input.GetKeyDown (KeyCode.UpArrow))
    53.         {
    54.             PlayerDirection = "Up";
    55.             TicTime = TicTimeLimit;
    56.         }
    57.         if (Input.GetKeyDown (KeyCode.DownArrow))
    58.         {
    59.             PlayerDirection = "Down";
    60.             TicTime = TicTimeLimit;
    61.         }
    62.         if (Input.GetKeyDown (KeyCode.LeftArrow))
    63.         {
    64.             PlayerDirection = "Left";
    65.             TicTime = TicTimeLimit;
    66.         }
    67.         if (Input.GetKeyDown (KeyCode.RightArrow))
    68.         {
    69.             PlayerDirection = "Right";
    70.             TicTime = TicTimeLimit;
    71.         }
    72.     }
    73.  
    74.     void AppendToSnake ()
    75.     {
    76.         AddSegment = true;
    77.     }
    78.  
    79.     void UpdateSnake()
    80.     {
    81.         TicTime++;
    82.         if (TicTime >= TicTimeLimit)
    83.         {
    84.             // Make note of last Segment position
    85.             if (Segments.Count > 0)
    86.             {
    87.                 lastSegmentPosition = Segments[Segments.Count - 1].transform.position;
    88.             }
    89.  
    90.             for (int j = Segments.Count - 1; j > 0; j--)
    91.             {
    92.                 if (j > 0)
    93.                 {
    94.                     Segments[j].transform.position = Segments[j - 1].transform.position;
    95.                 }
    96.             }
    97.  
    98.             Segments[0].transform.position = transform.position;
    99.  
    100.             // Add a new Segment if required
    101.             if (AddSegment == true)
    102.             {
    103.                 AddSegment = false;
    104.                 GameObject SnakeTail = Instantiate(SnakeBodyPrefab as GameObject, lastSegmentPosition, Quaternion.identity);
    105.                 Segments.Add(SnakeTail);
    106.             }
    107.  
    108.             switch (PlayerDirection)
    109.             {
    110.                 case "Up" :
    111.                     PlayerPosition.z++;
    112.                     break;
    113.                 case "Down" :
    114.                     PlayerPosition.z--;
    115.                     break;
    116.                 case "Left" :
    117.                     PlayerPosition.x--;
    118.                     break;
    119.                 case "Right" :
    120.                     PlayerPosition.x++;
    121.                     break;
    122.             }
    123.             TicTime = 0;
    124.         }
    125.         transform.position = PlayerPosition;
    126.     }
    127. }
    In the interests of 'cleanliness', I tried implementing SetParent to make the new body segments a child of my main object, but couldn't figure it out, any ideas why SetParent might not be working, I added mine in the AddSegment section, it parents the object, but right on top of the main object and the same with every subsequent object added thereafter, so a tail is never made?

    Regards
     
  4. DaemonicDreams

    DaemonicDreams

    Joined:
    Aug 23, 2018
    Posts:
    41
    When parenting objects you want to parent the transform of that object, like:
    Code (CSharp):
    1. // Add a new Segment if required
    2.             if (AddSegment == true)
    3.             {
    4.                 AddSegment = false;
    5.                 GameObject SnakeTail = Instantiate(SnakeBodyPrefab as GameObject, lastSegmentPosition, Quaternion.identity);
    6.                 SnakeTail.transform.SetParent(gameObject);
    7.                 Segments.Add(SnakeTail);
    8.             }
     
    Deleted User likes this.
  5. Deleted User

    Deleted User

    Guest

    @DaemonicDreamStudios

    Thanks, I've tried a few different methods now, they pretty much all parent, albeit 'directly', i.e. I no longer have a tail, literally every object is placed directly on top of the 'parent', not sure why ? Maybe it's the wrong place I'm trying to parent ( the tutorials I researched would suggest otherwise though ) ?

    In it's most basic form, I had :
    Code (CSharp):
    1. SnakeTail.transform.SetParent(transform);
     
  6. DaemonicDreams

    DaemonicDreams

    Joined:
    Aug 23, 2018
    Posts:
    41
    The transform of a child specifies the location relative to its parent. So it sounds like your transform position is at (0/0/0). You could parent the object first and then set your relative transform.
     
  7. Deleted User

    Deleted User

    Guest

    Not sure how to achieve that ? :confused:

    I tried instantiating first, then parenting, then moving to the LastSegmentPosition, still the same outcome, ah well, it's not really important, I can live without it, just would've been cleaner in Unity if I'd been able to set the new segments as children.
     
  8. DaemonicDreams

    DaemonicDreams

    Joined:
    Aug 23, 2018
    Posts:
    41
    Add a direction to your segments. You can just use a Vector2 of length 1 (a normal). Say your player is moving "Right". The resulting normal would be a Vector2(1, 0). For left (-1, 0) and so on. Now store that information for each segment. Each time you move just add the directional vector of the segment (n+1) to your segment n's transform and set its directional vector to that direction. When adding a new segment you can then just check the position of the last segment and then subtract its direction. That is your new transform.

    EDIT: An easier solution would be to just parent each segment to the segment before that. The transform is then just the direction.
     
    Deleted User likes this.