Private List<> becomes null

I am having an issue with the _rewards list throwing a null reference exception. The debug statement in ShowRewards() displays the count, however, the debug statement in ClaimRewards() throws the null reference exception. The really weird part is that if I change the debug statement in ClaimRewards() to be Debug.Log("Rewards - " + _rewards == null) is displays “Rewards - false”. If the list is not null, wouldn’t count return 0. Regardless, I don’t know why the list is changing.

    public class PlayerRewardCanvas : MonoBehaviour
    {
        private PlayerObject _playerObject;
        private HUD _hUD;
        private List<Item> _rewards;

        [Header("Modal Settings")]
        [SerializeField]
        TextMeshProUGUI title;
        [SerializeField]
        Button claimButton;
        [SerializeField]
        TextMeshProUGUI claimButtonLabel;
        [SerializeField]
        RewardContentController _rewardContentController;

        private void Start()
        {
            title.text = K.Modal.AdvertReward.TITLE;
            claimButtonLabel.text = K.ButtonText.CLAIM;
        }

        public void ShowReward(PlayerObject playerObject, HUD hUD)
        {
            _playerObject = playerObject;
            _hUD = hUD;
            _rewards = RewardManager.Instance.GetRewards().ToList();
            Debug.Log("Rewards- " + _rewards.Count);
            _rewardContentController.ShowRewards(_rewards, _playerObject);
        }

        public void ClaimRewards()
        {
            Debug.Log("Rewards- " + _rewards.Count);
            foreach (Item item in _rewards)
            {
                Debug.Log("Claim item- " + item.GetName());
                if(item is Ammo)
                {
                    _playerObject.GetPlayer().AddAmmo(item as Ammo);
                }
                else if (item is Token)
                {
                    _playerObject.GetPlayer().AddToken(item as Token);
                }
                else if (item is Currency)
                {
                    _playerObject.GetPlayer().AddCurrency(item.GetQuantity());
                }
            }
            _hUD.GetHeader().UpdateValues(_playerObject.GetPlayer());
            _hUD.ClosePlayerRewardScreen();
        }
    }

Hey and welcome!

You seem to be instantiating the list in ShowReward. Are you sure ClaimRewards is being called after that? How do you even differentiate between the Debug.Log messages. They are both the same.
Also, why dont you instantiate the rewards list in Awake() or Start()?
That would make sure it’s never null in your later method calls (unless of course you reset it yourself).

Thanks for the reply. To answer some of your questions.

Show Reward is being called by a button click in the Headsup Display that sets the Canvas active and then calls ShowReward()

ClaimReward is being call by a button click on the claimButton.

I think that answers your question regarding knowing which debug is which, plus the stack trace shows the null reference exception is coming from claim reward.

I tried both of your suggestions, neither of which changed the outcome.

Seriously, do NOT get hung up on nullref errors. This is something you will encounter every single day when writing software.

Some notes on how to fix a NullReferenceException in Unity3D (also Unassigned Reference errors):

http://plbm.com/?p=221

Push through it. Add more Debug code, check if you maybe have two versions of the script in your scene, attach the debugger, just push through it. Don’t waste time agonizing about it.

I’m reasonably sure it’s not possible that “_rewards.Count” throws a NullReferenceException and also “_rewards == null” returns false at the same time, which suggests there is something screwy with your tests (either there is some important detail to the code that you haven’t explained, or you failed to replicate the same conditions between the two tests, or you have somehow misunderstood the results).

The obvious danger here is that _rewards is only initialized in ShowReward(), and nothing in the code you posted structurally prevents ClaimRewards() from being invoked before that, in which case it is only logical that _rewards would be null. If each of those are called by different buttons, then you could get different results in different tests depending on the order you pushed those buttons during that particular test. (Because of issues like that, this is probably not the ideal way to structure your code.)

Of course, if you are not reading the console messages carefully (you haven’t posted the stack trace), it also could be the case that the exception is being thrown from somewhere later in ClaimRewards() and has nothing directly to do with the _rewards variable.

1 Like

