Action coming off as null

Hello everyone, how are you?

I have developed a simple inventory system with drag and drop functionality, and now I am working on a “switching” items functionality.

The main components are the Inventorymanager (manages the whole game inventory), and the inventoryslot (a UI parent component) which holds an inventoryitem (a component that holds the image in which the player can drag and drop into other slots).

My inventory has zero slots, and each time a new item enters it the inventory manager instantiates a new inventory slot.

All the drag-and-drop functionality is held within the inventory item component, which means that a new inventory item is created when a new item enters the inventory. But then something is happening. I developed an Action to ping the inventory manager when an item was dragged and dropped in place of another item, making them switch places. This way, the inventory manager can switch the items’ places in the inventory item list (I don’t know if this is necessary, but I would like to do it regardless for consistency’s sake). But every time I do it, my InventoryItem component says that the Action is null. I have tried with other simpler actions before and they all come as null.
Can anyone help me discover why they are null? Any tips on how to improve the logic/scripts are very welcome.

Inventory Manager ssubcribing to Action

        public GameObject inventoryItemPrefab;

        // TODO: subscribe to Item replacing action event
        private void Awake()
        {
            inventoryItemPrefab.GetComponent<InventoryItem>().ItemWasReplaced += ReplaceItemMethod;
        }

        private void ReplaceItemMethod(int newIndex, int oldIndex)
        {
            print(newIndex);
            print(oldIndex);
        }

Inventory Item component Awake method with an example Action (Just to see if things are working. They are not. Even this simple example Action come as null).

public event Action exampleThingy;

        private void Awake()
        {
            exampleThingy.Invoke();
        }

Inventory Item component with OnEndDrag function for the IEndDragHandler interface (This component is instantiated every time a new item is added. Maybe that why the action is null? Since the component does not exist at the hierarchy and is instantiated later the Action simply starts as null?)

public event Action<int, int> ItemWasReplaced;

public void OnEndDrag(PointerEventData eventData)
        {

            if (itemToReplace != null)
            {
                itemToReplace.transform.SetParent(parentBeforeDrag);
                itemReplaced = true;
            }

            itemImage.raycastTarget = true;
            transform.SetParent(parentAfterDrag);

            if (itemReplaced)
            {
                Transform parent = parentAfterDrag.parent;

                InventorySlot[] childList = parent.GetComponentsInChildren<InventorySlot>();

                print(ItemWasReplaced);

                CallItemReplaced();

                ItemWasReplaced.Invoke(ReturnFoundIndex(childList, parentAfterDrag), 
               ReturnFoundIndex(childList, parentBeforeDrag));
            }
        }

I also attatched a GIF to showcase the behaviour in game mode.
Bug-ezgif.com-video-to-gif-converter

If you don’t expect it to be null, you have a bug, time to fix the bug. It’s just a null reference error, same as any other.

If your design does expect it to be null then test it before you invoke it.

Beyond that… The answer is always the same… ALWAYS!

How to fix a NullReferenceException error

Three steps to success:

  • Identify what is null ← any other action taken before this step is WASTED TIME
  • Identify why it is null
  • Fix that

NullReference is the single most common error while programming. Fixing it is always the same.

Some notes on how to fix a NullReferenceException error in Unity3D:

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

1 Like

(Sorry for douple replying, my post went bananas)

Sorry mate, I know you’ve come here with the best intentions but that does not help at all.

I am not expecting the Action to be null, since I have put some InventoryItems objects in the hierarchy and the Action still comes up as null, even when I Invoke it on Awake and/or Start.

I truly dont know what I have done wrong since I have an Action in the InventoryManager class that was created the exact same way, but with bool and Gameobject params, and it is working fine.

Nobody like their actions to be null. The point of Kurt’s post is that you need to live with it, learn to debug your code like a grown up, get better at programming, and in general get used to resolving null exceptions on your own.

