Tuesday, June 20, 2017

unity - Detect Triple click


I need a triple click detection on my 2d game.


I have a Canvas with a fire button inside and I'm searching a way to trigger 3 different actions on the player's script when I press this button once, twice, or three times.


There's a similar question about detecting double-clicks in Unity, but it wasn't clear which of the seven different answers would be appropriate for this situation, or how to extend them to handle three clicks.



Answer



The basic strategy here is to set up our script to remember it was clicked (some number of times), but delay acting on that information for a short period of time, in case another click immediately follows.


For UI elements in a Canvas, the most robust method is probably to use a variation on Norb's answer for the double-click case. This leverages the Unity UI event system to give a lot of flexibility.


For this, we put a custom ClickController script on each UI object we want to be able to react to multiple click types - make sure they have their "Raycast Target" box checked so they're visible to mouse events:


using UnityEngine;

using UnityEngine.Events;
using UnityEngine.EventSystems;
using System.Collections;

public class ClickController : MonoBehaviour
{
// Configure how we listen for clicks (how fast, which button).

[Tooltip("Seconds after each click to wait for a follow-up")]
public float timeLimit = 0.25f;


[Tooltip("Which mouse/stylus button to react to")]
public PointerEventData.InputButton button;

// Expose events we can wire-up in the inspector to our desired handlers.

// I added this so we can use it to show visible feedback immediately
// on the first click, to minimize perceived latency.
[System.Serializable]
public class OnAtLeastOneClick : UnityEvent { };

public OnAtLeastOneClick onAtLeastOneClick;

[System.Serializable]
public class OnSingleClick : UnityEvent { };
public OnSingleClick onSingleClick;

[System.Serializable]
public class OnDoubleClick : UnityEvent { };
public OnDoubleClick onDoubleClick;


[System.Serializable]
public class OnTripleClick : UnityEvent { };
public OnTripleClick onTripleClick;

// Internal state for keeping track of clicks.

private int clickCount;
private Coroutine delayedClick;

// Auto-configure on Start.

// I added this to reduce fiddly inspector setup - we'll use an existing
// EventTrigger component if it's there, or add one & wire it up if not.
void Start()
{
EventTrigger trigger = GetComponent();
if (trigger == null)
trigger = gameObject.AddComponent();

var entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerClick;

entry.callback.AddListener(onClick);

trigger.triggers.Add(entry);
}

// Main click handler - this is where the magic happens.
public void onClick(BaseEventData data)
{
PointerEventData pointerData = data as PointerEventData;


// Ignore clicks on buttons we're not watching.
if (this.button != pointerData.button)
return;

// Count up the clicks.
clickCount++;

// React accordingly.
switch(clickCount)
{

// First click: fire OnAtLeastOneClick and wait to see if a second comes in.
case 1:
delayedClick = StartCoroutine(DelayClick(onSingleClick, timeLimit));
onAtLeastOneClick.Invoke();
break;
// Second click: cancel single-click and wait to see if a third comes in.
case 2:
StopCoroutine(delayedClick);
delayedClick = StartCoroutine(DelayClick(onDoubleClick, timeLimit));
break;

// Third click: cancel double-click fire OnTripleClick immediately.
case 3:
StopCoroutine(delayedClick);
delayedClick = null;
onTripleClick.Invoke();
clickCount = 0;
break;
}
}


// This handles firing off the click after a delay.
// We cancel it if a new click comes in sooner.
private IEnumerator DelayClick(UnityEvent clickEvent, float delay)
{
yield return new WaitForSeconds(delay);
// This coroutine didn't get stopped, so no new click came in.
// Fire off the corresponding click event and reset.
clickEvent.Invoke();
clickCount = 0;
delayedClick = null;

}
}

The script looks like this when attached, with four events you can link-up to call public methods on any component in your scene:


Example of this script's interface in the Unity Inspector


As described in the comments above, I added the OnAtLeastOneClick method so we have something we can call right away as soon as the player interacts (triggering this OnPress would also work if you need it even sooner). Firing off some visual and audio feedback at that moment can help cover the waiting period for the next click to come in, helping the interaction feel snappy & responsive. Since at this moment we don't yet know whether this will be a single-, double-, or triple-click, the feedback has to work for all three, and more specialized feedback can be triggered a fraction of a second later once we know for sure what input action we're dealing with.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...