Tek tıklamaları düzgün bir şekilde ayırt etmek ve C # kullanarak Unity3D'de çift tıklamak nasıl?


15

Burada anlamaya ve öğrenmeye çalıştığım şey çok temel bir şey: düzgün bir şekilde en genel ve en cilalı yol nedir C # kullanarak Unity'deki 3 ana fare düğmesi için tek tıklamaları ve çift tıklamaları ayırt nedir?

Kelimeye doğru şekilde vurgu yapıyorum hiçbir şey için değil. Bu web sitesinde daha önce hiç sorulmamış gibi görünmeme şaşırmış olsam da, elbette Unity'nin forumlarında ve Unity'nin kendi soru cevap sayfalarında çok sayıda benzer soru araştırdım ve buldum. Bununla birlikte, bu kaynaklarda gönderilen cevaplarda sıklıkla olduğu gibi, hepsi ya basit yanlış ya da en azından biraz amatörce görünüyordu.

Açıklamama izin ver. Önerilen çözümler her zaman iki yaklaşımdan birine ve ilgili kusurlarına sahipti:

  1. bir tıklama her gerçekleştiğinde, son tıklamadan bu yana geçen zaman deltasını hesaplayın ve belirli bir eşikten küçükse, çift tıklama algılanır. Ancak bu çözüm, çift tıklama algılanmadan önce hızlı bir tek tıklamanın algılanmasıyla sonuçlanır; bu, hem tek hem de çift tıklama eylemlerini etkinleştirdiği için çoğu durumda istenmeyen bir durumdur.

Kaba örnek:

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. tek tıklamanın etkinleştirilmesi için bir bekleme gecikmesi ayarlayın, yani her tıklamada tek tıklama işlemlerini yalnızca son tıklamadan sonraki gecikme süresi belirli bir eşiğin üzerindeyse etkinleştirin. Bununla birlikte, bu, yavaş sonuçlar verir, çünkü daha önce bir bekleme, tek tıklamalar algılanmadan önce beklemeyi fark etmek mümkündür. Daha da kötüsü, bu tür bir çözüm, makineler arasındaki tutarsızlıklardan muzdariptir, çünkü kullanıcının işletim sistemi düzeyinde çift tıklama hızı ayarını ne kadar yavaş veya ne kadar hızlı dikkate almaz.

Kaba örnek:

   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;
       }

   }

Bu nedenle, sorularım şöyle olur. Bu çözümlerin dışında C # kullanarak Unity'de çift tıklamayı saptamanın ve yukarıda bahsedilen sorunları ele alan daha parlak ve güvenilir bir yol var mı?


Hmm. Çift tıklamaları doğru bir şekilde tespit etmek ... Sadece bunu nasıl yapacağımı düşünebilirim , muhtemelen daha az amatörce değil.
Draco18s artık SE

2
Tıklamaları daha iyi tespit edebileceğinizi sanmıyorum - kullanıcının fiziksel işleminin sonuçlarını * tahmin edemezsiniz. GUI ve mekaniğimi tasarlamaya odaklanmayı tercih ederim, böylece tetiklenmiş eylemler gerçeği olabildiğince iyi gizler. * iyi, teorik olarak, bazı AI analiz kullanım davranışını uygulamak, ancak sonuçlar tutarlı olmaz
wondra

1
Cevap 1'e atıfta bulunarak, "Ancak bu çözüm, çift tıklama algılanmadan önce hızlı bir tek tıklamanın algılanmasına neden olur, bu da istenmeyen bir durumdur." Ne aradığını gerçekten göremiyorum. Bu çözümle ilgili bir sorun varsa, bence tweaking gerektiren oyun mekaniği. Örneğin bir RTS'yi ele alalım. Bir birimi seçmek için tıklatıyorsunuz (A eylemi), diğer tüm birimleri seçmek için çift tıklayın (B eylemi). Çift tıklarsanız, sadece B eylemini istemezsiniz, hem A hem de B'yi istersiniz. Tamamen farklı bir şeyin olmasını istiyorsanız, sağ tıklama veya ctrl + tıklama vb.
Peter

Yanıtlar:


13

Coroutines eğlencelidir:

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");
}

}

Bu, tek bir düğmeye basıldığında algılanmıyor gibi görünüyor; sadece ekran tıklandığında veya genel olarak çift tıklandığında (OP bunu istememiş olsa da, bu kadar çok sormak adil değildir). Bunu nasıl yapabileceğinize dair herhangi bir öneriniz var mı?
zavtra

Bunun soruda önerilen ikinci yöntemden ne kadar farklı olduğunu görmüyorum.
John Hamilton

5

İngilizce okuyamıyorum. Ancak UniRx size yardımcı olabilir.

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"));
}

Çalışır, ancak tam olarak Windows'da çift tıklama uygulandığı gibi değildir. Örneğin, arka arkaya 6 kez tıkladığınızda, yalnızca bir çift tıklama üretilir. Windows'da 3 çift tıklama olacaktır. Deneyebilirsiniz Control Panel → Mouse.
Maxim Kamalov

2

Windows varsayılan çift tıklama gecikmesi 500 ms = 0,5 saniyedir.

Genellikle bir oyunda, küresel olarak değil, tıklatılan denetimi çift tıklatmak çok daha kolaydır. Küresel olarak çift tıklamayı ele almaya karar verirseniz, çok istenmeyen 2 yan etkiyi önlemek için geçici çözümler uygulamanız gerekir:

  1. Her bir tıklama 0,5 saniye geciktirilebilir.
  2. Bir kontrole tek bir tıklama ve ardından başka bir kontrole tek bir tıklama, ikinci kontrole çift tıklama olarak yanlış yorumlanabilir.

