• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
6
Question by Hullabu · Apr 15, 2015 at 02:38 PM · uiguieventsystem

How to detect click outside UI panel

I want to create some kind of context menu with typical behaviour like in Windows. So if I click outside the opened menu it must be closed. But at the same time if any UI object located under the mouse cursor it must detect that click. It means I can't place invisible panel behind context menu and close the menu when I click on that panel. Is there way to do what I want?

Comment
Add comment · Show 6
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image furic · Apr 16, 2015 at 03:40 AM 0
Share

this problem can be easily solved if using NGUI, there is a static variable on mouse hovered gameobject.

I want to know too for Unity UI...

avatar image rakkarage · Apr 16, 2015 at 04:07 AM 0
Share

stretch a full screen empty image in back to catch all events?

avatar image Hullabu · Apr 16, 2015 at 07:08 AM 0
Share

Not an option. I wrote about this.

avatar image Hullabu · Apr 16, 2015 at 07:25 AM 0
Share

this problem can be easily solved if using NGUI, there is a static variable on mouse hovered gameobject.

Actually I found solution like this for Unity UI, but I don't like it. There are bunch of interfaces you can implement to catch different events. See http://docs.unity3d.com/$$anonymous$$anual/SupportedEvents.html So you can implement IPointerEnterHandler and IPointerExitHandler to detecting of cursor inside/outside the panel. But a lot of not obvious problems comes with this solution...
avatar image Addyarb · Apr 16, 2015 at 07:52 AM 0
Share

It depends on what type of game you are making.

For $$anonymous$$e, I just put a script on the terrain that deselects/closes any windows when it's clicked. You may have to end up putting things in different layers and using a raycast from the mouse position. If you don't hit a UI layer, then deselect the currently selected UI panel. Something like that. Can you explain more in-depth on what your situation is?

Show more comments

9 Replies

· Add your reply
  • Sort: 
avatar image
19

Answer by anisabboud · May 08, 2015 at 12:22 AM

I wrote a function:

     private void HideIfClickedOutside(GameObject panel) {
         if (Input.GetMouseButton(0) && panel.activeSelf && 
             !RectTransformUtility.RectangleContainsScreenPoint(
                 panel.GetComponent<RectTransform>(), 
                 Input.mousePosition, 
                 Camera.main)) {
             panel.SetActive(false);
         }
     }

which I call in Update() and it hides the panel when the user clicks outside it:

         HideIfClickedOutside(HelpPanel);

http://docs.unity3d.com/es/current/ScriptReference/RectTransformUtility.RectangleContainsScreenPoint.html

Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Hullabu · May 26, 2015 at 12:44 PM -1
Share

Too easy, man, too easy... This is actually works when you have just one simple panel, but it will not work if a panel has some childs beyond its frame. This is why I'm talking about native Unity UI events.

avatar image Zenov · Aug 04, 2015 at 08:51 PM 0
Share

Thanks for this. It helped me for what I needed. I have my canvas set to screen space and it didn't work at first, but setting the camera parameter to 'Null' fixed it. Thought I would post this in case anyone else has this issue.

avatar image hasCode · Sep 22, 2016 at 06:35 PM 2
Share

If your render mode is "Screen Space Overlay" (the default) you will need to pass the camera as null. Otherwise RectangleContainsScreenPoint does not work,

 private void CloseIfClickedOutside(GameObject panel)
 {
     if (Input.Get$$anonymous$$ouseButton(0))
     {
         RectTransform rectTransform = panel.GetComponent<RectTransform>();
         Canvas canvas = GetComponent<Canvas>();
         Camera camera = canvas.render$$anonymous$$ode == Render$$anonymous$$ode.ScreenSpaceOverlay ? null : this.Camera;
         if (!RectTransformUtility.RectangleContainsScreenPoint(rectTransform, Input.mousePosition, camera))
             this.Close();
     }
 }

