Search Unity

PlayerPrefs + Awake() + List.add

Discussion in 'Scripting' started by BenVenNL, Dec 5, 2019.

  1. BenVenNL

    BenVenNL

    Joined:
    Oct 14, 2019
    Posts:
    56
    Short version (I'm @ work doing boring stuff):

    I have a 'basebuilding' type of situation where I instantiate Prefabs into my scene by mouseclick.
    Each Prefab has a script attached to it, wich, on Awake, adds it's transform position and some other info to a List. Using a self created "AddPositionTo List"-method

    Works like a charm!

    Now I added a basic 'Save and Load level layout' option to my scene using PlayerPrefs.
    Prefabs are instantiated perfectly on 'loading', all of the right type, all at the right position with the right Quaternions etc.

    But ...

    Only the first Prefab adds his position to the List.

    ...

    So I added some debugging to my script. Just before the "AddPositionTo List"-method and right after.

    All variables, postions, tags etc. are making it into the consoles log. So the Awake() is triggerd for all prefabs instantiated in my scene and the code runs.

    But why is only the first Prefab Instantiated able to add his position to the List?
     
  2. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    How is that list declared? Sounds like every prefab adds itself to another list, maybe?
     
  3. BenVenNL

    BenVenNL

    Joined:
    Oct 14, 2019
    Posts:
    56
    I'm not used to the whole terminology of scripting yet, but it's a public List. If that is what you mean. This List is created when the scene loads. So it's always there for use.

    It uses the same class script in both cases..
    When I do it by mouseclick, a prefab gets instantiated and the Awake() on the prefab adds information to the list.

    Now.

    On Load, the same prefab gets instantiated, using the same Awake() on the prefab to add information to the same List. But fails (exept for te first prefab).

    The only difference, when I click there is only one prefab instantiated within update(), only one script trying to add information to the List.

    On Load there is a loop trying to instantiate multiple prefabs, who all, on Awake(), try to add something to the List within one update().
     
  4. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    There shouldn't be any problem if the List is already initialized and every unit/prefab tries to add itself to that list.

    Are you sure you made your list only in one copy, like in some singleton object that is your central location for gathering that scene information?

    I wonder if you interpret the situation a bit wrong and you have many Lists, for example.

    Also, use Awake only to initialize objects so that you deal with the components etc. that exist in the object itself, not in other objects. In Start you can look for those other dependencies, like try to cache values from other objects to a List (just as an example.)

    EDIT: can you post some code snippet where and how you have that List.
     
    Last edited: Dec 5, 2019
  5. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    It looks like this is the case, so OP, show the code of class where list is defined and where you add your item.
     
  6. BenVenNL

    BenVenNL

    Joined:
    Oct 14, 2019
    Posts:
    56
    The script that is on my nodes:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. public class NodeScript : MonoBehaviour
    4. {
    5.  
    6. // stuff happening
    7.  
    8.     public void Awake()
    9.     {
    10.        // stuff happening
    11.  
    12.         // adding nodes to list
    13.         nodeLijstScript.ToevoegenAanPfNodeLijsten(dezeNode.transform.position, dezeNode, dezeNode.tag);
    14.      
    15.         // more stuff happening
    16.     }
    17.  
    18. }

    The 'nodeLijstScript' which has the method:

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4.  
    5. public class NodeLijstScript : MonoBehaviour
    6. {
    7.     public List<Vector3> pf_nodes_positie_Lijst = new List<Vector3>(); // the original List!
    8.     public List<GameObject> pf_nodes_gameobject_Lijst = new List<GameObject>(); // the original List!
    9.  
    10.    
    11.     // This checks if position already exists in List
    12.     public bool CheckOfPfNodeAlBestaat(Vector3 nodePositie)
    13.     {
    14.         if (pf_nodes_positie_Lijst.Count != 0 && pf_nodes_positie_Lijst.Contains(nodePositie)) // of de locatie al in de lijst voorkomt
    15.         { return true; }
    16.         else return false;
    17.     }
    18.    
    19.         // This adds the position and objects to the List (using the above metyhod to check for duplicates)
    20.         public void ToevoegenAanPfNodeLijsten(Vector3 nodePositie, GameObject nodeGameObject, string tag)
    21.     {
    22.         if (CheckOfPfNodeAlBestaat(nodePositie) == false && tag == "Pf_Node")
    23.         {
    24.             pf_nodes_positie_Lijst.Add(nodePositie);
    25.             pf_nodes_gameobject_Lijst.Add(nodeGameObject);
    26.         }
    27.     }
    28.  
    This is what happens when Instantiate by mouseclicking (just building a layout):
    The list contains locations and GameObjects from the white spheres (nodes). These are children the blue cube frame thing which is the prefab instantiated.



    This is what happens if I Save, restart the scene, and Load.

     
  7. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    Also post your save, load, and new-on-mouse-click code.
     
  8. BenVenNL

    BenVenNL

    Joined:
    Oct 14, 2019
    Posts:
    56
    Now reading through my post I suddenly notice it isn't the fist node at all … .
    The first prefab instantiated is at (0,0,0), the one next to the green cube, only has 3 nodes. In this case I have 5 positions in my list.

    Did some more testing.

    It seems that when I Load the objects the original positions of the nodes in the blue prefab cube-frame-thing are added to the list, not the positions of the nodes in the scene, but the relative postions to their parent.

    Why does this happen on Loading and not when maually instantiating?
     
  9. BenVenNL

    BenVenNL

    Joined:
    Oct 14, 2019
    Posts:
    56
    Allright the whole save and load thing ..

    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine.SceneManagement;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class SaveLayoutManager : MonoBehaviour
    7. {
    8.     private static SaveLayoutManager slm;
    9.  
    10.     public List<SaveableSectiesAlgemeen> SectionsSaveLijst { get; private set; }
    11.    
    12.     public static SaveLayoutManager Save_Layout_Manager
    13.     {
    14.         get
    15.         {
    16.          if (slm == null)
    17.             {
    18.                 slm = GameObject.FindObjectOfType<SaveLayoutManager>();
    19.             }
    20.             return slm;
    21.         }
    22.     }
    23.  
    24.     void Awake()
    25.     {
    26.         SectionsSaveLijst = new List<SaveableSectiesAlgemeen>();
    27.     }
    28.  
    29.     public void Save()
    30.     {
    31.         string huidigeSceneNaam = SceneManager.GetActiveScene().name;
    32.         int aantal_in_SaveLijst = SectionsSaveLijst.Count;
    33.  
    34.         // aantal objecten in savefile opslaan onder huidige scene naam
    35.         PlayerPrefs.SetInt(huidigeSceneNaam, aantal_in_SaveLijst);
    36.        
    37.         for( int i = 0; i < SectionsSaveLijst.Count; i++)
    38.         {
    39.             SectionsSaveLijst[i].Save(i); // roept de savefunctie aan welke bij het specifieke GamObject in SectiesSaveLijst hoort, zie Save() in SaveableSectiesAlgemeen
    40.         }
    41.     }
    42.  
    43.     public void Load()
    44.     {
    45.         // Eerst alle bestaande Saveables verwijderen en SaveLijst leegmaken
    46.        
    47.         foreach (SaveableSectiesAlgemeen obj in SectionsSaveLijst)
    48.         {
    49.             print("Gevonden voor Destroy: " + obj);
    50.            
    51.             if (obj != null)
    52.             {
    53.                 Destroy(obj.gameObject);
    54.             }
    55.         }
    56.         SectionsSaveLijst.Clear();
    57.  
    58.         // Alle objecten inladen uit de savefile
    59.  
    60.         int aantal_in_SaveLijst = PlayerPrefs.GetInt(SceneManager.GetActiveScene().name); // eerst totaal aantal ophalen opgeslagen onder huidige scene naam
    61.  
    62.         for (int i = 0; i < aantal_in_SaveLijst; i++)
    63.         {
    64.             string huidigeSceneNaam = SceneManager.GetActiveScene().name;
    65.             string index = i.ToString();
    66.             string kenmerk_van_save = huidigeSceneNaam + "-" + index;
    67.  
    68.  
    69.             string savestring_compleet = PlayerPrefs.GetString(kenmerk_van_save); // haalt "scenenaam-i" uit save
    70.             string[] savestring_opgeknipt = savestring_compleet.Split('#');
    71.  
    72.             GameObject objectType = null;
    73.            
    74.             switch (savestring_opgeknipt[0]) // index [0] = objecttype (Enum)
    75.             {
    76.                 case "Frame_0":
    77.                     objectType = Instantiate(Resources.Load("Frame_0") as GameObject);
    78.                     break;
    79.                 case "Frame_1":
    80.                     objectType = Instantiate(Resources.Load("Frame_1") as GameObject);
    81.                     break;
    82.                 case "Frame_2":
    83.                     objectType = Instantiate(Resources.Load("Frame_2") as GameObject);
    84.                     break;
    85.                 case "Frame_3":
    86.                     objectType = Instantiate(Resources.Load("Frame_3") as GameObject);
    87.                     break;
    88.                 case "Frame_4":
    89.                     objectType = Instantiate(Resources.Load("Frame_4") as GameObject);
    90.                     break;
    91.                 case "Frame_5":
    92.                     objectType = Instantiate(Resources.Load("Frame_5") as GameObject);
    93.                     break;
    94.                 case "Frame_6":
    95.                     objectType = Instantiate(Resources.Load("Frame_6") as GameObject);
    96.                     break;
    97.                 case "Frame_7":
    98.                     objectType = Instantiate(Resources.Load("Frame_7") as GameObject);
    99.                     break;
    100.                 case "Frame_8":
    101.                     objectType = Instantiate(Resources.Load("Frame_8") as GameObject);
    102.                     break;
    103.             }
    104.            
    105.             if(objectType!= null)
    106.             {
    107.                 objectType.GetComponent<SaveableSectiesAlgemeen>().Load(savestring_opgeknipt);
    108.             }      
    109.             else print("Enum ObjectType staat niet in beschibare lijst met Enum om te laden.");
    110.        
    111.         }
    112.     }
    113.  
    114.     public Vector3 TekstNaarVectorOmzetten(string tekst)
    115.     {   // string is bijv. (3, 6, 12)
    116.         tekst = tekst.Trim(new char[] { '(', ')' }); // haakjes om de tekst weghalen
    117.         tekst = tekst.Replace(", ", "#"); // komma-spaties er tussenuit halen en vervangen door #
    118.         string[] tekst_gesplitst = tekst.Split('#'); // opsplitsen in array van waardes, splitsen op #
    119.  
    120.         // floats maken van strings
    121.         float x = float.Parse(tekst_gesplitst[0]);
    122.         float y = float.Parse(tekst_gesplitst[1]);
    123.         float z = float.Parse(tekst_gesplitst[2]);
    124.  
    125.         return new Vector3(x, y, z);
    126.     }
    127.    
    128.     public Quaternion TekstNaarRotatie(string tekst)
    129.     {   // string is bijv. (90, 0, 0)
    130.         tekst = tekst.Trim(new char[] { '(', ')' }); // haakjes om de tekst weghalen
    131.         tekst = tekst.Replace(", ", "#");  // komma-spaties er tussenuit halen en vervangen door #
    132.         string[] tekst_gesplitst = tekst.Split('#'); // opsplitsen in array van waardes, splitsen op #
    133.  
    134.         // floats maken van strings
    135.         float x = float.Parse(tekst_gesplitst[0]);
    136.         float y = float.Parse(tekst_gesplitst[1]);
    137.         float z = float.Parse(tekst_gesplitst[2]);
    138.         float w = float.Parse(tekst_gesplitst[3]);
    139.  
    140.         return new Quaternion(x, y, z, w);
    141.     }
    142. }
    143.  
    Abstract Class for the saveable objects:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine.SceneManagement;
    4. using UnityEngine;
    5.  
    6. enum ObjectType { Frame_0, Frame_1, Frame_2, Frame_3, Frame_4, Frame_5, Frame_6, Frame_7,  Frame_8}  // selecteerbaar via inspector
    7.  
    8. public abstract class SaveableSectiesAlgemeen : MonoBehaviour // abstract, kan niet aan gameobject gekoppeld worden
    9. { // Deze class bevat alle algemene functrionaliteit voor objecten
    10.  
    11.     protected string object_specifieke_eigenschappen;
    12.  
    13.     [SerializeField]
    14.     private ObjectType objectType;
    15.  
    16.     private void Awake() // in dit geval Awake.
    17.     {
    18.         // Indien objecten bij de start van het spel al aanwezig zijn moet dit in de 'Start' worden opgenomen!!!
    19.         // De lijst wordt al onder 'Awake' aangemaakt.
    20.  
    21.         SaveLayoutManager.Save_Layout_Manager.SectionsSaveLijst.Add(this);
    22.     }
    23.  
    24.     public virtual void Save(int indexnummer) // vitual, wordt later in specifieke scrip overruled
    25.     {
    26.         string huidigeSceneNaam = SceneManager.GetActiveScene().name;
    27.         string index = indexnummer.ToString();
    28.         string kenmerk_van_save = huidigeSceneNaam + "-" + index;
    29.  
    30.         PlayerPrefs.SetString(kenmerk_van_save, objectType + "#" + transform.position.ToString() + "#" + transform.localRotation.ToString() + "#" + object_specifieke_eigenschappen);  // # is deelteken!
    31.     }
    32.  
    33.     public virtual void Load(string[] tekst_data_gesplitst) // vitual, wordt later in specifieke scrip overruled
    34.     {
    35.         transform.localPosition = SaveLayoutManager.Save_Layout_Manager.TekstNaarVectorOmzetten(tekst_data_gesplitst[1]);
    36.         transform.localRotation = SaveLayoutManager.Save_Layout_Manager.TekstNaarRotatie(tekst_data_gesplitst[2]);
    37.     }
    38.  
    39.     public void DestroySaveable()
    40.     {
    41.         SaveLayoutManager.Save_Layout_Manager.SectionsSaveLijst.Remove(this);
    42.         Destroy(gameObject);
    43.     }
    44.  
    45.  
    46. }
    47.  
    And for the saveable object specific:

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. public class SaveableSectieSpecifiek : SaveableSectiesAlgemeen // <<<< LET OP: dit script is gabaseerd op  SaveableSectieAlgemeen srcipt!!
    6. { // Deze class bevat alle specifieke functionaliteit voor dit specifieke object
    7.    
    8.     [SerializeField]
    9.     private float damage;
    10.    
    11.     [SerializeField]
    12.     private float strength;
    13.  
    14.     public override void Save(int index)
    15.     {  
    16.         object_specifieke_eigenschappen = damage.ToString() + "#" + strength.ToString();
    17.        
    18.         base.Save(index); // Haalt vervolgens de "base" Save functie uit SaveableSectiesAlgemeen script
    19.     }
    20.     public override void Load(string[] tekst_data_gesplitst)
    21.     {
    22.         damage = float.Parse(tekst_data_gesplitst[3]);
    23.         strength = float.Parse(tekst_data_gesplitst[4]);
    24.         base.Load(tekst_data_gesplitst); // Haalt vervolgens de "base" Load functie uit SaveableSectiesAlgemeen script
    25.     }
    26. }
     
  10. BenVenNL

    BenVenNL

    Joined:
    Oct 14, 2019
    Posts:
    56
    Method used for instantiating manually:

    Code (CSharp):
    1. public void CreateNewFrame(Vector3 InstLoc)
    2.     {
    3.         frame_counter = frame_counter + 1;
    4.         GameObject FrameClone = Instantiate(framesLijst[frame_index], InstLoc, Quaternion.Euler(0, frame_rotation, 0));
    5.         FrameClone.name = "Frame" + frame_counter;
    6.      }
    Just the basic thing.
     
  11. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    OK, my guess is that the value of NodeScript.nodeLijstScript is somehow incorrect on the templates you load via Resources.Load but correct on the object you are copying via framesLijst[frame_index]. (You've cut all the code showing how those variables are declared or initialized, so I can only guess.)

    Be sure you check your console for errors while you are running, and consider adding some Debug.Log calls to the Awake function so you can see what it's doing.

    I also notice that the list you showed your newly-instantiated objects adding themselves to in post #6 doesn't seem to be the same as the list you are saving and loading in SaveLayoutManager in post #9, so it's not clear how that data is coordinated between the two.



    Side note regarding this:
    Code (CSharp):
    1. switch (savestring_opgeknipt[0]) // index [0] = objecttype (Enum)
    2. {
    3.     case "Frame_0":
    4.         objectType = Instantiate(Resources.Load("Frame_0") as GameObject);
    5.         break;
    6.     case "Frame_1":
    7.         objectType = Instantiate(Resources.Load("Frame_1") as GameObject);
    8.         break;
    9.     case "Frame_2":
    10.         objectType = Instantiate(Resources.Load("Frame_2") as GameObject);
    11.         break;
    12.     case "Frame_3":
    13.         objectType = Instantiate(Resources.Load("Frame_3") as GameObject);
    14.         break;
    15.     case "Frame_4":
    16.         objectType = Instantiate(Resources.Load("Frame_4") as GameObject);
    17.         break;
    18.     case "Frame_5":
    19.         objectType = Instantiate(Resources.Load("Frame_5") as GameObject);
    20.         break;
    21.     case "Frame_6":
    22.         objectType = Instantiate(Resources.Load("Frame_6") as GameObject);
    23.         break;
    24.     case "Frame_7":
    25.         objectType = Instantiate(Resources.Load("Frame_7") as GameObject);
    26.         break;
    27.     case "Frame_8":
    28.         objectType = Instantiate(Resources.Load("Frame_8") as GameObject);
    29.         break;
    30. }
    It looks like the name you are loading matches the "case" string in all cases. If that's the intent, you could probably replace the entire switch statement with something like this:
    Code (CSharp):
    1. objectType = Instantiate(Resources.Load(savestring_opgeknipt[0]) as GameObject);
     
    BenVenNL likes this.
  12. BenVenNL

    BenVenNL

    Joined:
    Oct 14, 2019
    Posts:
    56
    I changed the 'private void Awake()' on the 'nodeScript' to 'private void update()'. BAM! Everything works, only it keeps updating every frame.

    My theory...

    On my 'SaveLayoutManager' script this is the first initialisation of a new prafab. Inside the switch:
    objectType = Instantiate(Resources.Load("Frame_0") as GameObject);


    This is when a object gets instatiated by hand:
    GameObject FrameClone = Instantiate(framesLijst[frame_index], InstLoc, Quaternion.Euler(0, frame_rotation, 0));


    Before the line of code is finished, when I intantiate by hand, there is already a position and rotation set.

    On the SaveLayoutManager, the object is loaded script and all, at that point the Awake is already triggered but the object had not gotten his position and rotation yet. So it takes the defaut postions of the nodes inside the prefab.
     
    Last edited: Dec 8, 2019
  13. BenVenNL

    BenVenNL

    Joined:
    Oct 14, 2019
    Posts:
    56
    Thnx for the advise.

    Also changed this line of code, doing the 'text to Vector' and 'text to Rotation' in one go.

    objectType = Instantiate(Resources.Load(savestring_opgeknipt[0]) as GameObject, TekstNaarVectorOmzetten(savestring_opgeknipt[1]), TekstNaarRotatieOmzetten(savestring_opgeknipt[2]));


    Now the 'Awake()' on 'nodeScript' works as expected.:)