Search Unity

Need help with Toggle

Discussion in '2D' started by TheWebExpert, Jan 9, 2017.

  1. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    This one SHOULD be simple... but I'm missing something, and I need help from the community.

    I have a list of checkboxes, generated at runtime, that varies in length. Each checkbox starts off as "Off." This checkbox is getting added to the list through a prefab. The only way I could see to link in the associated function was to make the Main Camera (where the script lives) a prefab, then drag THAT into the Inspector, and then select the proper function.(called ScreenSetup_Check). What I want to do in this function is to add 1 to a variable if a checkbox gets checked, and subtract one if one gets unchecked.

    So: 0 to start, I check one, then another, then another. That's three. If I uncheck one of those, it should now be 2.

    However:
    Here's the code I have thus far:
    Code (CSharp):
    1.         public void ScreenSetup_Check()
    2.           {
    3.             Toggle checkbox = GameObject.Find("Toggle").GetComponent<Toggle>();
    4.             if (checkbox.isOn == true) CheckedItems ++;
    5.             if (checkbox.isOn == false) CheckedItems --;
    6.             print ("Checked: "+CheckedItems);
    7.           }
    8.  
    For some unknown reason, when I run the program, and check a box, instead of it going 1..2..3.. I got 6, 7, 8 and then 9, 10, 11 and then 12, 13, 14 and so on. The increment keeps on growing, and I have no idea why, especially when I affirmatively zeroed out the variable to begin with.

    I will be using this number later on, so it needs to be right. If I have three items checked, I want this number to be 3. I am absolutely stumped by this.
     
  2. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    There are quite a few ways to do this.

    You can have a script on the parent of the Toggles, which keeps track of them as they change values.
    Example:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. public class ToggleCounter : MonoBehaviour {
    6.  
    7.     public int TogglesOn { get; private set; }
    8.     public int TogglesOff{ get; private set; }
    9.  
    10.     public int TotalToggles {
    11.         get { return checkboxes.Count; }
    12.     }
    13.  
    14.     private List<Toggle> checkboxes;
    15.  
    16.     private void Start() {
    17.         checkboxes = GetComponentsInChildren<Toggle>().ToList();
    18.  
    19.         foreach(Toggle toggle in checkboxes) {
    20.             // get an initial count of on/off
    21.             if(toggle.isOn) {
    22.                 TogglesOn++;
    23.             } else {
    24.                 TogglesOff++;
    25.             }
    26.  
    27.             // listen for changes
    28.             toggle.onValueChanged.AddListener(OnToggleValueChanged);
    29.         }
    30.     }
    31.  
    32.     private void OnToggleValueChanged(bool isOn) {
    33.         TogglesOn += isOn ? 1 : -1;
    34.         TogglesOff += !isOn ? 1 : -1;
    35.        
    36.         print("Toggles On: " + TogglesOn);
    37.         print("Toggles Off: " + TogglesOff);
    38.     }
    39. }
    You can have a script anywhere, and then drag the Toggles to a public list, which then keeps track of them. (Use the previous example, but with a public list, and don't use the GetComponentsInChildren line).

    If you want to use the Toggle's built-in callback in the inspector -- OnValueChanged (Boolean), then create a function with a boolean parameter, and have each toggle call it using the inspector interface.

    If they all start off, this should work:
    Code (CSharp):
    1. int checkboxesOn = 0;
    2. // each toggle calls this:
    3. public void CheckboxValueChanged(bool isOn) {
    4.     checkboxesOn += isOn ? 1 : -1;
    5.     print("Checkboxes on = " + checkboxesOn);
    6. }
     
  3. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Well, neither of these is working for me. :(

    I tried the lower script (by itself), because that seemed the easiest... but what I get is:
    Checkboxes on = -3
    Checkboxes on = -4
    Checkboxes on = -5
    Checkboxes on = -6
    ...
    and continuing to get larger numbers, even after the program restarts. How it can possibly decrement or increment a variable that's not active, I don't know.

    I tried the upper example, attaching the script to the prefab item's parent.

    StartItem --> script here
    Toggle --> this was dragge into the Inspector under checkboxes
    ItemText

    However, I think the problem here is that there's only ONE checkbox recognized in this "List"; I only dragged in one, because that's all I have - I'm Instantiating these at runtime, so there's only one to drag, and that's the one in the prefab. Perhaps, instead of the List<Toggle> checkboxes I should use a single Toggle item? I'm still stumped on this.
     
  4. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    In the first example, you would need to have created the checkboxes beforehand in order to manually populate the list. Otherwise, if you left it as-is, then if the checkboxes are created during Awake(), that Start() function should find them in the children.

    For the second example, if your debug statement is counting backwards, then "isOn" is always false. I'm not sure why that would be though.

    Here's another adjustment to that example, which does the spawning as well and maybe simplifies things.

    You assign the prefab to spawn, the parent they should go under, and how many to spawn:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4. public class ToggleCounter : MonoBehaviour {
    5.  
    6.     public GameObject checkboxPrefab;
    7.     public RectTransform checkboxParent;
    8.     public int numberToSpawn;
    9.  
    10.     public int TogglesOn { get; private set; }
    11.     public int TogglesOff { get; private set; }
    12.  
    13.     public int TotalToggles {
    14.         get { return checkboxes.Count; }
    15.     }
    16.  
    17.     private List<Toggle> checkboxes;
    18.  
    19.     private void Awake() {
    20.         checkboxes = new List<Toggle>();
    21.  
    22.         CreateCheckboxes(numberToSpawn);
    23.     }
    24.  
    25.     private void Start() {
    26.         foreach(Toggle toggle in checkboxes) {
    27.             // get an initial count of on/off
    28.             if(toggle.isOn) {
    29.                 TogglesOn++;
    30.             } else {
    31.                 TogglesOff++;
    32.             }
    33.  
    34.             // listen for changes
    35.             toggle.onValueChanged.AddListener(OnToggleValueChanged);
    36.         }
    37.  
    38.         print("Toggles On: " + TogglesOn);
    39.         print("Toggles Off: " + TogglesOff);
    40.     }
    41.  
    42.     private void CreateCheckboxes(int count) {
    43.         for(int i = 0; i < count; i++) {
    44.             GameObject checkbox = Instantiate(checkboxPrefab, checkboxParent);
    45.             checkboxes.Add(checkbox.GetComponentInChildren<Toggle>());
    46.         }
    47.  
    48.         print("Created " + checkboxes.Count + " checkboxes");
    49.     }
    50.  
    51.     private void OnToggleValueChanged(bool isOn) {
    52.         TogglesOn += isOn ? 1 : -1;
    53.         TogglesOff += !isOn ? 1 : -1;
    54.  
    55.         print("A checkbox has been set to " + isOn);
    56.         print("Toggles On: " + TogglesOn);
    57.         print("Toggles Off: " + TogglesOff);
    58.     }
    59. }
    Try that. However, any of these methods could work, it just requires some debugging to see where it fails with your current setup. I am hoping with these examples you can see a method that can work for you, it wasn't really meant to be a copy-paste solution as I am not certain of your project setup.
     
  5. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    I'm getting the message:

    Cannot implicitly convert type `UnityEngine.Object' to `UnityEngine.GameObject'. An explicit conversion exists (are you missing a cast?)

    from line 44 of the above code.

    I'm not sure this will help, either; but I'm trying to take it apart & see if I can adapt it. Here's what I have:

    Code (CSharp):
    1.         List<GSItem> gsList = new List<GSItem>();
    2. ...
    3.             CheckedItems = 0;
    4.             gsList.Clear();
    5.             for (x = 1; x <= DeckCount; x++)
    6.               {
    7.                 if (TheDeck[x].Done == "x" && TheDeck[x].Include == "x")
    8.                   {
    9.                     t ++;
    10.                     AddItem(TheDeck[x].Name); KL[t] = TheDeck[x].Num;
    11.                   }
    12.               }
    13.  
    14. ...
    15.  
    16.         public void AddItem(string text)
    17.           {
    18.             GSItem tempItem = null;
    19.             tempItem = Instantiate(StartItem) as GSItem;
    20.             tempItem.nameTxt.text = text;
    21.             tempItem.transform.SetParent(GS_ListContainer.transform, false);
    22.             gsList.Add(tempItem);
    23.           }
    24.  
    The original code (GSItem) is from a custom list that would have an image & text; it was the only way (that I knew of) to create such a custom list. I knew, from that example, that I could have a list of checkbox items. Now, this creates the list of checkboxes... but, as you saw, I have problems PROCESSING the checkboxes. I'm wondering if I can replace my "AddItem" with your CreateCheckboxes.
     
  6. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    You can fix that error by replacing the Instantiate line with this:
    Code (CSharp):
    1. GameObject checkbox = (GameObject)Instantiate(checkboxPrefab, checkboxParent);
    I forgot to cast from Object to GameObject. I have not tested this code, it's merely an example of how you can create/interface with the toggles.

    My code is assuming you're using the Unity UI "Toggle" component. Is that correct? Or is this your own custom checkbox?

    Pretty much what it comes down it is creating the objects, then listening for changes in the objects. Ideally that happens via the checkbox firing events when it changes, and something else listens to those events. That event already exists in the "Toggle" component, but if you have a custom class, you can add your own events.
     
    Last edited: Jan 10, 2017
  7. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    This just isn't working for me. The script will say that there are 4 checkboxes checked out of 5, but I'm not seeing them - I have no idea WHERE they are. My list is an empty GameObject with a RectTransform as Parent, then a Panel child with another Panel subchild. It is the last Panel which receives the content of the list. The GS_List Container variable points to the second subchild; I then have a reference to the Prefab StartItem.

    When the items are added to the list, it is using the prefab (which contains the checkbox + a UI text object) to do so, n times. I somehow need to add a listener at the same time.

    If you've got a method to create the actual LIST (on screen) that has the check plus a text, that would be great.
     
  8. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    Try changing the instantiate line to this:
    Code (CSharp):
    1. GameObject checkbox = (GameObject)Instantiate(checkboxPrefab, checkboxParent, false);
    I just set up a scene and tested my code, and it is working with this change. Previously all the instantiated toggles had a bizarre scaling.

    The first and second screenshots are me setting up the layout, and then I took the first Toggle and made it a prefab. I then deleted all the Toggles from the scene, and assigned the new prefab to my script along with the parent object in screenshot 3.

    I used a Vertical Layout Group component to make a list of toggles. These are the default Toggles that Unity makes for you.
     

    Attached Files:

    Last edited: Jan 10, 2017
  9. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Well, that's somewhat better; however: When I try to increase the # of checks to say, 25, it ultra compresses them
    rather than keeping the spacing the same (say, about 30px). Also, this list needs to be scrollable.... which was the purpose of the original list.
     
  10. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
  11. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Ok: That's a bunch better. Using Greg's tutorial, I've successfully created a scrolling list of checkboxes. What I'm trying to do now is to somehow have the text portion be separate from the check itself; in other words, I want to be able to select the text, just like selecting an item on a list - without turning on/off the checkbox. Can you help with this?



    This is something like what I'm looking to do. When I click the checkbox, it checks/unchecks, and the variable counter increments/decrements. The program already does this. When I click the text, I can link to a different action; I want to display the picture associated with the line item.
     
  12. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    You can basically think of each one of your list items as being comprised of two toggles. One toggle is the default checkbox toggle (which I recommend inspecting and seeing how it works the way it does thru the editor interface). The text beside it can also be a toggle.

    I believe currently the default toggle behavior is show/hide style. To mimic your image there, you would probably want to write a short script that handles what to do when it gets toggled.

    To leverage the built-in toggle behavior, I would recommend hooking into the "onValueChanged" event (this is the event you see in the inspector for the Toggle component), and doing your logic there.

    It's pretty straightforward, here's an example:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. public class SelectableTextToggle : MonoBehaviour {
    5.  
    6.     [Header("Text")]
    7.     public Text text;
    8.     public Color selectedTextColor;
    9.  
    10.     [Header("Background")]
    11.     public Image background;
    12.     public Color selectedBackgroundColor;
    13.  
    14.     private Toggle toggle;
    15.     private Color initialTextColor;
    16.     private Color initialBackgroundColor;
    17.  
    18.     private void Awake() {
    19.         toggle = GetComponent<Toggle>();
    20.         initialTextColor = text.color;
    21.         initialBackgroundColor = background.color;
    22.     }
    23.  
    24.     private void OnEnable() {
    25.         if(toggle != null) {
    26.             toggle.onValueChanged.AddListener(OnToggled);
    27.         }
    28.     }
    29.  
    30.     private void OnDisable() {
    31.         if(toggle != null) {
    32.             toggle.onValueChanged.RemoveListener(OnToggled);
    33.         }
    34.     }
    35.  
    36.     private void OnToggled(bool isOn) {
    37.         if(isOn) {
    38.             text.color = selectedTextColor;
    39.             background.color = selectedBackgroundColor;
    40.         } else {
    41.             text.color = initialTextColor;
    42.             background.color = initialBackgroundColor;
    43.         }
    44.     }
    45. }
    So you would setup the Toggle component with the background as the "Target Graphic" which will allow it to be a clickable region. You would also set "Toggle Transition" to none and remove any reference to the "Graphic" below it so that it won't show/hide anything.

    Additionally, to make only one text selected at a time, you can add a "ToggleGroup" component to the parent of the list (it can go anywhere but it makes sense to me to put it on the containing object). Then you assign each relevant Toggle component a reference to that ToggleGroup (Toggle component has a spot for this). Then when one toggle in the group is turned on, any other that is on will be turned off. The "Allow Switch Off" option on the Toggle group allows all of them to be off.
     
    Last edited: Jan 12, 2017
  13. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137

    When I click an item, it goes white on white, and I can do multiple items, which I don't want. Also, as you see, the checkboxes are invisible. They also can't be clicked. I'm not sure what I'm doing wrong.
     
  14. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    Both of your colors are set to 0 alpha (default color is RGBA 0,0,0,0 or black and invisible), so they will be invisible (probably then showing the white panel behind). Selecting multiple items is solved using the ToggleGroup as I described above. Make sure your Text component has "Raycast Target" deselected, but the background Image component does have it checked. That way you're not clicking the text, but just the background image (and that the background image is set as the Target Graphic of the Toggle component.
     
    Last edited: Jan 12, 2017
  15. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Ok, this is looking very, very good. I can only select ONE item at a time. It's the right color foreground on the right color background. I can now see the checkboxes. But... I can't CLICK the checkboxes! If I click anywhere on the line, it just highlights the line!

    I did the ToggleGroup, Raycast Target is deselected...
     
  16. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    I need to be able to check on/off multiple checkboxes, yet only select one line item. When I changed the toggle transition back, I could check the boxes again - but only one at a time, and that check ALSO highlighted the text, which I don't want.
     
  17. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    I figured a picture would be worth a thousand words. Here's what I'm trying to accomplish:
     
  18. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    From your description it sounds like you've set up each list item with a single Toggle component. What you want to do is have the checkbox be one Toggle component, and then the Text as a sibling GameObject with its own Toggle component. The checkbox can work as the default Toggle works, and the Text will have our custom script. That way the checkbox and selectable text can work independently from one another.

    I would structure it like this:

    List Parent (ScrollRect, Mask etc.)
    List Item (Horizontal Layout Group)
    Checkbox (default Toggle object, remove "Label" from the default setup)
    Checkbox background (Image)
    Check Mark (Image)​
    Text (Text component, Toggle - setup how I described earlier, custom script to handle toggling)
    Text Background (Image) - (The Toggle component could live here instead if you want)​
     
  19. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Here's what I have:

    Canvas
    ...ScrollView - ScrollRect & Mask
    ......ScrollContent - Content Size Fitter & Grid Layout Group
    .........Toggle - with a ButtonHandler script (see below)
    ............Background
    ...............Checkmark
    ............Label - with Selectable Text Toggle script
    ...............Label background

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections;
    4.  
    5.     public class ButtonHandler : MonoBehaviour
    6.       {
    7.         private string Name;
    8.         public Text ButtonText;
    9.         public MyClass myScroll;
    10.         public void SetName(string name)
    11.           {
    12.             Name = name;
    13.             ButtonText.text = name;
    14.           }
    15.         public void Button_Click()
    16.           {
    17.             Toggle toggle = this.GetComponent<Toggle>();
    18.             if(toggle.isOn)
    19.               {
    20.                 myScroll.ButtonClicked(Name, true);
    21.               }
    22.             else
    23.               {
    24.                 myScroll.ButtonClicked(Name, false);
    25.               }
    26.           }
    27.       }
    When the toggles are instantiated, this is the code that's used:
    Code (CSharp):
    1.         public void AddItem(string text)
    2.           {
    3.             GameObject go = Instantiate(Button_Template) as GameObject;
    4.             go.SetActive(true);
    5.             ButtonHandler TB = go.GetComponent<ButtonHandler>();
    6.             TB.SetName(text);
    7.             go.transform.SetParent(Button_Template.transform.parent);
    8. }
    because the Label is no longer part of the Button_Template, the toggles get created with no text label. No text label, nowhere to click. I'm now more lost than ever. I tried adding the toggle component to the Label, but that didn't help;
     
    Last edited: Jan 13, 2017
  20. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    537
    I've used the toggle myself only in singe form, If I'm wrong what I'm writing here for give me.

    What I can see from the posts is a form of radial button, although radial buttons only allow a single item on, which is why your using a toggle. I though there is a Toggle group setting I'm sure I've seen that.

    if not lets see if we can wip up the answer for you in a project.
     
  21. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    Without being able to see your project and how you're trying to set things up, it's hard to tell you what to change. I just went ahead and set this up the way I would probably do it, so check it out and let me know if you need help adapting it to your needs.
     

    Attached Files:

  22. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    I'm trying to make a scrollable list of items with checkboxes (NOT radial buttons). I want to be able to check/uncheck each item individually. I also want to be able to click on the text portion of the checkbox, so that it highlights (see above picture); in other words, let's say I had the following list:

    [X] Apple
    [X] Pear
    [ ] Banana
    [ ] Grapefruit
    [X] Orange
    [ ] Pineapple

    If I click on the word "Grapefruit," I'll link to a picture of the grapefruit... but that click won't check or uncheck the box. I've got a partially working code (it either lets me check the boxes, or else highlight the text, but not both).
     
  23. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    I downloaded your code... but there's nothing in it. All of the items in the hierarchy have no names at all. They're also missing the associated script links. I'm not sure how you coded this. Can you put everything in a .zip file instead?
     
  24. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    537
    Yeah a form of listbox checkbox, which Winforms can do easily, hmmm!

    Well create a new project and copy JUST the part for the scrollable checkbox in there and we will have a look at it.

    It will be easier that why you can post up to 4mb zip file, as @jeffreyschoch is trying his best for you but I think that might be the best way forward.
     
  25. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    That's odd, it works fine downloading it on my end. That's just a package with a scene, a prefab, and two cs files. Maybe it's a Unity version issue or something. I'll upload the ZIP.
     
  26. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    537
    its a package you have to import it into unity
     
  27. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    Here you go, try this. If the references are still broken, it should only take a second to fix.

    The spawner script goes on the ScrollView object. You give that script the prefab, the Content object, and the number to spawn.

    The SelectableTextToggle script goes on the prefab's child "Text Toggle". You configure that how you did previously with the Text and Background objects and colors. You'll have to drag out an instance of the prefab to set it up with the children of "Text Toggle".
     

    Attached Files:

    Last edited: Jan 13, 2017
  28. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    Here's the whole project in case that's still causing you problems.
     

    Attached Files:

  29. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    This is very, VERY strange. the whole project one is having the SAME problems the imported file did - no names at all. Maybe it's a version issue? I'm using v5.4.1f1...

    I tried copying the files from the ListItem zip (same problem).
     
  30. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    Yeah I'm on 5.5

    I still have 5.4 let me see if I can get it working there and I'll upload it again.
     
  31. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    Here's the package and project, hopefully that works for you.
     

    Attached Files:

  32. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Jeff, you're a genius. I'll go ahead & update my Unity. This is PRECISELY what I wanted. Well, not EXACTLY, per se, but it does exactly what I was looking for. Now the only thing that remains is to customize it. Can you tell me how to send custom text to each of these checkboxes? Or will my own checkbox code work?
     
    jeffreyschoch likes this.
  33. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    I made the following modification:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public class ListItemSpawner : MonoBehaviour {
    7.  
    8.     public RectTransform listItem;
    9.     public RectTransform contentParent;
    10.     public Text myText;
    11.  
    12.     public int count;
    13.  
    14.     // Use this for initialization
    15.     void Awake() {
    16.         for(int i = 0; i < count; i++) {
    17.             Instantiate(listItem.gameObject, contentParent, false);
    18.             if (i == 0) myText.text = "a";
    19.             if (i == 1) myText.text = "b";
    20.             if (i == 2) myText.text = "c";
    21.             if (i == 3) myText.text = "d";
    22.             if (i == 4) myText.text = "e";
    23.             if (i == 5) myText.text = "f";
    24.             if (i == 6) myText.text = "g";
    25.             if (i == 7) myText.text = "h";
    26.         }
    27.     }
    28. }
    29.  
    I had to remove ListItem as a prefab, so I could access the Label element of the Text Toggle; now, I have a list like this:
    [ ] Toggle
    [ ] a
    [ ] b
    [ ] c
    [ ] d
    [ ] e
    [ ] f
    [ ] g
    [ ] h

    ... how can I get rid of that 1st one?? I'm assuming I can latch into the Text Toggle's onclick event to do further code
     
  34. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    A better way to handle this would be to expose a list of names for the toggles.

    You don't want to get rid of the prefab. Dynamically spawning objects are best spawned from a prefab. In order to get access to the Label element, let the prefab expose that field to the outside world.

    To do that, you can give the prefab a ListItem script that has references to it's sub objects, and has functions to configure them. Then you can spawn one, get the ListItem script, and call "newListItem.SetName(namesList)" or something.

    Here's an example of that in action.
     

    Attached Files:

    Last edited: Jan 13, 2017
  35. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Perfect. I think I can integrate this into my program. I'll let you know how it goes! :)
     
  36. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Ok, it's kind of a one step forward, two steps back kind of thing. I have the code integrated into my program now, as follows:

    Code (CSharp):
    1.  
    2.         public void AddItem(string text)
    3.           {
    4.             GameObject go = (GameObject)Instantiate(listItemPrefab, contentParent, false);
    5.             ListItem item = go.GetComponent<ListItem>();
    6.             item.SetName(text);
    7.           }
    8.         public void ButtonClicked(bool isOn)
    9.           {
    10.             CheckedItems += isOn ? 1 : -1;
    11.             print("checked: " + CheckedItems);
    12.           }
    The ButtonClicked is attached to the Checkbox in the Prefab. But once again, I get negative numbers that continue to increase (-5, -6, -7...) even after I've stopped & restarted the program. I *STILL* don't understand how this is possible.

    I also am trying to add a function to the OnValueChanged of the Text toggle. I'm not sure how to get the value of the text you clicked on.
     
  37. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Ok, I got the value of the text I clicked on using

    Code (CSharp):
    1. EventSystem.current.currentSelectedGameObject.GetComponentInChildren<Text>().text
    ... but I still have to fix the backwards numbers! It should start at zero, then count up for every one clicked (all the checks are starting off).
     
  38. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Another problem. I have a variable public Sprite[] CardPics; I've filled it in the inspector with dozens of sprites. However, when I go to access it CardPic.sprite = CardPics[n]; it tells me that the array index is out of range. What gives? Do I need to do something like this: public Sprite[] CardPics = {x, x, x, x}; to force the size of the array? How would I do that with sprites? If there's something else I'm missing, I don't know what it is.
     
  39. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Ok, I solved the second problem. You can have a script attached to a GameObject, and this will work fine (if you're looking to have a function on a Prefab object, the GameObject needs to be a Prefab as well). If the script is on the Main Camera, it won't work. Go figure.
     
  40. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    This is my #1 problem:

    Code (CSharp):
    1.  
    2.         public void AddItem(string text)
    3.           {
    4.             GameObject go = (GameObject)Instantiate(listItemPrefab, contentParent, false);
    5.             ListItem item = go.GetComponent<ListItem>();
    6.             item.SetName(text);
    7.           }
    8.         public void ButtonClicked(bool isOn)
    9.           {
    10.             CheckedItems += isOn ? 1 : -1;
    11.             print("checked: " + CheckedItems);
    12.           }
    The ButtonClicked is attached to the Checkbox in the Prefab. But once again, I get negative numbers that continue to increase (-5, -6, -7...) even after I've stopped & restarted the program. I *STILL* don't understand how this is possible. Perhaps some kind of listener when the objects are instantiated? Why isn't CheckedItems being reset to 0, when I explicitly do so? I am stumped, stumped, STUMPED!
     
  41. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    So you're seeing those numbers counting backwards when it prints to the console, as you check each box, or all at once?

    Print out the value of "isOn" each time "ButtonClicked" is called. Make all the toggles have the correct value for "isOn" boolean to start. If they should start off, make sure that is unchecked. \

    Also, how does "ButtonClicked" get called?

    It's really a matter of verifying all the inputs/outputs and tracking down where the incorrect data is coming from.

    Also a tip with the Sprites thing, save yourself the trouble and just add one sprite and get that to work perfectly before adding dozens into the mix.
     
  42. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    I have the code printing as I check each box:

    Code (CSharp):
    1.         public void ButtonClicked(bool isOn)
    2.           {
    3.             CheckedItems += isOn ? 1 : -1;
    4.             print("checked: " + CheckedItems);
    5.           }
    This code is attached to the On Value Changed for the Checkbox item from the ListItem prefab. "IsOn" is unchecked in the prefab. The Text Toggle item has the Selectable Text Toggle script attached to it. Both of these items get their script refrence from a GameObject I've called GameHandler (to which is attached the main script, where the functions all live).

    The CheckedItems is an int, which is zeroed out long before the ButtonClicked can be called; there really shouldn't be any way for the program to continue counting on a variable once the program is stopped, but I got -1, -2, -3 on the first run, then -4, -5, -6, -7 on the second. Also, the number increased whether I was clicking a checkbox on or off.
     
  43. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    Make sure that you're using the correct function when assigning the OnValueChanged callback. There are likely two version of ButtonClicked, one that shows a checkbox and one that doesn't. You want the one that doesn't. If you use the one with the checkbox and leave it unchecked, it will always pass false.

    I dont know why you would be seeing an accumulation between runs. Try printing out "CheckedItems" on Awake to verify what's happening.
     
  44. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    CheckedItems is 0 on Awake. However, you STILL get the accumulation upon running the ButtonClicked function. If I check the checkbox in the Inspector under the function call, the values go from 0 -> ... 1000 (or whatever), rather than negative. However, they still accumulate.

    I'm not sure what you mean by "one that shows a checkbox". There's only ONE ButtonClicked function among all of my scripts. If you're talking about the checkbox in the Inspector, I'd guess that this HAS to have this - otherwise, how would we know if the IsOn was on?
     
  45. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    In the Unity Inspector, in the list of functions for your class where you selected ButtonClicked, there will usually be a second entry also named ButtonClicked, at the top under a section that says "Dynamic Bool".

    The one you chose allows you to manually set what value is passed to the function. The other version lets the Toggle component send the current value of "isOn", which changes as you toggle it, and is what you want.

    Untitled.png
     
    Last edited: Jan 17, 2017
  46. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    Ok, that seems a BIT better... but it's STILL persisting the data past program close. It's still accumulating.
     
    Last edited: Jan 17, 2017
  47. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    That is really bizarre. You say that CheckedItems is 0 on Awake. What value is printed the first time you check a toggle?

    How are you declaring the variable CheckedItems? When you say "program close" what do you mean exactly, restarting the scene? stopping the game and playing again? Closing the Unity Editor?

    Are you using any PlayerPrefs or ScriptableObjects? Its possible you're setting the value to an asset in your project files which will persist between sessions.
     
  48. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    I pushed the Play button to run the code. The CheckedItems is 0 on Awake. When I first click a toggle, I get 5, 6, 7. Then I stopped the program running (pushed the Play button again). I ran it again, and got 8, 9, 10.

    int CheckedItems = 0;

    The GameHandler object (which has the code on it) is ALSO a prefab; otherwise, I wouldn't be able to load it into the prefab for the checkbox item. I thought maybe that made a difference, but I can't access the script without it.

    AFAIK, there aren't any PlayerPrefs or ScriptableObjects. I'm not certain what those are. I also don't know how I'd be setting the value to persist between sessions; this is something I don't know how to do. I can't even persist a value between scenes (unless I save it to the disk & reload it).
     
    Last edited: Jan 18, 2017
  49. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,558
    The only thing I can think is that you're setting the value directly on the prefab, not on an instance of the prefab. You say your GameHandler object is a prefab, are you creating an instance of it at the start? Otherwise you'll be editing your prefab at runtime, which would explain the accumulated values. Try using Instantiate to create a new instance of your prefab, and then use that instance instead of the prefab. Prefabs in general should be treated like a blueprint to make copies of.

    When you drag a prefab into the scene, that creates a new instance of it. You could use GameObject.Find to find the GameHandler in the scene at startup, or FindObjectOfType<GameHandler>() instead of assigning a reference to the prefab.

    If there's only ever one instance of GameHandler, you could implement a Singleton pattern which would give you a static reference to GameHandler from anywhere.

    The singleton pattern would allow you to do something like "GameHandler.Instance.someFunction()" from any class.
     
  50. TheWebExpert

    TheWebExpert

    Joined:
    Apr 14, 2015
    Posts:
    137
    I don't understand at all. The only way to access the script that's on GameHandler is to make GameHandler a prefab; you can't drag the GameObject into the prefab otherwise. I'm Instantiating the ListItem prefab objects, which contain the checkboxes. I don't instantiate the GameHandler; how or why would I do that?

    I'm not dragging prefabs into the scene - I'm using Instantiate. Finding the GameHandler wouldn't help, I don't think, because there needs to be SOMETHING on the OnValueChanged of the checkbox - hence, GameHandler is a prefab also, so I can drag it into the Inspector, so I can access the function for the OnValueChanged.

    I'm missing SOMETHING, but I don't know what.