How to add OnPointerEnter/Exit behavior programmatically to an instantiated GameObject?

I have a simple script which I can drag onto any GameObject to get control over OnPointerEnter/Exit behavior for it:

using UnityEngine;
using UnityEngine.EventSystems;

public class OnPointerEnterExitBehavior : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {
    bool inContext;
    GameObject myGO;

    private void Awake() {
        myGO = gameObject;
    }

    void Update() {
        if (Input.GetMouseButtonDown(0) && !inContext) {
            myGO.SetActive(inContext);
            GameObject.Destroy(gameObject);

        }
    }

    public void OnPointerEnter(PointerEventData eventData) {
        inContext = true;
    }

    public void OnPointerExit(PointerEventData eventData) {
        inContext = false;
    }
}

In that script it will simply destroy the item if you click outside it (eg. for a menu).

I also have a script for creating a menu programmatically from instantiation of a prefab which would most simply be:

public class GenerateAndOpenVenueMenu : MonoBehaviour
{
    public Canvas ParentCanvas;

    public void GenerateAndOpenVenuMenu() {

        GameObject newMenu = Instantiate(Resources.Load<GameObject>("MENU WINDOW")) as GameObject;
        newMenu.transform.SetParent(ParentCanvas.transform, false);
        newMenu.SetActive(true);
      }
}

I can control OnPointerEnter/Exit behavior by dragging the first script onto the prefab “MENU WINDOW” so it’s built into the prefab. But then if I want different OnPointerEnter/Exit behaviors for different menus this is less ideal.

I am wondering if there is some way to write the first script’s code into the second script so that I can handwrite in the second script what the OnPointerEnter/Exit behaviors should be for the GameObject “newMenu”.

Thanks.

gameObject.AddComponent<OnPointerEnterExitBehavior>();

You might want to check if the component already exists, though.

You can also make it configurable by using delegates and events instead of predefined methods.

1 Like

Thanks again. I understand I can add the existing static script that way. But that is just as you said a “predefined method.” I would then have to create many of those “OnPointerEnterExitBehavior” scripts (one for each menu) if I want different behaviors on different menus.

What I am trying to do is what you described in the second sentence: “make it configurable by using delegates and events instead of predefined methods.”

How would I change the second script I posted (the one where the menu is instantiated) to add capacity to control what happens to the menu object when the mouse is over or not? I am not sure how to do this.

ie. I want to get rid of the first script completely and re-write the essence into the second script so I have control over what happens there.

Thanks

Related reading: “Callback”:
https://en.wikipedia.org/wiki/Callback_(computer_programming)


You need to add support for events or delegates.

Unity events:
https://docs.unity3d.com/ScriptReference/Events.UnityEvent.html

C# events (and delegates):
https://docs.microsoft.com/en-us/dotnet/standard/events/

In this scenario, your class would have public field, for exxample

public event System.Action onExit;

And after adding the class you’d subscribe it to functionality you want.

myComp = gameObject.AddComponent<OnPointerEnterExitBehavior>();
myComp.onExit += myEventHandler;

or even:

myComp = gameObject.AddComponent<OnPointerEnterExitBehavior>();
myComp.onExit += () => {
    myButton.doStuff();
}

Be aware that C# events do not serialize (if you care about serialization), and if you want them to serialize or configure in editor, you’d need to use UnityEvents instead and AddListener interface.

1 Like

Wow C# is interesting. I didn’t know you could do that. I understood enough from that to get it working.

For the first script I wrote:

using UnityEngine;
using UnityEngine.EventSystems;

public class OnPointerEnterExitBehavior : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {
    bool inContext;
    GameObject myGO;
    public event System.Action OnExit;

    private void Awake() {
        myGO = gameObject;
    }

    void Update() {
        if (Input.GetMouseButtonDown(0) && !inContext) {
            OnExit.Invoke();
        }
    }

    public void OnPointerEnter(PointerEventData eventData) {
        inContext = true;
    }

    public void OnPointerExit(PointerEventData eventData) {
        inContext = false;
    }
}

And for the second script:

public class GenerateAndOpenVenueMenu : MonoBehaviour
{
    public Canvas ParentCanvas;

    public void GenerateAndOpenVenuMenu() {

        GameObject newMenu = Instantiate(Resources.Load<GameObject>("MENU WINDOW")) as GameObject;
        newMenu.transform.SetParent(ParentCanvas.transform, false);
        newMenu.SetActive(true);
       newMenu.GetComponent<OnPointerEnterExitBehavior>().OnExit +=()=> {
            GameObject.Destroy(gameObject);
          }
      }
}

And this works. So what I understand is that “newMenu.GetComponent().OnExit +=()=> {” is creating a lambda event handler on the fly. Then I can specify anything I want there.

The “OnPointerEnterExitBehavior” class thus becomes a generic one I can leave as part of the prefab and customize the behavior of on each menu made from it nicely.

Thanks again. Very appreciated.

1 Like

Variations of that existed for a long time, even in C (without ++).
It is available in many languages. Originally it was just “callback”.

You might want to use OnExit.?Invoke() here. This ? will check if OnExit is null and only call the method if it is not null. It can be null.

Yes. However the important part is that your local variable i is bound to a function parameter there.

Related:
https://discussions.unity.com/t/847536/7

Generic has a specific meaning in C#, and your class isn’t it. You can read more about generic classes and function on microsoft docs or in books about C#.[/QUOTE]

2 Likes