Küresel bir uygulama kullanmanız gerekiyorsa, tıklamanın konumuna da bakmanız gerekir - fare çok ileri hareket ederse, çift tıklama değildir. Normalde, odak her değiştiğinde çift tıklama sayacını sıfırlayan bir odak izleyici uygularsınız.

Çift tıklama zaman aşımı süresi geçene kadar beklemeniz gerekiyorsa, bunun her kontrol için farklı şekilde ele alınması gerekir. 4 farklı etkinliğiniz var:

  1. Hover.
  2. Çift tıklama olabilecek veya olmayabilecek tek bir tıklama.
  3. Çift tıklama.
  4. Çift tıklama zaman aşımı, çift tıklama olmamak için tek tıklama onaylandı.

Mümkün olduğunda, GUI etkileşimlerini son olay kullanılmayacak şekilde tasarlamaya çalışın: Windows Dosya Gezgini hemen tek bir tıklamayla bir öğe seçecek ve çift tıklamayla açılacak ve onaylanan tek bir öğede hiçbir şey yapmayacak Tıklayın.

Başka bir deyişle: Denetimin istenen davranışına bağlı olarak, önerdiğiniz her iki seçeneği de kullanırsınız.


2

Yukarıdaki çözümler tüm ekranda yapılan tıklamalar için geçerlidir. Tek tek düğmelere çift tıklamayı tespit etmek istiyorsanız, yukarıdaki bir cevaba yapılan bu değişikliğin iyi çalıştığını gördüm:

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");
    }
}

Bu komut dosyasını istediğiniz sayıda düğmeye ekleyin. Ardından düzenleyicinin Tıklandığında bölümünde (bunu eklediğiniz her düğme için), '+' düğmesini tıklayın, düğmeyi oyun nesnesi olarak kendisine ekleyin ve ardından çağrılacak işlev olarak "DoubleClickTest -> startClick () 'yi seçin düğmesine basıldığında.


1

Uygun bir tek-çift tıklama işleyicisi arıyordum. Bu konuyu buldum ve gerçekten iyi fikirler topladım. Sonunda aşağıdaki çözümü buldum ve sizinle paylaşmak istiyorum. Umarım bu yöntemi faydalı bulursunuz.

Unity'nin müfettişini kullanmak için harika bir yaklaşım olduğunu düşünüyorum, çünkü bu şekilde oyun nesnelerinize görsel ve güvenli bir şekilde başvurabileceğiniz için kodunuz daha esnektir.

Önce Event System bileşenini oyun nesnelerinize ekleyin. Bu, Bağımsız Giriş Modülü bileşenini de ekleyecektir . Bu, fare tıklamaları ve dokunmaları gibi giriş olaylarıyla ilgilenir. Bunu tek bir oyun nesnesinde yapmanızı tavsiye ederim.

Ardından Etkinlik Tetikleyici bileşenini, raycast özellikli oyun nesnelerinize ekleyin . Bir UI öğesi veya hareketli grafik gibi, çarpıştırıcılı bir 3D nesne . Bu, tıklama etkinliğini komut dosyamıza iletir. Pointer Click olay türünü ekleyin ve üzerinde Click Controller komut dosyası bileşenine sahip alıcı oyun nesnesini ayarlayın ve son olarak onClick () yöntemini seçin. (Ayrıca, bir Güncelleme () içindeki tüm tıklamaları almak için komut dosyasını değiştirebilir ve bu adımı atlayabilirsiniz.)

Dolayısıyla, olay tetikleyicisine sahip olabilecek alıcı oyun nesnesi, tek veya çift tıklama olaylarıyla her şeyi yapar.

Çift tıklama için zaman sınırını , istenen düğmeyi ve tek ve çift tıklama için özel etkinlikleri ayarlayabilirsiniz . Tek sınırlama , bir oyun nesnesi için bir kerede yalnızca bir düğme seçebilmenizdir .

resim açıklamasını buraya girin

Komut dosyasının kendisi, iki zamanlayıcı ile her ilk tıklamada bir coroutine başlatır. FirstClickTime ve currentTime, birincisiyle senkronize olur.

Bu koroutin, zaman sınırının süresi doluncaya kadar her karenin sonunda bir kez döner. Bu arada onClick () yöntemi tıklamaları saymaya devam eder.

Zaman sınırı sona erdiğinde, clickCounter'a göre single- veya doubleclick olayını çağırır. Daha sonra bu sayacı sıfıra ayarlar, böylece koroutin bitirir ve tüm süreç baştan başlar.

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;
            }
        }
    }
}

Evet, ilk yazım biraz daha bilgilendirici olmalı. Bu yüzden ayrıntılı bir açıklama ekledim ve senaryoyu biraz geliştirdim. İsterseniz -1'inizi kaldırın.
Norb

0

Sanırım kullanıcının zihnini okumak için tek ya da çift tıklayacak bir yol yok, sonuçta girişi beklemek zorundayız. Her ne kadar ikinci tıklama için 0.01 saniye (kadar düşük) bekleme ayarlayabilirsiniz. Bu bakımdan bekleme seçeneğini tercih ediyorum.

Ve EVET Coroutineseğlencelidir ...

Bir alternatif

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;
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.