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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Adding to list in one instantiated objects script is adding to all instances

Discussion in 'Scripting' started by taekwondokid963, Apr 21, 2023.

  1. taekwondokid963

    taekwondokid963

    Joined:
    Jan 12, 2023
    Posts:
    2
    Hi i am new to unity and c# so this is my first try to make my own program on it so any other tips than my problem would also be appreciated :)

    I am using unity to make a program which uses a simple genetic algorithm to get a square (ai) from one side of a platform to the other (Image linked here) once it goes through one generation and moving right is determined the best and all the new instantiated prefabs are created and there Aimovement list variable set to move right. In the resetAi method all is correct until the for loop where it should be giving each ai a single extra random movement however instead each time it executes: instancesScipt.AiMovements.Add(possibleMovements[randomNum]); it adds that movement to every ais scripts variable and then goes onto the next ai which has the one already added and then adds another one and the problem repeats so by the end of the loop each ai which was only meant to have gained a random movement instead has 10 movements added and is the exact same as every other ai.

    the controller script attached to empty object

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class AiManager : MonoBehaviour
    6. {
    7.     public GameObject AiPrefab;
    8.  
    9.     private string[] possibleMovements = { "MoveRight", "MoveLeft", "Jump" };
    10.     public int startingAiNum = 50;
    11.     public int currentAiNum;
    12.     public int generation = 1;
    13.     public List<GameObject> Ais = new List<GameObject>();
    14.     private bool completed = false;
    15.     private int waiting = 200;
    16.     public float sideSpeed = 2f;
    17.     public float jumpSpeed = 4f;
    18.     public int movementNum = 0;
    19.     private Vector2 velocityCheck = new Vector2(0f, 0f);
    20.     private Rigidbody2D randomAiRb;
    21.     public List<string> optimalMovements;
    22.  
    23.     private void Awake()
    24.     {
    25.         for (int turn = 0; turn < startingAiNum; turn++)
    26.         {
    27.             GameObject newInstance = Instantiate(AiPrefab) as GameObject;
    28.             Ais.Add(newInstance);
    29.             GenerateMovement(turn);
    30.  
    31.         }
    32.         currentAiNum = startingAiNum;
    33.  
    34.     }
    35.  
    36.     // Update is called once per frame
    37.     void Update()
    38.     {
    39.  
    40.         if (waiting < 200)
    41.         {
    42.             waiting++;
    43.         }
    44.         else
    45.         {
    46.             randomAiRb = Ais[Random.Range(0, Ais.Count)].GetComponent<Rigidbody2D>();
    47.             waiting = 0;
    48.             for (int turn = 0; turn < currentAiNum; turn++)
    49.             {
    50.                 Rigidbody2D AiRigidBody = Ais[turn].GetComponent<Rigidbody2D>();
    51.                 if (AiRigidBody.transform.position.x < -6f)
    52.                 {
    53.                     GameObject placeHolderAi = Ais[turn];
    54.                     //Ais[turn].active = false;
    55.                     Ais.Remove(placeHolderAi);
    56.                     Destroy(placeHolderAi);
    57.                     currentAiNum--;
    58.                     turn--;
    59.                     continue;
    60.                 }
    61.                 AiAtributes instancesScript = Ais[turn].GetComponent<AiAtributes>();
    62.                 if (movementNum < instancesScript.AiMovements.Count)
    63.                 {
    64.                     CompleteMovement(turn, movementNum);
    65.                     if (turn == currentAiNum - 1)
    66.                     {
    67.                         movementNum++;
    68.                     }
    69.                 }
    70.                 else if (movementNum >= instancesScript.AiMovements.Count - 1
    71.                 && (randomAiRb.velocity.x != 0 && randomAiRb.velocity.y != 0)
    72.                 && (turn == currentAiNum - 1))
    73.                 {
    74.                     if (turn == currentAiNum - 1)
    75.                     {
    76.                         movementNum++;
    77.                     }
    78.  
    79.                 }
    80.                 else if (turn == currentAiNum - 1)
    81.                 {
    82.                     currentAiNum = startingAiNum;
    83.                     movementNum = 0;
    84.                     generation++;
    85.                     CalculateBestMovements();
    86.                     ResetAis();
    87.                     print("here");
    88.                     break;
    89.                 }
    90.  
    91.             }
    92.         }
    93.  
    94.  
    95.  
    96.     }
    97.  
    98.     private void GenerateMovement(int turn)
    99.     {
    100.         AiAtributes instancesScript = Ais[turn].GetComponent<AiAtributes>();
    101.         //
    102.         int randomNum = Random.Range(0, 3);
    103.         //
    104.         instancesScript.AiMovements.Add(possibleMovements[randomNum]);
    105.     }
    106.  
    107.     private void CompleteMovement(int turn, int movement_Num)
    108.     {
    109.         AiAtributes instancesScript = Ais[turn].GetComponent<AiAtributes>();
    110.         //instancesScript.AiMovements
    111.         string movement = instancesScript.AiMovements[movement_Num];
    112.         Rigidbody2D AiRB = Ais[turn].GetComponent<Rigidbody2D>();
    113.         if (movement == "MoveRight")
    114.         {
    115.             AiRB.velocity = new Vector2(AiRB.velocity.x + sideSpeed, AiRB.velocity.y);
    116.         }
    117.         else if (movement == "MoveLeft")
    118.         {
    119.             AiRB.velocity = new Vector2(AiRB.velocity.x + -sideSpeed, AiRB.velocity.y);
    120.         }
    121.         else
    122.         {
    123.             AiRB.velocity = new Vector2(AiRB.velocity.x, AiRB.velocity.y + jumpSpeed);
    124.         }
    125.     }
    126.  
    127.     private void ResetAis()
    128.     {
    129.         for (int turn = 0; turn < currentAiNum; turn++)
    130.         {
    131.             GameObject placeHolderAi = Ais[0];
    132.             Ais.Remove(placeHolderAi);
    133.             Destroy(placeHolderAi);
    134.         }
    135.         Ais = new List<GameObject>();
    136.         for (int turn = 0; turn < startingAiNum; turn++)
    137.         {
    138.             GameObject newInstance = Instantiate(AiPrefab) as GameObject;
    139.             newInstance.name = "Ai" + turn.ToString();
    140.             Ais.Add(newInstance);
    141.             AiAtributes instancesScript = Ais[turn].GetComponent<AiAtributes>();
    142.             instancesScript.AiMovements = optimalMovements;
    143.         }
    144.         for (int turn = 0; turn < startingAiNum; turn++)
    145.         {
    146.             GenerateMovement(turn);
    147.         }
    148.  
    149.         currentAiNum = startingAiNum;
    150.     }
    151.  
    152.     private void CalculateBestMovements()
    153.     {
    154.         float bestFitness = -10000f;
    155.         for (int turn = 0; turn < currentAiNum; turn++)
    156.         {
    157.             AiAtributes instancesScript = Ais[turn].GetComponent<AiAtributes>();
    158.             float fitness = Ais[turn].transform.position.x * 15f;
    159.             if (instancesScript.collectedCoin)
    160.             {
    161.                 fitness = fitness + 120f;
    162.             }
    163.             else if (instancesScript.finished)
    164.             {
    165.                 fitness = fitness + 1000000f;
    166.             }
    167.             if (fitness > bestFitness)
    168.             {
    169.                 bestFitness = fitness;
    170.                 optimalMovements = instancesScript.AiMovements;
    171.             }
    172.         }
    173.     }
    174. }
    Script attached to each ai prefab
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class AiAtributes : MonoBehaviour
    6. {
    7.     public List<string> AiMovements = new List<string>();
    8.     public bool finished = false;
    9.     public bool collectedCoin = false;
    10.  
    11.     private void OnCollisionEnter2D(Collision2D other)
    12.     {
    13.         if (other.gameObject.name == "Finish")
    14.         {
    15.             finished = true;
    16.         }
    17.     }
    18. }
    a have put lots of print statements to find out what is going on to be able to i think narrow it down to that when i set the new ai's aimovement list to optimalmovements that somehow connects them but i can't find out how to get around it and all the solutions i have found and tried haven't worked or don't understand.

    thank you for any help.
     
  2. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,051
    All class instances (in contrast to struct instances) are passed by reference. If you assign them to another variable, it will just copy the reference and not copy the instance itself, both variables will point to the same instance and you will see the same changes from all variables pointing to the same instance.

    In your case, you're copying list references between
    AiMovements
    and
    optimalMovements
    and you end up with multiple variables pointing to the same list instance. You need to create new list instances instead (e.g. using
    new List<string>(otherList);
    ) or clear and copy the elements (e.g.
    list1.Clear(); list1.AddRange(list2);
    ).
     
  3. taekwondokid963

    taekwondokid963

    Joined:
    Jan 12, 2023
    Posts:
    2
    Perfect Thankyou that makes sense