We all introduce null exceptions at some point, regardless of how old or how proficient we are, due to how mistakes are made: typically by honest omission and/or lack of attention, never by intention. It’s always the same, and nobody likes to see a runtime null exception error in their code. If such a software would make it to the market, that would be a hard crash for the end consumer, and nobody likes angry customers.

The whole point is that by learning how to handle this quite regular process of basic debugging, you become a better craftsman in this line of duty, and because of this, Kurt’s advice is actually golden, and on spot. Maybe you’ll appreciate it one day.

If you came here with an expectation that we will run your code, think like you did, and reverse engineer why something that you did doesn’t work, well that’s not a very realistic expectation, because a) that’s not fun, b) it’s an unpaid work, and c) you’ll learn nothing.

In other words if you can’t handle null exceptions on your own, you can’t handle game programming. You can judge frequency of such errors just by searching this forum (and probably the internet), and it’s always the same — your null exceptions are your responsibility as a coder.

There is nothing someone else can do to make you better at avoiding them, because it’s you who is mishandling calls or data in your program, and if you haven’t asked for any help before the error, why now?

2 Likes

There is nothing someone else can do to make you better at avoiding them, because it’s you who is mishandling calls or data in your program, and if you haven’t asked for any help before the error, why now?

Because I tried everything and couldn’t find the solution after trying everything, so I asked for help in the official forum? What kind of question is that?
I don’t know what the objective of this wall of text was but to me, it sounds like you are just trying to belittle someone who is genuinely asking for help.

Also, I am not trying to get anyone to run my code and code it for me. I provided evidence of what I am doing, trying to be as didactic as possible so anyone who might be interested in helping, or even someone who is going through the same problems as me, can have more information than just a tiny “this is not working, help” with no explanation or context at all.

If you are not interested in helping or are just trying to “teach me a lesson” then please don’t.

Help isn’t about having someone do the things you can’t, even if you feel like you’ve tried everything. It’s not about being handed a solution to a specific problem. Because after solving that one problem, what happens with the next? Would you expect someone to provide the solution to that too? And the one after that?

That’s not seeking help—that’s seeking someone to do your work for you. And that has another name: work. Of course, you can pay someone to do the things you can’t, but it’s important not to confuse work with help.

Help is about offering you new approaches or tools so that you can learn to solve the things you’re currently unable to tackle on your own.

“Teaching someone a lesson” is one of the most valuable forms of help. It’s not about doing the work for someone; it’s about empowering them to do it themselves.

So, don’t confuse asking for help with asking someone to do your work for you.

For example, solving a null reference exception is a fundamental skill in programming. If you haven’t solved it yet, it doesn’t mean you’ve “tried everything”; it means you’ve only tried the things you know or can think of. Otherwise, the issue would already be resolved.

Take the advice shared by @Kurt-Dekker in his posts. Learning to fix the issue yourself is an invaluable skill. If his suggestions don’t work, revisit them and try again. If you still can’t solve it, then you can hire someone to do the work you can’t or wait if someone feels like doing it for free in his spare time. But it’s unreasonable to expect others to do it for free, or to get upset when they attempt to teach you how to handle it yourself.

EDIT: I suggested following @Kurt-Dekker’s advice to solve your problem, and you mistakenly marked my post as the solution. I’ve corrected this by marking Kurt’s post as the solution so that anyone reading this topic in the future can benefit from it.

3 Likes

Null references are just like stubbing your toe while walking.

Really, that’s all they are.

Imagine walking down some street somewhere and stubbing your toe because you didn’t notice the curb, then coming and posting to a forum about it and asking people to help you figure out why you keep stubbing your toe.

And we all stub our toes.

Today I worked a full day and probably stubbed my toe (got some kind of null reference) at least a half dozen times.

It happens so much it’s just not even worth remembering.

The three steps ALWAYS fix it. Really! Honest. See above.

NOW… just to make you feel a lot better about yourself, keep this in mind… and this is NOT an exaggeration:

Inventories are MASSIVELY DIFFICULT THINGS!! MASSIVELY!

These things (inventory, shop systems, character customization, dialog tree systems, crafting, ability unlock systems, tech trees, etc) are fairly tricky hairy beasts, definitely deep in advanced coding territory.

The following applies to ALL types of code listed above, but for simplicity I will call it “inventory.”

Inventory code never lives “all by itself.” All inventory code is EXTREMELY tightly bound to prefabs and/or assets used to display and present and control the inventory. Problems and solutions must consider both code and assets as well as scene / prefab setup and connectivity.

If you contemplate late-delivery of content (product expansion packs, DLC, etc.), all of that has to be folded into the data source architecture from the beginning.

Inventories / shop systems / character selectors all contain elements of:

  • a database of items that you may possibly possess / equip
  • a database of the items that you actually possess / equip currently
  • perhaps another database of your “storage” area at home base?
  • persistence of this information to storage between game runs
  • presentation of the inventory to the user (may have to scale and grow, overlay parts, clothing, etc)
  • interaction with items in the inventory or on the character or in the home base storage area
  • interaction with the world to get items in and out
  • dependence on asset definition (images, etc.) for presentation
    → what it looks like lying around in the world? In a chest? On a shelf?
    → what it looks like in the inventory window itself?
    → what it looks like when worn by the player? Does it affect vision (binoculars, etc.)
    → what it looks like when used, destroyed, consumed?

Just the design choices of such a system can have a lot of complicating confounding issues, such as:

  • can you have multiple items? Is there a limit?
  • if there is an item limit, what is it? Total count? Weight? Size? Something else?
  • are those items shown individually or do they stack?
  • are coins / gems stacked but other stuff isn’t stacked?
  • do items have detailed data shown (durability, rarity, damage, etc.)?
  • can users combine items to make new items? How? Limits? Results? Messages of success/failure?
  • can users substantially modify items with other things like spells, gems, sockets, etc.?
  • does a worn-out item (shovel) become something else (like a stick) when the item wears out fully?
  • etc.

Your best bet is probably to write down exactly what you want feature-wise. It may be useful to get very familiar with an existing game so you have an actual example of each feature in action.

Once you have decided a baseline design, fully work through two or three different inventory tutorials on Youtube, perhaps even for the game example you have chosen above.

Breaking down a large problem such as inventory:

If you want to see most of the steps involved, make a “micro inventory” in your game, something whereby the player can have (or not have) a single item, and display that item in the UI, and let the user select that item and do things with it (take, drop, use, wear, eat, sell, buy, etc.).

Everything you learn doing that “micro inventory” of one item will apply when you have any larger more complex inventory, and it will give you a feel for what you are dealing with.

Breaking down large problems in general:

The moment you put an inventory system into place is also a fantastic time to consider your data lifetime and persistence. Create a load/save game and put the inventory data store into that load/save data area and begin loading/saving the game state every time you run / stop the game. Doing this early in the development cycle will make things much easier later on.

Various possible inventory data structures in Unity3D:

You should always check delegates for null before invoking them.

Usually:

if (SomeAction != null)
{
    SomeAction();
}

Or in short:

SomeAction?.Invoke();
2 Likes

Super nitpick:

Those two methods of null-checking are not completely equivalent.

The latter actually results in lowered code where the delegate is assigned to a temporary variable before being null-checked and raised:


  Action someAction = SomeAction;
  if(someAction != null)
  {
      someAction();
  }

While this very rarely makes any difference in practice, if raising of the event and any unsubscribing from the event can take place on different threads, then doing the null-checking and raising manually in two steps is not actually guaranteed to protect you from a NullReferenceException, while using the null-conditional operator is.

So, I would recommend always using the null-conditional operator by default when invoking events, since it’s both less verbose and arguably a little bit more robust :slightly_smiling_face:


  SomeAction?.Invoke();
3 Likes