Can you copy and paste the full and exact error message you are getting? Can you show your code that is calling ShowReward? How are you getting your reference to the PlayerRewardCanvas? How many copies of that script are there in your scene?

Stack Trace ----Line 42 is Line 34 in snippet above

NullReferenceException: Object reference not set to an instance of an object
LAG.PlayerRewardCanvas.ClaimRewards () (at Assets/Scripts/UI/Canvases/PlayerRewardCanvas.cs:42)
UnityEngine.Events.InvokableCall.Invoke () (at /Users/builduser/buildslave/unity/build/Runtime/Export/UnityEvent.cs:166)
UnityEngine.Events.UnityEvent.Invoke () (at /Users/builduser/buildslave/unity/build/Runtime/Export/UnityEvent_0.cs:58)
UnityEngine.UI.Button.Press () (at /Users/builduser/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:66)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at /Users/builduser/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:108)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at /Users/builduser/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:50)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at /Users/builduser/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:261)
UnityEngine.EventSystems.EventSystem:Update() (at /Users/builduser/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/EventSystem.cs:377)

That is why I finally posted something in the forum, because I am completely baffled.

ShowReward is what populated the dialog. ClaimReward is only called by the button that appears in the dialog.

        public void OpenPlayerRewardScreen()
        {
            playerRewardCanvas.gameObject.SetActive(true);
            playerRewardCanvas.ShowReward(_playerObject, this);
        }

I have a button in the main scene HUD that sets the PlayerRewardCanvas active. As far as I know there is only one gameobject with that script attached to it. Does unity have a way to search game objects for all those using a script?

Where does this particular code snippet get its reference to playerRewardCanvas from? I’m wondering if maybe you’re pointing to a prefab by accident or another instance of the script in the scene.

You can search your scene in the editor for all instances of a script by using the search bar in the hierarchy. For this particular search you can type: t: PlayerRewardCanvas. You can do this while the game is running or in edit mode.

2 Likes

This is a blocker to implementing this feature. The ClaimReward() adds items to player inventory and closes the canvas.

Searching for a component name in the object hierarchy will filter for all objects with that component attached (the spelling must be complete and exact; it won’t do a substring match).

Note that Debug.Log() also has an optional second argument where you can pass a GameObject, and then clicking on that message in the log will scroll the object hierarchy to that object. Can be handy for tracking down the object that emitted an error, and I recommend including it as standard practice on pretty much all Log() calls that you intend to leave in your code permanently (and some that you don’t).

If you are really, truly convinced that you are getting “_rewards == null” returning false and “_rewards.Count” throwing a null reference exception at the same time, then you should combine them into a single test (probably involving a try-catch block). If you can write a script that clearly and reproducibly shows that behavior, that will be a really big deal.

If you can’t, then we have to assume that some unknown portion of the information you are feeding us is simply wrong, which makes it pretty hard to help.

2 Likes

Thanks for the tip. It did only return one result.

This is the only place that _rewards is being used outside of the PlayerRewardCanvas Class.

        public void ShowRewards(List<Item> items, PlayerObject playerObject)
        {
            foreach (Transform child in transform)
            {
                Destroy(child.gameObject);
            }

            List<RewardTile> tiles = new List<RewardTile>();

            foreach (Item item in items)
            {
                RewardTile tile = Instantiate(_rewardTile, transform);
                tile.SetItem(item, this);
                tile.SetTileImage(GetTileSprite(item));
                tiles.Add(tile);
            }
        }

As you can see I am just using the list of items to Instantiate a bunch of Tiles to Display in the UI, and I am not modifying the list I am passing in any way.

6083193--660234--Screen Shot 2020-07-12 at 6.27.05 PM.png

Winner, Winner Chicken Dinner. The Click Event on the claimButton was hooked up in the inspector to the prefab I am no longer using. Weird that it only showed up in the search once. I would have expected it to show up on the prefab and the unpacked version I was actually using in the scene. Thank you. That was driving me freaking nuts.

1 Like

The hierarchy only shows objects in open scenes. Prefabs are not part of any scene.

1 Like