C #을 사용하여 Unity3D에서 한 번 클릭과 두 번 클릭을 올바르게 구별하는 방법은 무엇입니까?


15

여기에서 이해하고 배우려고하는 것은 매우 기본적인 것입니다 .C #을 사용하여 Unity의 3 가지 주요 마우스 버튼에 대한 단일 클릭과 이중 클릭 을 올바르게 구별 하는 가장 일반적이고 가장 세련된 방법은 무엇입니까?

단어에 내 중점을 제대로 아무것도 아니다. 이전에이 웹 사이트에서이 질문을 한 적이없는 것 같습니다. 물론 Unity 포럼과 Unity 자체의 질문-답변 페이지에서 비슷한 질문을 많이 검색했습니다. 그러나 이러한 리소스에 답변이 게시 된 경우가 많기 때문에 모두 명백한 잘못이거나 최소한 너무 아마추어적인 것 같습니다.

설명하겠습니다. 제안 된 솔루션에는 항상 두 가지 접근 방식과 해당 결함이 있습니다.

  1. 클릭이 발생할 때마다 마지막 클릭 이후 시간 델타를 계산하고 주어진 임계 값보다 작 으면 더블 클릭이 감지됩니다. 그러나이 솔루션을 사용하면 더블 클릭이 감지되기 ​​전에 빠른 싱글 클릭이 감지되므로 싱글 클릭과 더블 클릭 동작이 모두 활성화되므로 대부분의 상황에서 바람직하지 않습니다.

대략적인 예 :

float dclick_threshold = 0.25f;
double timerdclick = 0;

if (Input.GetMouseButtonDown(0) 
{
   if (Time.time - timerdclick) > dclick_threshold)
   {
       Debug.Log("single click");
       //call the SingleClick() function, not shown
   } else {
       Debug.Log("double click");
       //call the DoubleClick() function, not shown
   }
   timerdclick = Time.time;
}
  1. 단일 클릭이 활성화 될 때까지 대기 지연을 설정합니다. 즉, 모든 클릭에서 마지막 클릭 이후 시간 지연이 지정된 임계 값을 초과하는 경우 단일 클릭 동작 만 활성화합니다. 그러나 한 번의 클릭이 감지되기 ​​전에 대기하는 것을 알 수 있기 때문에 결과가 느리게 나타납니다. 그보다 더 나쁜 것은 이러한 종류의 솔루션은 OS 수준에서 사용자의 두 번 클릭 속도 설정이 얼마나 느리거나 얼마나 빠르지 않기 때문에 여러 시스템에서 불일치가 발생한다는 것입니다.

대략적인 예 :

   float one_click = false;
   float dclick_threshold = 0.25f;
   double timerdclick = 0;

   if (one_click && ((Time.time - timerdclick) > dclick_threshold))
   {
       Debug.Log("single click");
       //call the SingleClick() function, not shown
       one_click = false;
   }

   if (Input.GetMouseButtonDown(0))
   {

       if (!one_click)
       {
           dclick = -1;
           timerdclick = Time.time;
           one_click = true;
       }

       else if (one_click && ((Time.time - timerdclick) < dclick_threshold))
       {
           Debug.Log("double click");
           //call the DoubleClick() function, not shown
           one_click = false;
       }

   }

따라서 제 질문은 다음과 같습니다. 이러한 솔루션 외에 C #을 사용하여 Unity에서 더블 클릭을 감지하고 앞에서 언급 한 문제를 처리하는보다 세련되고 안정적인 방법이 있습니까?


흠. 제대로 더블 클릭을 검출 ... 난 단지 방법에 대해 생각할 수있는 내가 줄 아마 덜 아마추어하지 않은, 그 일에 대해 이동합니다.
Draco18s는 더 이상

2
클릭을 더 잘 감지 할 수 있다고 생각 하지 않습니다. 사용자의 물리적 행동 결과를 예측할 수 없습니다. 오히려 트리거 된 동작이 사실을 최대한 숨길 수 있도록 GUI와 메커니즘을 디자인하는 데 집중하고 싶습니다. * 이론 상으로는 AI 분석 사용 동작을 구현할 수 있지만 결과는 일관성이 없습니다.
wondra

