Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Help trying to make a 3D snake game and understanding for loops

Discussion in 'Scripting' started by kmo86, May 8, 2021.

  1. kmo86

    kmo86

    Joined:
    Dec 15, 2020
    Posts:
    104
    I have followed this tutorial on making a 2D snake game. It works and also works in 3D with same controls meaning it is still a 2D game. I would like to make a 3D snake game and have tried changing controls to 3D like this
    Code (CSharp):
    1.   private void FixedUpdate()
    2.     {
    3.         transform.Translate(Vector3.forward * Time.deltaTime * speed);
    4.  
    5.         horizontalInput = Input.GetAxis("Horizontal");
    6.         transform.Rotate(Vector3.up * Time.deltaTime * rotationSpeed * horizontalInput);
    7.  
    8.         verticalInput = Input.GetAxis("Vertical");
    9.         transform.Rotate(Vector3.right * Time.deltaTime * rotationSpeed * verticalInput);
    10.  
    11.        
    12.     }
    So the full snake script looks like this.

    Code (CSharp):
    1. public class Snake : MonoBehaviour
    2. {
    3.     Vector2 direction = Vector2.right;
    4.     private List<Transform> segments = new List<Transform>();
    5.     public Transform segmentPrefab;
    6.  
    7.     public float speed = 20;
    8.     private float forwardInput;
    9.     private float verticalInput;
    10.  
    11.     public int initialSize = 4;
    12.  
    13.     // Start is called before the first frame update
    14.     void Start()
    15.     {
    16.         ResetState();
    17.     }
    18.  
    19.   private void FixedUpdate()
    20.     {
    21.         transform.Translate(Vector3.forward * Time.deltaTime * speed);
    22.  
    23.         horizontalInput = Input.GetAxis("Horizontal");
    24.         transform.Rotate(Vector3.up * Time.deltaTime * rotationSpeed * horizontalInput);
    25.  
    26.         verticalInput = Input.GetAxis("Vertical");
    27.         transform.Rotate(Vector3.right * Time.deltaTime * rotationSpeed * verticalInput);
    28.  
    29.        
    30.     }
    31.  
    32.   private void Grow()
    33.     {
    34.         Transform segment = Instantiate(segmentPrefab);
    35.         segment.position = segments[segments.Count - 1].position;
    36.  
    37.         segments.Add(segment);
    38.     }
    39.  
    40.     private void ResetState()
    41.     {
    42.         for (int i = 1; i < segments.Count; i++)
    43.         {
    44.             Destroy(segments[i].gameObject);
    45.         }
    46.         segments.Clear();
    47.         segments.Add(transform);
    48.  
    49.         for (int i = 1; i < initialSize; i++)
    50.         {
    51.             segments.Add(Instantiate(segmentPrefab));
    52.         }
    53.        
    54.         transform.position = Vector3.zero;
    55.     }
    56.      
    57.  
    58.     private void OnTriggerEnter(Collider other)
    59.     {
    60.         if (other.tag == "Food")
    61.         {
    62.             Grow();
    63.         }
    64.         if(other.tag == "Obstacle")
    65.         {
    66.             ResetState();
    67.         }
    68.     }
    69. }
    70.  
    Using this script the controls work but the new segments won't join onto the snake tho they do spawn in one place and don't move. Yet if I change to 2D control with this in update it works as it should.
    Code (CSharp):
    1.  void Update()
    2.     {
    3.         if (Input.GetKeyDown(KeyCode.UpArrow))
    4.         {
    5.             direction = Vector2.up;
    6.         }
    7.         if (Input.GetKeyDown(KeyCode.DownArrow))
    8.         {
    9.             direction = Vector2.down;
    10.         }
    11.         if (Input.GetKeyDown(KeyCode.LeftArrow))
    12.         {
    13.             direction = Vector2.left;
    14.         }
    15.         if (Input.GetKeyDown(KeyCode.RightArrow))
    16.         {
    17.             direction = Vector2.right;
    18.         }
    19.  
    20.     }
    21.  
    22.     private void FixedUpdate()
    23.     {
    24.         for(int i = segments.Count - 1; i > 0; i--)
    25.         {
    26.             segments[i].position = segments[i - 1].position;
    27.         }
    28.  
    29.         transform.position = new Vector3(
    30.            Mathf.Round(transform.position.x) + direction.x,
    31.            Mathf.Round(transform.position.y) + direction.y
    32.            );
    33.     }
    I can't understand why the segments spawn on the snake in 2D version but not 3D version. All the scrips are being used in a 3D game.

    Also is there anywhere I could learn better how for loops work as I'm guessing it could help me with this.
     
  2. TheNightglow

    TheNightglow

    Joined:
    Oct 1, 2018
    Posts:
    201
    well in the new FixedUpdate method you are no longer moving the segments^^
    what the for loop does in the original FixedUpdate:
    it goes through every segment you have in the list "segments" and moves it to the position of its predecessor

    you are simply missing these 4 lines in your new movement function:
    Code (CSharp):
    1.         for(int i = segments.Count - 1; i > 0; i--)
    2.         {
    3.             segments[i].position = segments[i - 1].position;
    4.         }
    how for loops generally work:

    you have some counter, often called i, some function you use on that counter, often i = i-1 or i = i+1, and a continue condition to break after
    now with your example:

    you have a list of segments, lets say in it are currently 4 segments of your snake, as an example

    you begin by setting i to be the amount of elements in that list - 1, so i will start of as 3 (since 4-1) in our example
    now the body of your for declearation can get executed with i == 3
    the body being everything inside the { }
    thus
    Code (CSharp):
    1. segments[i].position = segments[i - 1].position;
    gets executed with i ==3 (if the continue condition is met, more about this later)
    Code (CSharp):
    1. segments[3].position = segments[2].position;
    after the first execution the counter function gets applied to i, in your case this is i-- (i-- means that i gets decreased by one, thus mathmaticly the same as i=i-1 (there is some technical difference about execution order you dont need to worry about for now))
    thus after this i will be 2 (since 3-1 = 2)

    now the for loop will check if your continue condition was broken, this one is defined by your for loop as i > 0
    since i == 2 right now the condition is still valid, since 2 > 0, thus we continue and execute the function body again, now with i == 2

    thus we now execute:
    Code (CSharp):
    1. segments[2].position = segments[1].position;
    and so on, always using whatever function you defined on the counter, then checking the condition, then entering the loop body untill the condition is no longer met

    after another iterartion we will reach i == 0, thus your condition of i > 0 in the for loop is no longer met, and the application exits the loop without executing the function body another time

    ---------------

    now how do we know which function and numbers you need in the loop header?

    for your case you are interating through a list, so you know the smallest allowed index is 0, the largest allowed index is is the amount of elements in that list -1, since you cant access elements beyond those points (since your lists has no entries for them)
    since in your list you are actually working on i-1 (since you work on the predecessor of the current element) you can actually not enter the index 0 either, since 0-1 is -1 and thus outside the bounds of your list

    in other words, you know that you need to iterate through the range of 1 and segments.Count-1
    in your case you start at the largest index (i=segments.Count-1) and count down (i--) to 1 (i>0)
    in theory you could also do it the other way around, by starting with the smallest (i=1) and counting up (i++) to the largest index of your list (i < segments.Count)
     
  3. kmo86

    kmo86

    Joined:
    Dec 15, 2020
    Posts:
    104
    I have done the script like this now but the snake doesn't move at all now.
    Code (CSharp):
    1. public class Snake : MonoBehaviour
    2. {
    3.     Vector2 direction = Vector2.right;
    4.     private List<Transform> segments = new List<Transform>();
    5.     public Transform segmentPrefab;
    6.  
    7.     public float speed = 20;
    8.     public float rotationSpeed = 60;
    9.     private float forwardInput;
    10.     private float verticalInput;
    11.     private float horizontalInput;
    12.  
    13.     public int initialSize = 4;
    14.  
    15.     // Start is called before the first frame update
    16.     void Start()
    17.     {
    18.         ResetState();
    19.     }
    20.  
    21.     // Update is called once per frame
    22.     void Update()
    23.     {
    24.         transform.Translate(Vector3.forward * Time.deltaTime * speed);
    25.  
    26.         horizontalInput = Input.GetAxis("Horizontal");
    27.         transform.Rotate(Vector3.up * Time.deltaTime * rotationSpeed * horizontalInput);
    28.  
    29.         verticalInput = Input.GetAxis("Vertical");
    30.         transform.Rotate(Vector3.right * Time.deltaTime * rotationSpeed * verticalInput);
    31.     }
    32.  
    33.     private void FixedUpdate()
    34.     {
    35.         for(int i = segments.Count - 1; i > 0; i--)
    36.         {
    37.             segments[i].position = segments[i - 1].position;
    38.         }
    39.  
    40.      
    41.     }
    42.  
    43.     private void Grow()
    44.     {
    45.         Transform segment = Instantiate(segmentPrefab);
    46.         segment.position = segments[segments.Count - 1].position;
    47.  
    48.         segments.Add(segment);
    49.     }
    50.  
    51.     private void ResetState()
    52.     {
    53.         for (int i = 1; i < segments.Count; i++)
    54.         {
    55.             Destroy(segments[i].gameObject);
    56.         }
    57.         segments.Clear();
    58.         segments.Add(transform);
    59.  
    60.         for (int i = 1; i < initialSize; i++)
    61.         {
    62.             segments.Add(Instantiate(segmentPrefab));
    63.         }
    64.        
    65.         transform.position = Vector3.zero;
    66.     }
    67.      
    68.  
    69.     private void OnTriggerEnter(Collider other)
    70.     {
    71.         if (other.tag == "Food")
    72.         {
    73.             Grow();
    74.         }
    75.         if(other.tag == "Obstacle")
    76.         {
    77.             ResetState();
    78.         }
    79.     }
    80. }
    81.