Search Unity

  1. Are you interested in providing feedback directly to Unity teams? Sign up to become a member of Unity Pulse, our new product feedback and research community.
    Dismiss Notice

Detect double click on something (what is the best way)

Discussion in 'Scripting' started by Mayo54, Jun 14, 2017.

  1. Mayo54

    Mayo54

    Joined:
    Jun 8, 2017
    Posts:
    32
    Hi !

    I want to detect double click on UI Image but i found loads of way to do it, some of them don't work (onDoubleClick() for instance) but i don't know why.

    At this point, i made this:

    Code (CSharp):
    1. {
    2.     float clicked = 0;
    3.     float clicktime = 0;
    4.     float clickdelay = 0.5f;
    5.  
    6.     bool DoubleClick()
    7.     {
    8.         if (Input.GetMouseButtonDown(0))
    9.         {
    10.             clicked++;
    11.             if (clicked == 1) clicktime = Time.time;
    12.         }
    13.         if (clicked > 1 && Time.time - clicktime < clickdelay)
    14.         {
    15.             clicked = 0;
    16.             clicktime = 0;
    17.             return true;
    18.         }
    19.         else if (clicked > 2 || Time.time - clicktime > 1) clicked = 0;
    20.         return false;
    21.     }
    22.  
    23.  
    24.     private void Update()
    25.     {
    26.        
    27.         if (DoubleClick())
    28.         {
    29.             Vector2 mousePos = new Vector2(Screen.width - Input.mousePosition.x, Screen.height - Input.mousePosition.y);
    30.             if (this.GetComponent<RectTransform>().rect.Contains(mousePos)) {
    31.                 Debug.Log("DoubleCLick on: "+ this.GetComponent<RectTransform>().name);
    32.             }
    33.  
    34.         }
    35.     }
    36. }
    And i attach this componement to my UI Image. This script return "DoubleClick" when i doubleClick. But the problem is that the following part is not triggered: (even if my mouse is above the UI Image..)
    Code (CSharp):
    1.     if (this.GetComponent<RectTransform>().rect.Contains(Input.mousePosition)) {
    Do you have any ideas (or a best way) to do this ?

    Thanks for your attention !
     
    Last edited: Jun 14, 2017
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,395
    Your basic concept for a double-click is in the right direction. However, when using the UI, you should use the UI functions, not the standard Input classes.

    In this case, I'd make your script an EventTrigger component. Add a method for OnPointerDown that applies basically the same logic as your DoubleClick function uses. It's a mindset shift to switch from bool-based logic to event-driven logic, but you'll find yourself much better off ultimately.

    If you need help beyond the code samples on that page, ask away.
     
    Mayo54 likes this.
  3. Mayo54

    Mayo54

    Joined:
    Jun 8, 2017
    Posts:
    32
    Is it possible to create an EventTrigger by script ?

    because every UI things in my game are create with scripts... So I think I have to create the EventTrigger with a script.
     
  4. Mayo54

    Mayo54

    Joined:
    Jun 8, 2017
    Posts:
    32
    Ok I did this :

    Code (CSharp):
    1. public class GestionClickInventaire : MonoBehaviour, IPointerDownHandler
    2. {
    3.     float clicked = 0;
    4.     float clicktime = 0;
    5.     float clickdelay = 0.5f;
    6.  
    7.     public void OnPointerDown(PointerEventData data)
    8.     {
    9.         clicked++;
    10.         if (clicked == 1) clicktime = Time.time;
    11.  
    12.         if (clicked > 1 && Time.time - clicktime < clickdelay)
    13.         {
    14.             clicked = 0;
    15.             clicktime = 0;
    16.             Debug.Log("Double CLick: "+this.GetComponent<RectTransform>().name);
    17.  
    18.         }
    19.         else if (clicked > 2 || Time.time - clicktime > 1) clicked = 0;
    20.  
    21.     }
    22.  
    23.  
    24.  
    25. }
    26.  
    It works perfectly, but is it the best way to do it ?
     
  5. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,395
    That's how I would recommend doing this, yes. Nicely done!
     
    ConorL and Mayo54 like this.
  6. Randy-Edmonds

    Randy-Edmonds

    Joined:
    Oct 10, 2005
    Posts:
    1,120
    Try this...
    Code (CSharp):
    1.  
    2.     public virtual void OnPointerClick(PointerEventData eventData)
    3.     {
    4.         if (eventData.clickCount == 2) {
    5.             Debug.Log ("double click");
    6.         }
    7.     }
    8.  
     
    Last edited: Sep 10, 2017
  7. Linix

    Linix

    Joined:
    Jul 14, 2012
    Posts:
    4
    vamidi16 likes this.
  8. rnnicoletti

    rnnicoletti

    Joined:
    Sep 27, 2017
    Posts:
    4
    When I have used this solution in the past, it doesn't work, because if you are trying to interpret single clicks and double clicks on the same component, then you get two events fired: one with eventData.clickCount == 1, and one with eventData.clickCount == 2. This has always prevented this answer from working for me, because inevitably when I want to detect double clicks, it's because there is already a single click handler I'm using on that component. It took me hours of fiddling with this the first time to discover that's why my logic didn't work right.
     
    thom66 likes this.
  9. Capricornum

    Capricornum

    Joined:
    Feb 1, 2018
    Posts:
    22
    Randy-Edmonds suggestion works fine for me. Thank you for it.
    I am using it to flip objects on some user defined amount of clicks:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3. using UnityEngine.UI;
    4.  
    5. public class Flippable : MonoBehaviour, IPointerClickHandler
    6. {
    7.     Image myImage;
    8.     Sprite face;
    9.     public Sprite back;
    10.     public int clickCount = 1;
    11.  
    12.     bool _faceFront = true;
    13.     bool FaceFront {
    14.         get { return _faceFront; }
    15.         set { _faceFront = value; if (value) myImage.sprite = face; else myImage.sprite = back; }
    16.     }
    17.  
    18.     private void Start() {
    19.         myImage = GetComponent<Image>();
    20.         face = myImage.sprite;
    21.     }
    22.  
    23.     public void OnPointerClick(PointerEventData eventData) {
    24.         if (eventData.clickCount >= clickCount) {
    25.             FlipCard();
    26.         }
    27.     }
    28.  
    29.     void FlipCard() {
    30.         FaceFront = !FaceFont;
    31.     }
    32. }
    33.  
     
  10. Dominus12

    Dominus12

    Joined:
    Jul 30, 2017
    Posts:
    1
    Thank you guys, Randy-Edmonds solution worked for me partially, it detected double clicks using mouse but only detected a single click with touch Input (I was using an iPad with Unity Remote). However, the code provided by Mayo54 in this comment:

    Worked for me for both mouse click and touch input, I used OnPointerClick instead of OnPointerDown but it should work anyway.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3.  
    4. public class MultipleClick : MonoBehaviour, IPointerClickHandler
    5. {
    6.     //[SerializeField] private int numberOfClicks = 2;
    7.  
    8.     float clicked = 0;
    9.     float clicktime = 0;
    10.     float clickdelay = 0.5f;
    11.  
    12.     public void OnPointerClick(PointerEventData eventData)
    13.     {
    14.         /*
    15.         if (eventData.clickCount == numberOfClicks)
    16.         {
    17.             Debug.Log("double click");
    18.         }
    19.         */
    20.  
    21.         // Detecting double click
    22.         clicked++;
    23.  
    24.         if (clicked == 1)
    25.             clicktime = Time.time;
    26.  
    27.         if (clicked > 1 && Time.time - clicktime < clickdelay)
    28.         {
    29.             // Double click detected
    30.             clicked = 0;
    31.             clicktime = 0;
    32.             Debug.Log("Double Click: " + this.GetComponent<RectTransform>().name);
    33.         }
    34.         else if (clicked > 2 || Time.time - clicktime > 1)
    35.             clicked = 0;
    36.     }
    37. }
    38.  
     
    Last edited: Feb 26, 2020
    Futurristic and Ghosthowl like this.
  11. NexusDIS

    NexusDIS

    Joined:
    Mar 9, 2020
    Posts:
    1
    The above examples work if a user performs a "clean" double click as expected. I believe the logic could fail however if the user does something more unexpected such as a single click followed by a double click.

    This expression needs more work. I don't believe it will respond correctly to every usecase:
    Code (CSharp):
    1. else if (clicked > 2 || Time.time - clicktime > 1)
    2.             clicked = 0;
    The following example is possibly easier to understand and code and as such doesn't suffer from logical errors. If you don't need the single click just don't add any code to the method.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Timers;
    3.  
    4. public class DoubleClick : MonoBehaviour
    5. {
    6.     private readonly Timer _MouseSingleClickTimer = new Timer();
    7.  
    8.     // Start is called before the first frame update.
    9.     void Start()
    10.     {
    11.         _MouseSingleClickTimer.Interval = 400;
    12.         _MouseSingleClickTimer.Elapsed += SingleClick;
    13.  
    14.     }
    15.  
    16.     void SingleClick(object o, System.EventArgs e)
    17.     {
    18.         _MouseSingleClickTimer.Stop();
    19.  
    20.         System.Diagnostics.Debug.WriteLine("Single Click");
    21.         //Do your stuff for single click here....
    22.     }
    23.  
    24.     private void Update()
    25.     {
    26.         if (Input.GetMouseButtonDown(0))
    27.         {
    28.             if (_MouseSingleClickTimer.Enabled == false)
    29.             {
    30.                 // ... timer start
    31.                 _MouseSingleClickTimer.Start();
    32.                 // ... wait for double click...
    33.                 return;
    34.             }
    35.             else
    36.             {
    37.                 //Doubleclick performed - Cancel single click
    38.                 _MouseSingleClickTimer.Stop();
    39.  
    40.                 //Do your stuff here for double click...
    41.                 System.Diagnostics.Debug.WriteLine("Double Click");
    42.             }
    43.         }
    44.     }
    45. }
    46.  
     
    Last edited: Mar 12, 2020
  12. Full_Tilt

    Full_Tilt

    Joined:
    Apr 25, 2018
    Posts:
    15
    This works very well for me. I would like to try and detect long clicks with this as well, so I'll give it a try. Thank you for the nice code.
     
  13. jfa257

    jfa257

    Joined:
    Jul 17, 2013
    Posts:
    8
    Previous implementation to Randy-Edmonds is nice, since if you dont have/know of the clickCount property, its a clean implementation. However, I just implemented it, the Randy-Edmonds solution and it's the way to go. If your logic it's not working, the problem it's in your logic, or the analysis of the results. These both solutions should be implemented inside OnPointerClick. OnPointerDown will wait a bit to raise the event, since its for detecting MouseDown like events. So if you expect a "clean" double click using OnPointerDown, sit down, light your best cigar, and get yourself comfy on the bench because that train is not coming. Please let's try to properly analize user cases to create great UI logic, instead of justify our own mistakes.
     
    Last edited: Jun 26, 2020
  14. maurofuentes

    maurofuentes

    Joined:
    Apr 5, 2018
    Posts:
    7
  15. Georodin

    Georodin

    Joined:
    Jun 10, 2021
    Posts:
    2
    Hey guys,
    what would be the approach if just want to detect a double-click of the middle mouse button anywhere in the scene?
    To trigger reset the camera rotation.
     
  16. raniaaaaa

    raniaaaaa

    Joined:
    Aug 24, 2021
    Posts:
    1
     
unityunity