1
그러나 답변 1을 참조하면 "이 솔루션을 사용하면 더블 클릭이 감지되기 ​​전에 빠른 단일 클릭이 감지되므로 바람직하지 않습니다." 나는 당신이 찾고있는 것을 실제로 보지 못합니다. 이 솔루션에 문제가 있다면, 조정이 필요한 게임 메커니즘이라고 생각합니다. 예를 들어 RTS를 보자. 유닛을 클릭하여 선택하고 (액션 A), 더블 클릭하여 다른 모든 유닛을 선택하십시오 (액션 B). 두 번 클릭하면 작업 B 만 원하지 않고 A와 B를 모두 원합니다. 완전히 다른 것을 원하면 오른쪽 클릭 또는 ctrl + 클릭 등이 필요합니다.
Peter

답변:


13

코 루틴은 재미있다 :

using UnityEngine;
using System.Collections;

public class DoubleClickTest : MonoBehaviour 
{
private float doubleClickTimeLimit = 0.25f;

protected void Start()
{
    StartCoroutine(InputListener());
}

// Update is called once per frame
private IEnumerator InputListener() 
{
    while(enabled)
    { //Run as long as this is activ

        if(Input.GetMouseButtonDown(0))
            yield return ClickEvent();

        yield return null;
    }
}

private IEnumerator ClickEvent()
{
    //pause a frame so you don't pick up the same mouse down event.
    yield return new WaitForEndOfFrame();

    float count = 0f;
    while(count < doubleClickTimeLimit)
    {
        if(Input.GetMouseButtonDown(0))
        {
            DoubleClick();
            yield break;
        }
        count += Time.deltaTime;// increment counter by change in time between frames
        yield return null; // wait for the next frame
    }
    SingleClick();
}


private void SingleClick()
{
    Debug.Log("Single Click");
}

private void DoubleClick()
{
    Debug.Log("Double Click");
}

}

개별 버튼을 누를 때 감지되지 않는 것 같습니다. 일반적으로 화면을 클릭하거나 더블 클릭하면 OP가 요청하지 않았지만 그렇게 많이 요구하는 것은 불공평합니다. 어떻게 할 수 있는지에 대한 제안이 있으십니까?
zavtra

나는 이것이 질문에서 제안 된 두 번째 방법과 어떻게 다른지 알지 못합니다.
존 해밀턴

5

나는 영어를 읽을 수 없습니다. 그러나 UniRx가 도움이 될 수 있습니다.

https://github.com/neuecc/UniRx#introduction

void Start () {

    var clickStream = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0));
    var bufferStream = clickStream.Buffer (clickStream.Throttle (TimeSpan.FromMilliseconds (250))).Publish ().RefCount ();
    bufferStream.Where (xs => xs.Count == 1).Subscribe (_ => Debug.Log ("single click"));
    bufferStream.Where (xs => xs.Count >= 2).Subscribe (_ => Debug.Log ("double click"));
}

작동하지만 Windows에서 두 번 클릭이 구현되는 것과 똑같지는 않습니다. 예를 들어 연속으로 6 번 클릭하면 한 번의 더블 클릭 만 생성됩니다. Windows에서는 3 번의 더블 클릭입니다. 당신은 시도 할 수 있습니다 Control Panel → Mouse.
Maxim Kamalov

2

Windows 기본 더블 클릭 지연은 500ms = 0.5 초입니다.

일반적으로 게임에서는 전역이 아닌 클릭되는 컨트롤을 더블 클릭하는 것이 훨씬 쉽습니다. 전 세계에서 두 번 클릭을 처리하기로 결정한 경우 매우 바람직하지 않은 2 가지 부작용을 피하려면 해결 방법을 구현해야합니다.

  1. 모든 클릭은 0.5 초 지연 될 수 있습니다.
  2. 한 컨트롤을 한 번 클릭 한 다음 다른 컨트롤을 한 번 클릭하면 두 번째 컨트롤을 두 번 클릭하는 것으로 잘못 해석 될 수 있습니다.