You will have to replace "this.Camera" and "this.Close();" with your own camera object and your own close method.

avatar image Ziplock9000 · Jul 10, 2019 at 02:57 AM 1
Share

Doing this in Update is a terrible solution that ignores the whole event system.

avatar image
9

Answer by Ziplock9000 · Jul 16, 2019 at 08:27 PM

By far the best way to do this is already built into Unity. It requires no raycasting or polling Update and uses minimal code. I use this for hiding my popup context menus in my game.

Pop this in Awake:

 EventSystem.current.SetSelectedGameObject(gameObject);

Then create this function to accept the event:

 public void OnDeselect(BaseEventData eventData)
 {
         //Mouse was clicked outside
 }


Comment
Add comment · Show 6 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image BluishGreenPro · Jan 11, 2020 at 09:42 AM 3
Share

YES! This is a far better solution. However, when I tried this, any time I tried to interact with a button on the UI panel, it would consider itself "deselected" and close immediately. To fix this, here is the solution I arrived at; Use information about whether the mouse pointer is above the UI panel to deter$$anonymous$$e if the menu should ACTUALLY be closed on a click. I also found it necessary to constantly reset the "SelectedGameObject" to the panel on pointer exit / enter. Here's everything you should need to make this work:

 using UnityEngine.EventSystems;
       public class CLASSNA$$anonymous$$E : $$anonymous$$onoBehaviour, IDeselectHandler, IPointerEnterHandler, IPointerExitHandler
       {
 
        private bool mouseIsOver = false;
 
        private void OnEnable() {
             EventSystem.current.SetSelectedGameObject(gameObject);
         }
 
         public void OnDeselect(BaseEventData eventData) {
     //Close the Window on Deselect only if a click occurred outside this panel
             if (!mouseIsOver)
                 CloseSelf();
         }
 
         public void OnPointerEnter(PointerEventData eventData) {
             mouseIsOver = true;
             EventSystem.current.SetSelectedGameObject(gameObject);
         }
      
         public void OnPointerExit(PointerEventData eventData) {
             mouseIsOver = false;
             EventSystem.current.SetSelectedGameObject(gameObject);
         }
 }

avatar image CyanIdle BluishGreenPro · Nov 20, 2020 at 10:58 AM 0
Share

Thank you so much for this it's really well done. I just wanted to know if it's possible to check if another button (outside) has been pressed? I have a lot of button across my screen and it'll be nice if they don't mess with this when you press them

avatar image BluishGreenPro BluishGreenPro · Nov 20, 2020 at 06:23 PM 0
Share

I can't say for sure, but I don't see any reason why the code I've written here would prevent those "outside" buttons from functioning. Should be pretty painless to try an implementation to be sure.

avatar image teh1archon · Apr 02, 2020 at 05:46 AM 0
Share

The best answer, no idea why it's not placed on top. I had a similar scenario where I have a button that opens and closes an info tooltip but clicking anywhere should close the tooltip regardless. Using the EventTrigger component on the button with OnDeselect event to close the tooltip is the best input-agnostic solution for these situations (since it also works with touch input).

avatar image JaviTheGreat · Aug 20, 2020 at 12:18 AM 0
Share

This is by far the best approach. Checking in Update is overkill.

avatar image forest_wind123 · Apr 28 at 09:19 AM 0
Share

A side effect I found: auto navigation of buttons in the panel failed. Because it relies on the selection state of elements.

avatar image
3

Answer by rageingnonsense · Jun 15, 2015 at 10:11 PM

