Destroying Interface object (MissingReferanceException)

Hello!

I’m using raycasts for selecting, deselecting and interacting with gameObjects. For example, I got a soda can which I can drink and I need to destroy the gameobject, but when I destroy the gameobject its giving missing referance exception and breaking raycast system.

I got a interface like this:

public interface IInteractable
{
    void select();
    void Interact();
    void deSelect();
}

This is my raycast:

void Update()
    {
        raycastForInteractable();

        if (Input.GetKeyDown(KeyCode.F))
        {
            if (currentTarget != null)
            {
                currentTarget.Interact();
            }
        }
    }

    private void raycastForInteractable()
    {
        RaycastHit hit;
        Vector3 fwd = transform.TransformDirection(Vector3.forward);

        if (Physics.Raycast(transform.position, fwd, out hit, range))
        {
            IInteractable interactable = hit.collider.GetComponent<IInteractable>();

            if (interactable != null)
            {
                if (hit.distance <= range)
                {
                    if (interactable == currentTarget)
                    {
                        return;
                    }
                    else if (currentTarget != null)
                    {
                        currentTarget.deSelect();
                        currentTarget = interactable;
                        currentTarget.select();
                        return;
                    }
                    else
                    {
                        currentTarget = interactable;
                        currentTarget.select();
                        return;
                    }
                }
                else
                {
                    if (currentTarget != null)
                    {
                        currentTarget.deSelect();
                        currentTarget = null;
                        return;
                    }
                }

and this is my soda can:

 private GameObject player;
    private playerNeeds playerNeeds;

    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player");
        playerNeeds = player.GetComponent<playerNeeds>();
    }

    public void select()
    {
        gameObject.GetComponent<OutlineObject>().enabled = true;
    }

    public void deSelect()
    {
        if (gameObject != null)
        {
            gameObject.GetComponent<OutlineObject>().enabled = false;
        }
    }

    public void Interact()
    {
        Drink();
    }

    void Drink()
    {
        playerNeeds.thirst += 20;
        Destroy(this.gameObject);
    }

After I destroy it, its still trying to deselect(); but I destroyed it. Even checking if (gameobject != null) not working.
What can I do in this situation?

Thanks!

6368358--708804--Yeni proje.gif

For starters, don’t use null checking for Unity’s gameobject’s, Unity overrides null checking behavior because it has its own null objects in place because of the gap between native code and mono. This is highly unreliable especially when destroying objects, and doing checks in the same frame before the engine has settled down and starts to reflect the changes.

I’m still to provide you with a clear answer of what exactly went wrong, if I can’t muster cognitive power to do so. I’m a bit fuzzy these days.

I’m failing to see where you’re destroying the object though.

Sorry :frowning: I updated the script. Its on the soda script.

and where do you implement IInteractable? I assume it’s at the same soda can object where you inherit MonoBehaviour?

this is why I never do this, and treat my gameobjects completely separately from the ordinary C# code.
I’ve seen other people reason differently with how the system works, but if I were you, I’d completely redesign this behavior and separate MonoBehaviour from any interfaces.

in reality this is a very simple setup, but you want to be sure that it doesn’t backfire, by ensuring that you don’t constantly checking it for null etc. don’t do that with gameobjects. treat your MonoBehaviors as dumb puppets, not as being part of the state logic.

don’t feed their state back into the system, just control it from it outside, flick them on or off, move them, destroy them, but in the end, care less about what happens to them internally, if you get what I mean.

1 Like

btw when you call Destroy there are no guarantees that it truly goes to (C#'s) null.
because you’re working with interfaces, you’re basically trumping the Unity’s null, and are comparing apples to oranges.

it’s not intuitive, I know, so stick to basics – don’t use interfaces on MonoBehaviours, you don’t need them anyway.

it’s a bad design (that Unity’s null != C# null), sure, but you’ve compounded several things, making it run terribly unpredictable from what I can see. people normally don’t stumble into that.

maybe there is a simpler solution for the error you’ve got, and I’m not seeing it, but still it’s not a good general approach to a functionality you’d like to have.

You see internally when you check whether a gameobject is null, Unity has overriden equality operators so that myobject != null means myobject.Equals(VerySpecificUnityObjectThatBehavesLikeNull)

this stupid surrogate comparison exists for a multitude of under-the-hood reasons let’s not dive into that.

you’ve managed to circumvent this by casting the object back to interface. and whatdoyouknow, it’s not null, because it’s not null, it’s VerySpecificUnityObjectThatBehavesLikeNull so this can break your mind if you don’t know a bit of the history behind this decision.

Im confused. I dont know if there is solution either but I know that I need interface’s so I will try to get rid of monobehaviour. Thanks for the comments :slight_smile:

yes, you can introduce a step in between. if you definitely need interfaces, make sure your true C# objects drive the behavior of any underlying MB’s and not the other way around. make them act like controllers. just let MB’s be as dumb as possible. it should work without any problems.

you should be able to find entire articles on the topic, I can find any now for some reason, but here you can see a glimpse of it, it all starts with Unity.Object

why? because ?. and ?? work with the actual C# null, so it cannot work.

all in all, until you learn a bit more about this, just be mindful that whenever you do myobject == null or myobject != null , if myobject derives from UnityEngine.Object this isn’t as simple as you thought it was. try to steer away from it, it’ll save you from a lot of headaches and confusions. and sadly, they did this exactly to prevent any confusion. I hate such beginner-friendly approaches where you hide critically dangerous misconceptions under the carpet, but I guess it’s something you learn to live with.

I finally found a solution. Its working! Not a single missingRefaranceException.

This is the new soda can script and its with monoBehaviour.

public class drinkableObject : MonoBehaviour, IInteractable
{
    private GameObject player;
    private playerNeeds playerNeeds;

    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player");
        playerNeeds = player.GetComponent<playerNeeds>();
    }

    public void select()
    {
        gameObject.GetComponent<OutlineObject>().enabled = true;
    }

    public void deSelect()
    {
        if ((UnityEngine.Object)this != null)
        {
            gameObject.GetComponent<OutlineObject>().enabled = false;
        }
    }

    public void Interact()
    {
        playerNeeds.thirst += 20;
        UnityEngine.Object.Destroy(this.gameObject);
    }

The links you sent me was amazing helpfull! Thanks alot <3

Great! I had a good hunch after all. Sorry if I confused you for a moment, but the whole thing is confusing anyway.
I’m fully aware you don’t want to just throw away your code, but be advised to take this into account in the future. There are much more generous ways to work with this instead of working around it. Consider changing your approach, as you’re still prone to hitting this regularly if you approach gameobjects from the flat C# perspective. (Always mind the native C++ ↔ C# gap)

1 Like

I had a similar problem and now after a “lesson learned” I fully agree with @orionsyndrome to avoid using Interfaces with Monobehaviour type. Even if you’ve managed to perform null check with help of casting to the UnityEngine.Object, the problem will get even worse. The exception will occur later, because of the code execution attempt from a non-existing GameObject.

The lesser evil will be to use an abstract class instead of the interface.

abstract public class HeroAbstract : MonoBehaviour
    {       
        protected Board cBoard;

        protected void Awake() {
            cBoard = Controller.instance.GetComponent<Board>();        
        }

        public abstract void decideBehaviour();
        public abstract void trigger(CellMaterialized heroCell);
                   
    }