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
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Menu Array Save State Method...

Discussion in 'Scripting' started by Elsewher3, Jun 2, 2022.

  1. Elsewher3

    Elsewher3

    Joined:
    Mar 24, 2019
    Posts:
    7
    Hello there,

    My primary focus has always been video editing and film, and as such I've never delved into Unity or any other game development before. However, I was thinking as I have for some time about something that I wish existed and as such I decided to create it. I've had time off and so I've barely slept over the last few days. I watched countless YouTube videos and pored over forum posts and managed to get the system in place for the entire project.

    TL;DR: The only problem remains that I have no clue how I should save it.

    I was able to comprehend most systems so far if I beat it into my head, sometimes even the simple concepts took three or four hours of brute force but I ended up getting it. Yesterday I spent 16 hours learning about PlayerPrefs and I still just can't seem to get it through my thick head how to attach it to my project.

    So, here's a brief overview of what that project is. I followed this guide:

    And with it I created a panel of cyclable buttons. See below:



    There are currently three of them for the proof of concept, however on this single page I intend to have 184 different cyclable images. I have it set so that clicking the right side of the image cycles it from 0-25 and clicking the left side will bring is back down to the value you wish. This is a counter, an abacus of sorts for a game. You need specific items in certain quantities. This is intended to track those quantities as you play, keep it open in a browser as you do so you don't have to write it down or memorize it. I have 1 out of five of this, four out of seven of this etc.

    As you click it applies a different lens image over the background image, tinting it from red (0) to yellow (1) to green (2) to violet (3). I have it set so that the text also changes, showing you the exact number of times you clicked and cycled forward or back, allowing you to set the value. 3/3 would read green, clicking it one more time would make it say FIN and read violet.

    Here is the code:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class Cycle : MonoBehaviour
    7. {
    8.  
    9.     public GameObject[] background;
    10.     int index;
    11.  
    12.     void Start()
    13.     {
    14.         index = 0;
    15.     }
    16.  
    17.  
    18.     void Update()
    19.     {
    20.         if(index >= 25)
    21.            index = 25 ;
    22.  
    23.         if(index < 0)
    24.            index = 0 ;
    25.  
    26.  
    27.  
    28.         if(index == 0)
    29.         {
    30.             background[0].gameObject.SetActive(true);
    31.         }
    32.  
    33.     }
    34.  
    35.     public void Next()
    36.      {
    37.         index += 1;
    38.  
    39.          for(int i = 0 ; i < background.Length; i++)
    40.          {
    41.             background[i].gameObject.SetActive(false);
    42.             background[index].gameObject.SetActive(true);
    43.          }
    44.             Debug.Log(index);
    45.      }
    46.  
    47.      public void Previous()
    48.      {
    49.           index -= 1;
    50.  
    51.         for(int i = 0 ; i < background.Length; i++)
    52.          {
    53.             background[i].gameObject.SetActive(false);
    54.             background[index].gameObject.SetActive(true);
    55.          }
    56.             Debug.Log(index);
    57.      }
    58.  
    59.  
    60. }
    And here is how I applied it:








    So in this single example, the user is able to cycle between the red lens (Element 0) to the violet lens (Element 8) and it will count up to 7/7, then complete. I need to be able to save the state of the index on each of the soon to be 184 different clickable menus so that when the user comes back the next day it will read and track the same as before so that they can tick it further as they progress.

    Here's a view of my save button (save state of indexes) and my reset button (reset all to original states).



    They're obviously not hooked up as I have no idea how to proceed.

    I assume PlayerPrefs to be an option, though I couldn't make it click how to set it to save the index state of the gameObject background. Perhaps PlayerPrefs isn't suitable for so many menus either.

    I'm sure that was long winded and tedious and I'm sure this is all very simple to the educated. I apologize for the trouble, I was really doing my best to get this project through without bothering anyone else.

    Thank you for your help!

    ~~~3lse
     
    Last edited: Jun 2, 2022
  2. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,596
    So let me try to summarize it to see if I understand it correctly.

    You have a bunch of buttons which cycle through images when clicked, and you want to be able to save the state of each of them, so that when you launch the game again, they retain their values?
     
  3. Elsewher3

    Elsewher3

    Joined:
    Mar 24, 2019
    Posts:
    7
    That is correct. And preferably the ability to reset those values with the press of the other button.
     
  4. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,596
    So what I would do, is create some kind of "CycleManager" class which can access the data of all of your Cycles. Then, when you quit the game, you can use OnApplicationQuit to save the values in a string to store your data neatly. Then, on Start(), you give each of the values their index and update them. Something like this

    Code (CSharp):
    1. public class CycleManager : MonoBehavior
    2. {
    3.     public List<Cycle> CycleList;
    4.  
    5.     void Start()
    6.     {
    7.         if (!PlayerPrefs.HasKey("CycleValues"))
    8.             return;  // do nothing if there's no save value
    9.         string[] loadedStrings = PlayerPrefs.GetString("CycleValues").Split(' ', StringSplitOptions.RemoveEmptyEntries);
    10.         for(int i=0; i<CycleList.Count; i++)
    11.             CycleList[i].index = int.Parse(loadedStrings[i]);
    12.     }
    13.  
    14.     void OnApplicationQuit()
    15.     {
    16.         string saveString = "";
    17.         foreach(Cycle c in CycleList)
    18.             saveString += c.index + " ";
    19.         PlayerPrefs.SetString("CycleValues", saveString);
    20.         PlayerPrefs.Save();
    21.     }
    22. }
    You also want to remove the code in your Start() method which sets their index to 0 in your Cycle class.
     
  5. Elsewher3

    Elsewher3

    Joined:
    Mar 24, 2019
    Posts:
    7
    Sorry it took me a minute to reply, finally got some sleep.

    Okay, this works great. I posted the code you suggested like this:

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class CycleManager : MonoBehaviour
    8. {
    9.     public List<Cycle> CycleList;
    10.     void Start()
    11.     {
    12.         if (!PlayerPrefs.HasKey("CycleValues"))
    13.             return;  // do nothing if there's no save value
    14.         string[] loadedStrings = PlayerPrefs.GetString("CycleValues").Split(' ', StringSplitOptions.RemoveEmptyEntries);
    15.         for(int i=0; i<CycleList.Count; i++)
    16.             CycleList[i].index = int.Parse(loadedStrings[i]);
    17.     }
    18.     void OnApplicationQuit()
    19.     {
    20.         string saveString = "";
    21.         foreach(Cycle c in CycleList)
    22.             saveString += c.index + " ";
    23.         PlayerPrefs.SetString("CycleValues", saveString);
    24.         PlayerPrefs.Save();
    25.     }
    26. }
    ...And got rid of the index = 0 value in Cycle.cs. Then I dragged CycleManager.cs onto my main camera, then dragged the individual Cycle objects into the list. Now when I load the game it saves the state of the buttons. PERFECT!... But.

    When you first start the application after it saves, the buttons are invisible. When I click the spot I know they should be, they cycle forward one (if I saved one on 2 it becomes 3) and become visible, and I can cycle back down one notch and it's correct. But that would mean clicking up one and down one or vice versa on 184 game objects every time you load just to make them visible.

    This is likely due to the fact that I hide each individual image in Inspector, otherwise the images all stack on top of eachother at launch. When they were all the first value of 0, the 0/? with the red lens, it would always start correctly. Now since it starts at different (saved) values, they all start invisible until clicked.

    I know I must be missing something simple.

    Thank you!
     
  6. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,596
    On the line you set the index, you can just set that specific background to be true using
    int.Parse(loadedStrings[i])
    as the index for the array.
     
  7. Elsewher3

    Elsewher3

    Joined:
    Mar 24, 2019
    Posts:
    7
    I'm sorry, I've only been doing this a couple of days. I tried plugging that in a few places unsuccessfully, I assume on my original Cycle.cs somewhere around line 27?

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class Cycle : MonoBehaviour
    8. {
    9.  
    10.     public GameObject[] background;
    11.     public int index;      
    12.  
    13.     void Update()
    14.     {
    15.         if(index >= 25)
    16.            index = 25 ;
    17.  
    18.         if(index < 0)
    19.            index = 0 ;
    20.        
    21.  
    22.  
    23.         if(index == 0)
    24.         {
    25.             background[0].gameObject.SetActive(true);
    26.         }
    27.        
    28.     }
    29.  
    30.     public void Next()
    31.      {
    32.         index += 1;
    33.    
    34.          for(int i = 0 ; i < background.Length; i++)
    35.          {
    36.             background[i].gameObject.SetActive(false);
    37.             background[index].gameObject.SetActive(true);
    38.          }
    39.             Debug.Log(index);
    40.      }
    41.    
    42.      public void Previous()
    43.      {
    44.           index -= 1;
    45.    
    46.         for(int i = 0 ; i < background.Length; i++)
    47.          {
    48.             background[i].gameObject.SetActive(false);
    49.             background[index].gameObject.SetActive(true);
    50.          }
    51.             Debug.Log(index);
    52.      }
    53.  
    54.  
    55. }
    But I didn't know how to properly designate it. My instinct is to basically copy the:

    Code (CSharp):
    1. if(index == 0)
    2.         {
    3.             background[0].gameObject.SetActive(true);
    4.         }
    and copy it to read index 1, 2, 3 etc and say background 0-25. But that seems overly complex and I'm probably wrong.

    Thank you for your patience.
     
  8. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,596
    No, inside of CycleManager, in the for loop, right when you're setting the index to something, set that background object to active.
     
  9. Elsewher3

    Elsewher3

    Joined:
    Mar 24, 2019
    Posts:
    7
    Uh. So that sort of makes sense. And I get what you're saying in a broad sense as far as the location. However in an attempt to follow your direction I did this:

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class CycleManager : MonoBehaviour
    8. {
    9.     public List<Cycle> CycleList;
    10.     void Start()
    11.     {
    12.         if (!PlayerPrefs.HasKey("CycleValues"))
    13.             return;  // do nothing if there's no save value
    14.         string[] loadedStrings = PlayerPrefs.GetString("CycleValues").Split(' ', StringSplitOptions.RemoveEmptyEntries);
    15.         for(int i=0; i<CycleList.Count; i++)
    16.             CycleList[i].index = int.Parse(loadedStrings[i])
    17.             background[i].gameObject.SetActive(true);
    18.     }
    19.     void OnApplicationQuit()
    20.     {
    21.         string saveString = "";
    22.         foreach(Cycle c in CycleList)
    23.             saveString += c.index + " ";
    24.         PlayerPrefs.SetString("CycleValues", saveString);
    25.         PlayerPrefs.Save();
    26.     }
    27. }
    ...And it's obviously very much the wrong way to go about it.

    Cheers, I await your facepalm patiently.