전역 구현을 사용해야하는 경우 클릭 위치도 확인해야합니다. 마우스가 너무 멀리 이동 한 경우 더블 클릭이 아닙니다. 일반적으로 포커스가 변경 될 때마다 더블 클릭 카운터를 재설정하는 포커스 추적기를 구현합니다.

더블 클릭 타임 아웃이 끝날 때까지 기다려야하는 경우 각 컨트롤마다 다르게 처리해야합니다. 4 가지 이벤트가 있습니다 :

  1. 호버.
  2. 한 번의 클릭으로 더블 클릭이 될 수도 있고 아닐 수도 있습니다.
  3. 더블 클릭.
  4. 더블 클릭 시간 초과, 싱글 클릭은 더블 클릭이 아닌 것으로 확인되었습니다.

가능하면 마지막 이벤트가 사용되지 않도록 GUI 상호 작용을 설계하려고합니다. Windows 파일 탐색기는 한 번의 클릭으로 항목을 즉시 선택하고 두 번 클릭하면 항목을 선택하고 확인 된 단일 항목에서는 아무 것도 수행하지 않습니다. 딸깍 하는 소리.

다시 말해 : 원하는 컨트롤 동작에 따라 제안한 두 옵션을 모두 사용합니다.


2

위의 솔루션은 전체 화면에서 발생한 클릭에 적용됩니다. 개별 버튼에서 두 번 클릭을 감지하려면 위의 답변에 대한 수정 사항이 효과적입니다.

using UnityEngine;
using System.Collections;

public class DoubleClickTest : MonoBehaviour
{
    private float doubleClickTimeLimit = 0.35f;
    bool clickedOnce = false;
    public string singleTapMove;
    public string doubleTapMove;
    float count = 0f;

    public void startClick(){
        StartCoroutine (ClickEvent ());
    }

    public IEnumerator ClickEvent()
    {
        if (!clickedOnce && count < doubleClickTimeLimit) {
            clickedOnce = true;
        } else {
            clickedOnce = false;
            yield break;  //If the button is pressed twice, don't allow the second function call to fully execute.
        }
        yield return new WaitForEndOfFrame();

        while(count < doubleClickTimeLimit)
        {
            if(!clickedOnce)
            {
                DoubleClick();
                count = 0f;
                clickedOnce = false;
                yield break;
            }
            count += Time.deltaTime;// increment counter by change in time between frames
            yield return null; // wait for the next frame
        }
        SingleClick();
        count = 0f;
        clickedOnce = false;
    }
    private void SingleClick()
    {
        Debug.Log("Single Click");
    }

    private void DoubleClick()
    {
        Debug.Log("Double Click");
    }
}

이 스크립트를 원하는만큼 버튼에 첨부하십시오. 그런 다음 편집기의 클릭시 섹션 (이 버튼을 부착 한 각 버튼에 대해)에서 '+'를 클릭하고 버튼을 게임 객체로 추가 한 다음 호출 할 함수로 "DoubleClickTest-> startClick ()"을 선택하십시오. 버튼을 누를 때


1

적절한 단일 더블 클릭 핸들러를 찾고있었습니다. 나는이 주제를 발견하고 정말 좋은 아이디어를 모았습니다. 마지막으로 다음 솔루션을 생각해 냈으며 여러분과 공유하고 싶습니다. 이 방법이 도움이 되길 바랍니다.

Unity의 인스펙터를 사용하는 것이 좋은 방법이라고 생각합니다.이 방법으로 게임 객체를 시각적으로 안전하게 참조 할 수 있기 때문에 코드가 더 유연합니다.

먼저 이벤트 시스템 구성 요소를 게임 오브젝트에 추가하십시오 . 또한 독립형 입력 모듈이 추가됩니다 구성 요소가 . 마우스 클릭 및 터치와 같은 입력 이벤트를 처리합니다. 개별 게임 개체에서이 작업을 수행하는 것이 좋습니다.

