Forum rules - please read before posting.

NullReferenceException when trying to use AC in a script

edited November 2016 in Technical Q&A
Hey again :)

I'm slowly getting into learning how to use AC within scripts, but now i have encountered an error that doesn't really make sense to me. I use the following script:









public class WhereCanWeGo : MonoBehaviour {



    AC.MenuElement XXXMarker = AC.PlayerMenus.GetElementWithName("NotebookMap","GoToXXX");



    void OnEnable () {



        if (AC.GlobalVariables.GetBooleanValue(0) == true){

            XXXMarker.isVisible = true;

            AC.KickStarter.playerMenus.RecalculateAll ();

    }

        else if (AC.GlobalVariables.GetBooleanValue(0) == false){

            XXXMarker.isVisible = false;

            AC.KickStarter.playerMenus.RecalculateAll ();

        }

    }

}


Unity is now giving me an error:
"NullReferenceException: Object reference not set to an instance of an object WhereCanWeGo.OnEnable () (at Assets/LB1.1/Scripts/WhereCanWeGo.cs:16)".

Same error happens for line 12, so always for the "isVisible" lines.
«13

Comments

  • edited November 2016
    Wait, where are you initializing XXXMarker?? I don't think you can call functions for variable initialization at the class level (you could create a property to do it in get/set though)...

    Anyway, I notice a few possible causes here. One is where, and hence, when, you are initializing the menu element. Try moving all your code to Start()- try to avoid OnEnable() and Awake() when you are first initializing variables from AC stuff, because if for some reason your code runs before AC finishes initializing itself you are always going to get null reference exceptions (because OnEnable() and Awake() are first to run in the game loop - and AC probably initializes in one of those). You simply can't get data which is not yet there. If you need to re-initialize the vars in OnEnable just make sure it happens because the object was previously disabled (just use a switch, a boolean var which gets false OnDisable and true OnEnable).

    The other possibility which could be the culprit, which is kinda similar though, is that if the specific menu owning the element is not present when you call the function then you'll probably get a null too... So double check if the menu name is correct, and make sure it's present in the scene (it'll show up under the UI folder during playmode).

    PS: mind you, unity also has a feature to define the Script initialization order, but the thought of putting tons of scripts one by one in the menu they provide, then set the delay for each, makes me shudder...
  • Alright, but when i move everything into Start(), it only checks the variables at start of the game, but i need to have the check happen everytime i open the map UI in this case, since they become available over the course of the game


  • edited November 2016
    As I said, just use a switch, a boolean var which gets changed on OnDisable and OnEnable. On OnEnable put an if conditional checking if the switch variable is the correct value, if it is, you know the object was disabled before. Once you know that, you can reinitialize your stuff inside, then you can reset your switch. Sort of like this:

    bool _wasDisabled; // as you probably know bools default to "false"

    OnEnable()
    {

    if(_wasDisabled)
    {
    //reinit variables
    }
    _wasDisabled = false;

    }

    OnDisable()
    {
    _wasDisabled = true;

    }

    That'll make sure that the first initialization only happens on Start(), then if the object gets disabled the vars will be reinitialized when the object is enabled again. This, of course, is only important if the object with your script is already enabled when the scene/level first loads (cause the script will initialize at the same time as AC). If your object gets created or is only enabled after the scene has finished loading then it should be safe to initialize on Awake() or OnEnable(). If you are using UGUI for your menus and your menu only shows up on a key press or when clicking a UI button, you could always just attach the script to the canvas and you can skip the whole issue (because, if I remember well, UGUI menus not visible are kept disabled until used, so they don't initialize when the level first loads).
  • If you're using the latest version of AC, you can use custom events to run code whenever a Menu is open.  The OnMenuTurnOn event is what you want:

    private void OnEnable ()
    {
        EventManager.OnMenuTurnOn += OnMenuTurnOn;
    }


    private void OnDisable ()
    {
        EventManager.OnMenuTurnOn -= OnMenuTurnOn;
    }


    private void OnMenuTurnOn (AC.Menu _menu, bool isInstant)
    {
        Debug.Log ("The Menu " + _menu.title + " was turned on.");
    }

  • edited November 2016
    That's right :-?, I was forgetting about custom events. This really is a perfect chance for subscribing to an event. I do tend to have many situations where they're not enough, though... but mind you I'm making hybrids or games straying from the regular adventure game style... And by the way, It's cool to see the list has gotten longer too, there's a few I didn't remember (either that or it's been too long since the last time I checked the list, lol). 
  • Hmmm, okay, so at least i got rid of the errors in both my scripts! Hooray! :)
    However i have one more question.. When i post that code into my script, it does not to anything, not even the debug message shows up.. Do i have to tell the script which AC menu we turn on/off? And if so, how would i do that? I have tried changing "_menu" to the actual menu name, but that did not work
  • The script doesn't turn on Menus on, it merely reacts when one is turned on through its natural course.  Is that script on a GameObject in your AC scene, and do you have an Event Manager component on your GameEngine prefab?
  • The script is attached to the Unity UI button inside the prefab of that UI and the event manager is on the GameEngine, yes
  • Try placing it on an empty GameObject in your scene instead.
  • edited November 2016
    Placing it on an empty object works, and shows me whenever any menu opens/closes.
    I have also tried placing it into the UI itself, not the button, but nothing happens.
  • That's because the UI is instantiated at runtime.  You may get it working if you rename "OnEnable" to "OnAwake", but otherwise you'll have to place it on an empty GameObject in every scene.
  • I tried changing it to OnAwake, but it did nothing. I have now basically split the button script from the other one and put the OnMenuTurnOn onto an empty object in the scene. However now it does not like it when i tell it to set the label to a specific text..

    "NullReferenceException: Object reference not set to an instance of an object"

    which does

    "GridName = GameObject.Find ("CNV_LB1_UI_NB_Dossier/GRD_LB1_UI_NB_Dossier_PortraitGrid/BTN_LB1_UI_NB_Dossier_PortraitGrid_5/LBL_LB1_UI_NB_Dossier_PortraitGrid_5").GetComponent<Text> ();"

    I have also noticed that, when i remove that line, the debug text does indeed show, but the script seems to be running even after i closed the UI, which is sub-optimal, since we do not need to have it running all the time during gameplay, but only when we really need it (i.e. when we open the UI)

  • edited November 2016
    Ummm... are you sure it's OnAwake? As far as I know the Unity event should be just Awake()... maybe Chris was sleepy or mistyped (or was referring to an AC event I've never heard of?) Anyways, I think you should really check the unity game loop and order of execution for unity events. You won't be able to code in Unity properly if you don't at least skim it. If the OnAwake() event doesn't exist all you did was create a function named OnAwake, which never gets properly called...

    Then about the script continuing to work, what's your reason to say that? are there debug lines or something? As far as I can tell, none of those events are loops, so they are only triggered once (when their event is triggered). The only exception could be the subscribed function you added which should run every time AC fires the OnMenuTurnOn event. In other words, every time *any* menu opens, the event won't make a distinction between the different menus you have. The only thing you can do if you want the main body of your code to run just for a specific menu is to use an if statement to verify the name of the menu before proceeding (you can get the name or the title similar to how Chris did to show the menu title). 

    I was reluctant to tell you to use event subscription because it can be confusing unless properly explained... and you really should read a bit about events and delegates... but anyway, to put it simply you can create variables which can hold functions (even multiple functions), that way you can fire these all at the same time at some point during your scripts. This makes it easy to add functionality to your script cause you can just make an extra function anywhere and then append it inside that variable/event and they all fire together at that point of your script. There are a few restrictions though, when you create an event you can define the parameters it'll have and they always have to be that way in the new functions you create. That explains the OnMenuTurnOn (AC.Menu _menu, bool isInstant)those parameters are basically been taken care of by Chris' script so you'll always receive data even though you don't see how, and that can be confusing if you have never used events (or have never read about abstraction or virtual functions, etc).

    Anyway, all unity events work on a similar idea, they just don't usually give you parameters (although some do, like OnSceneLoaded). OnEnable, OnDisable, Awake, Start, Update, FixedUpdate, etc are just events, the code gets appended when they're detected on a Monobehaviour script, then the code is fired at the right moment.
  • Doy, yes - Awake, not OnAwake.  Thanks @Alverik
  • edited November 2016
    Alright, i have now attached the script on an gameobject, and it looks like this:








           
    bool WeMetKeys = AC.GlobalVariables.GetBooleanValue (1);

    if (WeMetKeys == true) {
    Debug.Log ("This works, but the next function does not.");
    KeysGridName.text = "Keys";

    }

    Those are within the OnMenuTurnOn, and the debug message shows, however it does not execute any other things (Like setting the text to "Keys"). The text object itself i have set in the editor (it's a public), however even when it was set to private and i told it where exactly the label is located it did not go past the Debug message..

    Error message is still roughly the same:
    NullReferenceException: Object reference not set to an instance of an object
    WeMetKeysNow.OnMenuTurnOn (AC.Menu _menu, Boolean isInstant) (at Assets/WeMetKeysNow.cs:32)

    Line 32 is the keys.text one

  • And KeysGridName is assigned while the game is running?  What's it assigned to exactly?

    If you're getting the Debug log, the AC side of your script is working - the rest should be down to general coding.
  • edited November 2016
    The object is assigned to a Unity UI text label in the Editor, and when it was set to "private", this is what it looked like:

    KeysGridName = GameObject.Find ("CNV_LB1_UI_NB_Dossier/GRD_LB1_UI_NB_Dossier_PortraitGrid/BTN_LB1_UI_NB_Dossier_PortraitGrid_5/LBL_LB1_UI_NB_Dossier_PortraitGrid_5").GetComponent<Text> ();

    And that caused that same error, too.
  • edited November 2016
    quote:The text object itself i have set in the editor (it's a public), however even when it was set to private and i told it where exactly the label is located it did not go past the Debug message.

    ummm, then this may have to do with the way you are getting the reference. Are you sure that's the correct name of the menu element?? by the dashes I assume you are looking inside an object in the hierarchy... When the variable was public were you getting the reference from the project window or from the hierarchy? How have you setup your menu in the AC menu manager? is it a prefab? menu in scene? Or are you skipping the menu manager completely for this UGUI menu? The way the menu is called into action will affect the way you are to get the reference, specially because menus from prefabs will be instantiated by AC, so you have to make sure you don't place the menus manually in the scene, cause it will create doubles (the manually placed menus will not work in that case).

    Also, unfortunately I can't recall the script you shared and you never clarified exactly what you were trying to achieve, but, if what you want is to populate the same menu elements with different data it's a lot easier to go into the menu manager and link the labels in a menu to AC Global variables (you'll find an option, I think in the label type or something). That way all your script or custom action has to do is change the contents of the AC Global variables and the menu will refresh to the new data automatically (and that's a relatively painless bit of coding), getting references to the labels and such then becomes totally unnecessary, because AC will do it automatically on initialization.
  • I can't simply link an AC var to the UI, since i also need to change graphics, and i can only do that via script.

    AC Simply calls the menu up prefab currently, everything else is done via scripts.

    All the scirpt is supposed to is check an AC var (WeMetKeys) to check if we did or did not meet him, and if we did, switch out a button's images and set a text label from "Unknown" to "Keys".
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Welcome to the official forum for Adventure Creator.