Search Unity

Toggling Objects with UI

Discussion in 'Scripting' started by skantron, Jan 29, 2019.

  1. skantron

    skantron

    Joined:
    May 5, 2011
    Posts:
    35
    I am just starting some do some scripting in Unity, and don't really know much of anything yet, so any and all help and advice will be appreciated. I am trying to use a UI to turn parts of a mesh on and off, (some are simple on or off, some are one turns on while another part turns off). I made a script that triggers on a button click, and it will turn the affected object off, but not turn it on again, and I get the error:


    NullReferenceException: Object reference not set to an instance of an object
    SwitchState.Togglestate () (at Assets/Scripts/SwitchState.cs:14)
    UnityEngine.Events.InvokableCall.Invoke () (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:166)
    UnityEngine.Events.UnityEvent.Invoke () (at C:/buildslave/unity/build/Runtime/Export/UnityEvent_0.cs:58)
    UnityEngine.UI.Button.Press () (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:36)
    UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:45)
    UnityEngine.EventSystems.ExecuteEvents.Execute (IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:50)
    UnityEngine.EventSystems.ExecuteEvents.Execute[IPointerClickHandler] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:261)
    UnityEngine.EventSystems.EventSystem:Update()

    So I am missing something but not sure what exactly.

    The object in the scene has a prefab with the script attached, and the mesh is inside the prefab and has three parts (switch_low_height, SwitchOpen, SwitchClosed), The only other thing of note I can think of is the UI is in worldspace, and I have it included in the prefab.

    Prefab (with script component)
    -FBX
    --Main Model
    --Switch On
    --Switch Off
    -UI Canvas

    Code (CSharp):
    1.  using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class SwitchState : MonoBehaviour
    5. {
    6.     private bool toggle;
    7.     private GameObject SWopen;
    8.     public void Togglestate()
    9.     {
    10.         SWopen = GameObject.Find("SwitchOpen");
    11.         SWopen.SetActive(false);
    12.         if (!toggle)
    13.         {
    14.             toggle = true;
    15.             SWopen.SetActive(true);
    16.         }
    17.         else
    18.         {
    19.             toggle = false;
    20.             SWopen.SetActive(false);
    21.         }
    22.     }
    23. }
    Any help is appreciated.
    Thanks
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    GameObject.Find() doesn't return inactive objects, so after you toggle it off the first time, it'll be null the second time.

    Store a reference instead of "Find"ing it every time.
     
    Kiwasi and skantron like this.
  3. skantron

    skantron

    Joined:
    May 5, 2011
    Posts:
    35
    Thanks, that makes sense. I'll look into making a reference.
     
  4. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    Also, you should get rid of line 11 in the code sample you posted.

    I've had problems in the past when I tried to do SetActive(true) and SetActive(false) on the same GameObject in a single frame. I expected the last call to win, but it doesn't appear that's necessarily true. And in this case, you can be sure the initial call is unnecessary--all possible code paths after it call SetActive() anyway--so removing it should be harmless at worst, and might save you headaches later.
     
    Kiwasi likes this.
  5. skantron

    skantron

    Joined:
    May 5, 2011
    Posts:
    35
    So I have just been using the GetComponent<Renderer>(); and that has worked for me rather than SetActive. and that works, but I am wondering if there is a easier way to do this, rather than trying to find each object name and making the script know about it, just stick a script on each object and then connect it to the UI respective UI button (I have maybe three hundred unique objects that need to toggle visiblity based on UI buttons. Can I make a generic script that can be attached to the mesh and turn the rendering on and off?

    Thanks.
     
  6. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    There's definitely ways to automate or simplify this, but could you elaborate on your hierarchy structure? It's hard to imagine what buttons link to what scripts and whatnot without it.
     
    SparrowGS likes this.
  7. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    If you've successfully written some code that can turn rendering on and off, then you should absolutely be able to put that code into a MonoBehaviour and attach it to your individual objects.

    The key question is probably: how does the code know when to turn rendering on and off? There are many possible answers to that, depending on how your game is organized and what you are trying to accomplish.
     
  8. skantron

    skantron

    Joined:
    May 5, 2011
    Posts:
    35
    Hi thanks for continuing on this with me. I appreciate it.
    So, what I have is a FBX, it has a bunch of parts named appropriately within itself. I have a series of UI panels that you approach, and there are buttons you press to toggle the various mesh parts on and off (if it matters the UI panels are in world space). So at this point, I have, the FBX, with a flat hierarchy with the parts that turn off and on. I also have several UI panels with the buttons on them. Each of these panels control a specific group of the FBX parts. I have this script on each part in the FBX:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class ToggleVis : MonoBehaviour
    6. {
    7.     public bool toggle;
    8.     private Renderer rend;
    9.  
    10.  
    11.     // Use this for initialization
    12.     public void togglewire ()
    13.     {
    14.         print(gameObject.name);
    15.         rend = gameObject.GetComponent<Renderer>();
    16.         rend.enabled = true;
    17.         if (!toggle)
    18.         {
    19.             toggle = true;
    20.             rend.enabled = (true);
    21.             print("turn On");
    22.         }
    23.         else
    24.         {
    25.             toggle = false;
    26.             rend.enabled = (false);
    27.             print("turn off");
    28.         }
    29.  
    30.     }
    31. }
    32.  
    And then the UI button OnClick() is calling the script.
    It's actually working OK now, with a couple of issues. Some objects are on by default, and some are off be default. The ones that are off default turn on on first click, the ones that are off by default have to be clicked three times to turn off. I am wondering if I need to do something with not having a Start() and Update() structure?

    Also, my debug commands seem to stop working after a couple clicks as well.

    I will also have to get the UI buttons to reflect the state they are in (changing color or swapping a texture on another button element) and I also want to get a 'reset' button to put each UI panel back to the initial state. But for now, I just want to get the button clicks to work like I want.

    Thanks, I am just muddling my way through this, but hoping to get a bit more understanding as I go along.
     
  9. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    There is a built-in Unity UI object called a Toggle that is designed for making checkbox buttons and similar stuff. It may or may not be useful for you.

    Just like before, I recommend you delete the line that always sets "enabled = true" right before you decide whether you want it to be true or false. That line is doing nothing at best and might conceivably cause problems.

    The most likely reason for you needing to click multiple times in order to make something change is if your "toggle" variable is out of sync with the actual renderer, causing you to e.g. try to turn it off when it's already off. You could consider getting rid of the "toggle" variable entirely and just setting

    rend.enblaed = !rend.enabled;
     
    skantron likes this.
  10. skantron

    skantron

    Joined:
    May 5, 2011
    Posts:
    35
    Great thank you! MUCH cleaner. I changed the code and it seems to be working much better on first toggle. I had used another example code and it had the toggle variable.

    I now have this
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class ToggleVis : MonoBehaviour
    6. {
    7.     private Renderer rend;
    8.     // Use this for initialization
    9.     public void togglewire ()
    10.     {
    11.  
    12.         print(gameObject.name);
    13.         rend = gameObject.GetComponent<Renderer>();
    14.         rend.enabled = !rend.enabled;
    15.         print("Toggled");
    16.     }
    17. }
    But I do get an odd error message when the script if first compiled
    "Unable to parse file Assets/Scripts/ToggleVis.cs.meta: [mapping values are not allowed in this context] at line 4"
    but it goes away and does not affect the script otherwise.

    And the print debugs also only work once. Not a huge deal, but I was wondering if that is something that I can change so that the debugs work?
     
  11. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    Are you sure you don't have the console set to collapse so it stacks the print message? If togglewire is being called correctly, your prints are going to work.
     
    skantron likes this.
  12. skantron

    skantron

    Joined:
    May 5, 2011
    Posts:
    35
    Yes, that was it! thanks, debugs are debuggin'!
     
  13. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,836
    If the error complaints about the .meta file, then it's probably not related to your source code at all. Unity uses *.meta files to keep track of your assets, so it's probably some kind of asset-management issue. I've never seen that particular error before, though.