다음으로 Raycast 가능 게임 오브젝트에 Event Trigger 구성 요소를 추가하십시오 . UI 요소 또는 스프라이트 처럼 collider가 있는 3D 객체 입니다 . 클릭 이벤트가 스크립트로 전달됩니다. 포인터 클릭 이벤트 유형을 추가하고 클릭 컨트롤러 스크립트 컴포넌트 가있는 리시버 게임 오브젝트를 설정 하고 마지막으로 onClick ()을 선택하십시오. 메소드를 . 또한 Update ()에서 모든 클릭을 수신하도록 스크립트를 변경하고이 단계를 건너 뛸 수 있습니다.

따라서 이벤트 트리거가있는 수신자 게임 오브젝트는 단일 또는 이중 클릭 이벤트로 무엇이든 수행합니다.

더블 클릭, 원하는 버튼 및 싱글 및 더블 클릭에 대한 사용자 정의 이벤트시간 제한 을 설정할 수 있습니다 . 유일한 제한 사항하나의 게임 오브젝트에 대해 한에 하나의 버튼 만 선택할 수 있다는 것 입니다.

여기에 이미지 설명을 입력하십시오

스크립트 자체는 두 번의 타이머로 처음 클릭 할 때마다 코 루틴을 시작합니다. firstClickTime 및 currentTime은 첫 번째와 동기화됩니다.

이 코 루틴은 시간 제한이 만료 될 때까지 모든 프레임의 끝에서 한 번 반복됩니다. 한편 onClick () 메소드는 계속 클릭 수를 계산합니다.

시간 제한이 만료되면 clickCounter에 따라 싱글 또는 더블 클릭 이벤트를 호출합니다. 그런 다음이 카운터를 0으로 설정하여 코 루틴이 완료되고 전체 프로세스가 처음부터 시작되도록합니다.

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

public class ClickController : MonoBehaviour
{
    public float timeLimit = 0.25f;
    public PointerEventData.InputButton button;

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

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

    private int clickCount;
    private float firstClickTime;
    private float currentTime;

    private ClickController () {
        clickCount = 0;
    }

    public void onClick (BaseEventData data) {

        PointerEventData pointerData = data as PointerEventData;

        if (this.button != pointerData.button) {
            return;
        }

        this.clickCount++;

        if (this.clickCount == 1) {
            firstClickTime = pointerData.clickTime;
            currentTime = firstClickTime;
            StartCoroutine (ClickRoutine ());
        }
    }

    private IEnumerator ClickRoutine () {

        while (clickCount != 0)
        {
            yield return new WaitForEndOfFrame();

            currentTime += Time.deltaTime;

            if (currentTime >= firstClickTime + timeLimit) {
                if (clickCount == 1) {
                    onSingleClick.Invoke ();
                } else {
                    onDoubleClick.Invoke ();
                }
                clickCount = 0;
            }
        }
    }
}

글쎄, 내 첫 번째 게시물은 좀 더 유익해야합니다. 그래서 자세한 설명을 추가하고 스크립트를 약간 수정했습니다. 원한다면 -1을 제거하십시오.
Norb

0

사용자의 마음을 읽을 수있는 방법이 없다고 생각합니다. 입력을 기다려야 할 때마다 싱글 또는 더블 클릭 할 것입니다. 두 번째 클릭에 대해 대기 시간을 0.01 초 (낮음)로 설정할 수 있습니다. 이와 관련하여 대기 옵션을 사용하겠습니다.

그리고 그렇습니다 Coroutines 재미 있습니다 ...

대안

float _threshold = 0.25f;

void Start ()
{
    StartCoroutine ("DetectTouchInput");
}

IEnumerator DetectTouchInput ()
{
    while (true) {
        float duration = 0;
        bool doubleClicked = false;
        if (Input.GetMouseButtonDown (0)) {
            while (duration < _threshold) {
                duration += Time.deltaTime;
                yield return new WaitForSeconds (0.005f);
                if (Input.GetMouseButtonDown (0)) {
                    doubleClicked = true;
                    duration = _threshold;
                    // Double click/tap
                    print ("Double Click detected");
                }
            }
            if (!doubleClicked) {
                // Single click/tap
                print ("Single Click detected");
            }
        }
        yield return null;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.