I had a rough time with this, and did not find any of the answers I found on google satisfactory. I decided to roll out my own component to help with this. Just attached teh following script to the panel you want to treat as a context menu; and also attach a canvas group (as I use alpha to hide the menus):

 using UnityEngine;
 using UnityEngine.UI;
 using UnityEngine.EventSystems;
 using System.Collections;
 
 public class ContextMenu : MonoBehaviour, IPointerExitHandler, IPointerEnterHandler {
     private bool hasFocus = false;
     private CanvasGroup cv;
     private bool scheduleShow = false;
 
 
     public void Start() {
         cv = GetComponent<CanvasGroup>();
     }
 
     public void Update() {
         if (!scheduleShow && !hasFocus) {
             if (Input.GetMouseButtonUp(0)) {
                 cv.alpha = 0;
                 cv.blocksRaycasts = false;
             }
 
         } else if (scheduleShow) {
             cv.alpha = 1;
             cv.blocksRaycasts = true;
             scheduleShow = false;
         }
     }
 
 
     public void Show() {
         scheduleShow = true;            
     }
 
 
     public void OnPointerExit(PointerEventData eventData) {
         hasFocus = false;        
     }
 
     public void OnPointerEnter(PointerEventData eventData) {
         hasFocus = true;               
     }
 }
 

To use it, just call Show() from another script to display the menu. The automatic hiding will work without any other code required.

This is better than using the Selectable method, as that method intercepts all kinds of keypresses that are undesirable (like WASD controls, middle mouse click, etc). It also is better than the giant hidden panel method, as that method can have the side effect of blocking raycasts on other gameobjects in your scene.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
1

Answer by moghes · Apr 16, 2015 at 08:35 AM

More than a solution could be applicable.

One method is to be aware whether a window is open or not. you can simply have a bool like windowIsOpen and set to true any time a window opens.

and on any mouse event you check

 if(Input.GetMouseButton(0))
  if(windowIsOpen)
    // close window

and placing a transparent (collider) background would work as well, you have to have the window collider depth closer to the camera.. but first try with the first method

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Hullabu · Apr 16, 2015 at 09:19 AM 0
Share

Your code is require using IPointerEnterHandler and IPointerExitHandler to detecting of cursor inside/outside the panel/window. This way is applicable, but with several restrictions which I dislike. I'm trying to find another way.

avatar image moghes · Apr 16, 2015 at 12:14 PM 1
Share

@Hullabu the method using a bool doesn't require using IPointerEnterHandler.. In your code when you open your window like myWindow.SetActive(true) or any other method, set windowIsOpen to false.

even you can check without the bool by checking yourWindow.activeself but in case you have several windows, you need to check them all.. by using a bool you can set to true in case any of your windows open.

Hope I could explain it well

avatar image Hullabu · Apr 16, 2015 at 01:14 PM 0
Share

@moghes Ok, how can I close the window when I click outside and don't close when I click inside using your code?

avatar image moghes · Apr 16, 2015 at 01:32 PM 1
Share

@Hullabu if you are using new UI (4.6) you must have your window as panel.. or any gameObject, so yourPanel.SetActive(false) to close

and when you open your window, you have to set your bool windowIsOpen = true

you may paste a code section to get more specific help

avatar image Hullabu · Apr 16, 2015 at 02:06 PM 0
Share

@moghes Looks like we have some misunderstanding here. I told about your code which will close the window no matter where I click. But the window should be closed only if I'll click outside it. This is why I told about IPointerEnterHandler and IPointerExitHandler. They can be implemented to detect if pointer above a panel.

avatar image
1

Answer by f4bo · Apr 17, 2015 at 03:58 PM

if you got a hierarchy with a main container, let's say a panel, as parent for all your ui objects (windows, menu panels etc), you can add a trigger event component there, add it a pointerclick event and inside that, add a proper handler for each object you want to close or simply deactivate.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
  • 1
  • 2
  • ›

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

19 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Detect If Pointer Is Over Any UI Element 6 Answers

Trigger UI element callback from script 0 Answers

Default Event Handler 1 Answer

How to start the game with CurrentInputMode being "Buttons" instead of "Mouse" in StandaloneInputModule? 0 Answers

UGUI keyboard navigation direction for different sounds 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges