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

Setting selectable navigation explicitly in script - ending up with missing selectable.

Discussion in 'UGUI & TextMesh Pro' started by kenncann, Jul 16, 2016.

  1. kenncann

    kenncann

    Joined:
    Jul 16, 2015
    Posts:
    17
    I'm trying to code an inventory system which has different tabs for different item categories. I want to control the selectables on the screen via arrows and I am trying to set up the selectable navigation via code.

    My code works fine for the first screen that loads up, the navigation is exactly as it should be. But when I click on another tab which calls the function displayItemCategory, the navigation can find the objects on the screen that weren't changed (basically the tabs) but is unable to find the adjacent items and displays 'missing (selectable)' in the inspector.

    At the beginning of each call to displayItemCategory, I clear the slots array which holds the reference to each of my instantiated prefabs (the items). I think that when I change the tabs, slotsNav is still trying to reference the old objects that were in my slots list and this is why the selectable is appearing missing. I have checked if the gameobjects referenced by slotsNav.selectOnDown are active and they are not (though when everythings working, they are active) and trying to force them to be active did not work or show any change. It seems like there is some rule about referencing or using lists that I am unaware of. Can anyone tell me what I am doing wrong.

    Also the tabs are buttons that call displayItemCategory on click.

    Edit 1: I removed the code where I'm destroying the game objects. This caused the old ones to remain in the hierarchy but caused the new ones to not lose the selectables. So it seems the problem definitely has something to do with referencing the old destroyed objects.

    Edit 2: I was playing around with it a bit more. When I load the first screen, it currently displays 5 items. If I click on another tab with more items, then only the first 5 items are broken, but all items after that work fine and continue to work fine when switching back and forth between tabs. Why is this happening : /

    Edit 3: crawled around in the debugger a bit and didn't find anything that I haven't already said. The assignments in slotNav look right but the objects that are being referenced are not active.



    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine.UI;
    6. using UnityEditor;
    7.  
    8.  
    9. public class InventoryController : MonoBehaviour {
    10.  
    11.     GameObject inventoryPanel;
    12.     GameObject itemList;
    13.     GameObject inventoryDisplay;
    14.     ItemDatabase database;
    15.     GameObject eventSystem;
    16.     public GameObject inventorySlot;
    17.  
    18.     Navigation slotNav;
    19.  
    20.     PlayerData playerData;
    21.  
    22.     public List<GameObject> slots = new List<GameObject>();
    23.     public List<Item> items = new List<Item>();
    24.  
    25.     void Awake()
    26.     {
    27.         database = GetComponent<ItemDatabase>();
    28.         inventoryPanel = GameObject.Find("Inventory Panel");
    29.         itemList = inventoryPanel.transform.FindChild("Item List").gameObject;
    30.         inventoryDisplay = GameObject.Find("Inventory Panel");
    31.         eventSystem = GameObject.Find("EventSystem");
    32.  
    33.         playerData = GameObject.Find("PlayerData").GetComponent<PlayerData>();
    34.     }
    35.  
    36.     void Start()
    37.     {
    38.         displayItemCategory(0);
    39.     }
    40.  
    41.     public void displayItemCategory(int cat)
    42.     {
    43.         List<List<int>> allItems = playerData.allItems;
    44.         int[] itemCounts = playerData.itemCounts;
    45.  
    46.         items.Clear(); //clear current items list
    47.         //destroy objects in slots
    48.         for (int i = 0; i < slots.Count; i++)
    49.         {
    50.             DestroyObject(slots[i]);
    51.         }
    52.         slots.Clear();
    53.  
    54.         for (int i = 0; i < allItems[cat].Count; i++)
    55.         {
    56.             slots.Add(Instantiate(inventorySlot));
    57.             slots[i].transform.SetParent(itemList.transform, false);
    58.             slots[i].gameObject.name = i.ToString();
    59.             print("slot name: " + slots[i].name);
    60.  
    61.             Item itemToAdd = database.FetchItemByID(allItems[cat][i]);
    62.             items.Add(itemToAdd);
    63.             slots[i].transform.GetComponentInChildren<Text>().text = items[i].Name;
    64.         }
    65.  
    66.         //Explicitly set the up down navigation of the items
    67.         for (int i = 0; i < slots.Count; i++)
    68.         {
    69.             slotNav = slots[i].GetComponent<Selectable>().navigation;
    70.             //up/down navigation
    71.             if (i == 0)
    72.             {
    73.                 switch (cat)
    74.                 {
    75.                     case 0:
    76.                         slotNav.selectOnUp = GameObject.Find("Weapons").GetComponent<Button>();
    77.                         break;
    78.                     case 1:
    79.                         slotNav.selectOnUp = GameObject.Find("Armor").GetComponent<Button>();
    80.                         break;
    81.                     case 2:
    82.                         slotNav.selectOnUp = GameObject.Find("Cons").GetComponent<Button>();
    83.                         break;
    84.                     case 3:
    85.                         slotNav.selectOnUp = GameObject.Find("Misc").GetComponent<Button>();
    86.                         break;
    87.                     case 4:
    88.                         slotNav.selectOnUp = GameObject.Find("Key").GetComponent<Button>();
    89.                         break;
    90.                 }
    91.                 slotNav.selectOnDown = GameObject.Find((int.Parse(slots[i].name) + 1).ToString()).GetComponent<Selectable>();
    92.             }
    93.             else if (i == slots.Count - 1)
    94.             {
    95.                 slotNav.selectOnUp = GameObject.Find((int.Parse(slots[i].name) - 1).ToString()).GetComponent<Selectable>();
    96.                 slotNav.selectOnDown = null;
    97.             }
    98.             else
    99.             {
    100.                 slotNav.selectOnUp = GameObject.Find((int.Parse(slots[i].name) - 1).ToString()).GetComponent<Selectable>();
    101.                 slotNav.selectOnDown = GameObject.Find((int.Parse(slots[i].name) + 1).ToString()).GetComponent<Selectable>();
    102.             }
    103.  
    104.             //left/right navigation
    105.             switch (cat)
    106.             {
    107.                 case 0:
    108.                     slotNav.selectOnRight = GameObject.Find("Armor").GetComponent<Button>();
    109.                     break;
    110.                 case 1:
    111.                     slotNav.selectOnLeft = GameObject.Find("Weapons").GetComponent<Button>();
    112.                     slotNav.selectOnRight = GameObject.Find("Cons").GetComponent<Button>();
    113.                     break;
    114.                 case 2:
    115.                     slotNav.selectOnLeft = GameObject.Find("Armor").GetComponent<Button>();
    116.                     slotNav.selectOnRight = GameObject.Find("Misc").GetComponent<Button>();
    117.                     break;
    118.                 case 3:
    119.                     slotNav.selectOnLeft = GameObject.Find("Cons").GetComponent<Button>();
    120.                     slotNav.selectOnRight = GameObject.Find("Key").GetComponent<Button>();
    121.                     break;
    122.                 case 4:
    123.                     slotNav.selectOnLeft = GameObject.Find("Misc").GetComponent<Button>();
    124.                     break;
    125.             }
    126.  
    127.             //This is me testing if the selectable classes are active
    128.             if (i < slots.Count - 1)
    129.             {
    130.                 //print(GameObject.Find((int.Parse(slots[i].name) + 1).ToString()).GetComponent<Selectable>().ToString());
    131.                 //print("object destroyed? " + slotNav.selectOnDown.IsDestroyed().ToString());
    132.                 print("before: " + slotNav.selectOnDown.isActiveAndEnabled.ToString());
    133.                 slotNav.selectOnDown.enabled = true;
    134.                 print(i.ToString() + " Instance id: " + slotNav.selectOnDown.gameObject.GetInstanceID().ToString());
    135.                 print("after: " + slotNav.selectOnDown.isActiveAndEnabled.ToString());
    136.             }
    137.             slots[i].GetComponent<Selectable>().navigation = slotNav;
    138.         }
    139.  
    140.         //sets selected item to first item, will need to change this for when inventory empty (tryexcept?)
    141.         eventSystem.GetComponent<UnityEngine.EventSystems.EventSystem>().SetSelectedGameObject(slots[0]);
    142.     }
    143. }
    144.  
     
    Last edited: Jul 16, 2016
  2. kenncann

    kenncann

    Joined:
    Jul 16, 2015
    Posts:
    17
    To summarize above, it seems like I am able to create a navigation object the way I want to, but I am running into bugs when I try to set a gameobjects selectable navigation to it.

    Here is a video showing the error (it has annotations):

     
  3. kenncann

    kenncann

    Joined:
    Jul 16, 2015
    Posts:
    17
    Ok I have created a work around to this problem. I set the layout element prefabs (the items) to vertical selectables instead of trying to explicitly code it. Then I explicitly coded the button navigations so that their down selects pointed to the first item. Only one problem... I was getting the same error as before! Still can't find the items in the list and I end up with missing selectables.

    So I decided to try putting that part of my code into LateUpdate and it started working properly. I'll take it for now but this is not really ideal, I don't want to be trying to set the the navigations of the buttons every frame.

    So my question now is, are objects instantiated in a coroutine? How can I ensure that the objects are actually instantiated before trying to call the rest of my code?
     
  4. KeySam

    KeySam

    Joined:
    Jan 25, 2013
    Posts:
    8
    Hi I found a quick and simple solution setting the selectable elements explicitly in code. I dont know if this feature was added in a later version of Unity. Hope it helps someone.

    Code (CSharp):
    1. Navigation nav = Menue.navigation;
    2. nav.selectOnDown = AnySelectable;
    3. nav.selectOnUp =  AnotherSelecatble;
    4. Menue.navigation = nav;
    Not that for some reason I can not directly assign it like this:

    Code (CSharp):
    1. Menue.navigation.selectOnDown = AnySelectable;