Nie lubię odpowiadać na własne pytania, ale po wielu badaniach i wprowadzeniu ekspertów, poniżej jest jedyny sposób, aby to zrobić.
Podsumujmy:
1. Ty w rzeczywistości trzeba dodać demona. Tak więc, dla Pinch, po prostu upuść "PinchInputModule.cs" na obiekcie konsumenta - i gotowe.
Możesz pomyśleć: "to jest do bani, że nie działa automagicznie bez dodawania demona". ALE - w rzeczywistości przy użyciu Unity trzeba dodać demona, rodzinę "TouchInput". (Które czasem dodają automatycznie, czasami zapominają i musisz to zrobić.)
Po prostu "pogoń" dla automagic jest głupia, zapomnij o tym. Musisz dodać demona.
2. Musisz odziedziczyć bok z IPointerDownHandler/etc, ponieważ, po prostu, Unity zawiedli i nie można dziedziczyć poprawnie w StandAloneInputModule. Kopiowanie i wklejanie nie jest programowaniem.
Po prostu nie jest to dobra technika, aby podążać ścieżką podklasy StandAloneInputModule, ponieważ Unity zawiodło. Po prostu użyj IPointerDownHandler/etc w swoich nowych demonom. Więcej dyskusji na ten temat poniżej.
Poniżej podaję przykłady "pojedynczego dotknięcia" i "uszczypnięcia". Są gotowe do produkcji. Możesz napisać własną rękę do innych sytuacjach, takich jak cztery-touch itp So, z pinch-demona (dosłownie upuść go na danego przedmiotu), to wtedy szalenie łatwe w obsłudze szczypty:
public void OnPinchZoom (float delta)
{
_processPinch(delta);
}
Trudne aby było łatwiej.
Więc to zrobić, aż Unity pamięta to produkt „używane w telefonach” i dodać szczyptę dla połączeń itp
Dodać kostkę, umieścić swój własny scenariusz na nim FingerMove
.
Utwórz skrypt, powiedz: przenieś kamerę LR, UD. (Lub cokolwiek - tylko debug.log zmian.)
Wklej w tym skrypcie handler ...
SingleFingerInputModule.cs
/*
ISingleFingerHandler - handles strict single-finger down-up-drag
Put this daemon ON TO the game object, with a consumer of the service.
(Note - there are many, many philosophical decisions to make when
implementing touch concepts; just some issues include what happens
when other fingers touch, can you "swap out" etc. Note that, for
example, Apple vs. Android have slightly different takes on this.
If you wanted to implement slightly different "philosophy" you'd
do that in this script.)
*/
public interface ISingleFingerHandler
{
void OnSingleFingerDown (Vector2 position);
void OnSingleFingerUp (Vector2 position);
void OnSingleFingerDrag (Vector2 delta);
}
/* note, Unity chooses to have "one interface for each action"
however here we are dealing with a consistent paradigm ("dragging")
which has three parts; I feel it's better to have one interface
forcing the consumer to have the three calls (no problem if empty) */
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
public class SingleFingerInputModule:MonoBehaviour,
IPointerDownHandler,IPointerUpHandler,IDragHandler
{
private ISingleFingerHandler needsUs = null;
// of course that would be a List,
// just one shown for simplicity in this example code
private int currentSingleFinger = -1;
private int kountFingersDown = 0;
void Awake()
{
needsUs = GetComponent(typeof(ISingleFingerHandler)) as ISingleFingerHandler;
// of course, you may prefer this to search the whole scene,
// just this gameobject shown here for simplicity
// alternately it's a very good approach to have consumers register
// for it. to do so just add a register function to the interface.
}
public void OnPointerDown(PointerEventData data)
{
kountFingersDown = kountFingersDown + 1;
if (currentSingleFinger == -1 && kountFingersDown == 1)
{
currentSingleFinger = data.pointerId;
if (needsUs != null) needsUs.OnSingleFingerDown(data.position);
}
}
public void OnPointerUp (PointerEventData data)
{
kountFingersDown = kountFingersDown - 1;
if (currentSingleFinger == data.pointerId)
{
currentSingleFinger = -1;
if (needsUs != null) needsUs.OnSingleFingerUp(data.position);
}
}
public void OnDrag (PointerEventData data)
{
if (currentSingleFinger == data.pointerId && kountFingersDown == 1)
{
if (needsUs != null) needsUs.OnSingleFingerDrag(data.delta);
}
}
}
umieścić, że demon na obiekt gry, z konsumentem FingerMove
i zapomnij o tym.Jest teraz
śmiesznie łatwe
obsłużyć przeciąganie:
public class FingerMove:MonoBehaviour, ISingleFingerHandler
{
public void OnSingleFingerDown(Vector2 position) {}
public void OnSingleFingerUp (Vector2 position) {}
public void OnSingleFingerDrag (Vector2 delta)
{
_processSwipe(delta);
}
private void _processSwipe(Vector2 screenTravel)
{
.. move the camera or whatever ..
}
}
jak powiedziałem,
śmiesznie proste!
Teraz zastanówmy się nad przypadkiem z dwoma palcami, uszczypnięciem w celu przybliżenia/oddalenia.
PinchInputModule.cs
/*
IPinchHandler - strict two sequential finger pinch Handling
Put this daemon ON TO the game object, with a consumer of the service.
(Note, as always, the "philosophy" of a glass gesture is up to you.
There are many, many subtle questions; eg should extra fingers block,
can you 'swap primary' etc etc etc - program it as you wish.)
*/
public interface IPinchHandler
{
void OnPinchStart();
void OnPinchEnd();
void OnPinchZoom (float gapDelta);
}
/* note, Unity chooses to have "one interface for each action"
however here we are dealing with a consistent paradigm ("pinching")
which has three parts; I feel it's better to have one interface
forcing the consumer to have the three calls (no problem if empty) */
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
public class PinchInputModule:MonoBehaviour,
IPointerDownHandler,IPointerUpHandler,IDragHandler
{
private IPinchHandler needsUs = null;
// of course that would be a List,
// just one shown for simplicity in this example code
private int currentFirstFinger = -1;
private int currentSecondFinger = -1;
private int kountFingersDown = 0;
private bool pinching = false;
private Vector2 positionFirst = Vector2.zero;
private Vector2 positionSecond = Vector2.zero;
private float previousDistance = 0f;
private float delta = 0f;
void Awake()
{
needsUs = GetComponent(typeof(IPinchHandler)) as IPinchHandler;
// of course, this could search the whole scene,
// just this gameobject shown here for simplicity
}
public void OnPointerDown(PointerEventData data)
{
kountFingersDown = kountFingersDown + 1;
if (currentFirstFinger == -1 && kountFingersDown == 1)
{
// first finger must be a pure first finger and that's that
currentFirstFinger = data.pointerId;
positionFirst = data.position;
return;
}
if (currentFirstFinger != -1 && currentSecondFinger == -1 && kountFingersDown == 2)
{
// second finger must be a pure second finger and that's that
currentSecondFinger = data.pointerId;
positionSecond = data.position;
FigureDelta();
pinching = true;
if (needsUs != null) needsUs.OnPinchStart();
return;
}
}
public void OnPointerUp (PointerEventData data)
{
kountFingersDown = kountFingersDown - 1;
if (currentFirstFinger == data.pointerId)
{
currentFirstFinger = -1;
if (pinching)
{
pinching = false;
if (needsUs != null) needsUs.OnPinchEnd();
}
}
if (currentSecondFinger == data.pointerId)
{
currentSecondFinger = -1;
if (pinching)
{
pinching = false;
if (needsUs != null) needsUs.OnPinchEnd();
}
}
}
public void OnDrag (PointerEventData data)
{
if (currentFirstFinger == data.pointerId)
{
positionFirst = data.position;
FigureDelta();
}
if (currentSecondFinger == data.pointerId)
{
positionSecond = data.position;
FigureDelta();
}
if (pinching)
{
if (data.pointerId == currentFirstFinger || data.pointerId == currentSecondFinger)
{
if (kountFingersDown==2)
{
if (needsUs != null) needsUs.OnPinchZoom(delta);
}
return;
}
}
}
private void FigureDelta()
{
float newDistance = Vector2.Distance(positionFirst, positionSecond);
delta = newDistance - previousDistance;
previousDistance = newDistance;
}
}
umieścić, że demon się do obiektu gra, w której masz konsumenta usługi. Zauważ, że nie ma absolutnie żadnego problemu z "mieszaniem i dopasowaniem". W tym przykładzie zróbmy OBU gest przeciągania i szczypania. Jest teraz
prostu głupio proste
obsłużyć pinch:
public class FingerMove:MonoBehaviour, ISingleFingerHandler, IPinchHandler
{
public void OnSingleFingerDown(Vector2 position) {}
public void OnSingleFingerUp (Vector2 position) {}
public void OnSingleFingerDrag (Vector2 delta)
{
_processSwipe(delta);
}
public void OnPinchStart() {}
public void OnPinchEnd() {}
public void OnPinchZoom (float delta)
{
_processPinch(delta);
}
private void _processSwipe(Vector2 screenTravel)
{
.. handle drag (perhaps move LR/UD)
}
private void _processPinch(float delta)
{
.. handle zooming (perhaps move camera in-and-out)
}
}
Jak mówię,
głupio proste! :)
Aby zobaczyć, jak elegancko to jest, rozważ takie kwestie jak: czy przy szczypcaniu chcesz, aby to "wstrzymało" przeciąganie, czy pozwoliło, aby to się stało? Zadziwiające jest to, że po prostu programujesz to wewnątrz SingleFingerInputModule.cs. W tym konkretnym przykładzie chciałem, aby "trzymał" przeciąganie, podczas gdy/jeśli użytkownik przybliża, tak pojedynczy program SingleFingerInputModule.cs jest tak zaprogramowany. Możesz go łatwo modyfikować, aby kontynuować przeciąganie, zmieniać na centroidy, anulować przeciąganie lub cokolwiek chcesz. Zadziwiające jest to, że FingerMove.cs w ogóle nie ma wpływu! Niezwykle przydatna abstrakcja!
Należy pamiętać, że za doskonały przykład cztery narożną GÖKHAN za powyższym, chciałbym napisać to tak:
public class FingerStretch:MonoBehaviour, IFourCornerHandler
{
public void OnFourCornerChange (Vector2 a, b, c, d)
{
... amazingly elegant solution
... Gökhan does all the work in FourCornerInputModule.cs
... here I just subscribe to it. amazingly simple
}
Która jest tylko umysł bogglingly proste podejście.
To umysł bogglingly prosta: O
Gökhan by upakować całą logikę palców wewnątrz FourCornerInputModule.cs które mają IFourCornerHandler interfejsu. Zauważ, że FourCornerInputModule rozsądnie podejmowałby wszystkie filozoficzne decyzje (przykład, musisz mieć wszystkie cztery palce w dół, co jeśli masz jeden dodatkowy, etcetera itp.).
Oto kwestie wynikające:
1. powinniśmy zrobić "Event-System jak" programowanie?
Spójrz na swój projekt jedności w tak zwanym „Stand Alone modułu wejść”, który jest przedmiotem gra z EventSystem
i StandAloneInputModule
Można w rzeczywistości „Write od zera” lub coś podobnego SingleFingerInputModule.cs PinchInputModule.cs, , aby "działał jak" Unity StandAloneInputModule.
Mimo że można to zrobić, zwróć uwagę na linki w komentarzach in this answer.
Ale jest problem specyficzny, knock-down: śmiesznie, nie można korzystać z zasad OO w moim kodu powyżej SingleFingerInputModule.cs
, mamy bardzo rozsądnie - oczywiście - wykorzystanie istniejącego niesamowite IPointerDownHandler itp moc, która Jedność już się dokonała, a my (w zasadzie) "podklasujemy", dodając trochę więcej logiki. Dokładnie to, co powinieneś zrobić, naprawdę musisz. W przeciwieństwie do tego: jeśli zdecydujesz się "stworzyć coś, co działa jak StandAloneInputModule", to jest to fiasko - musisz zacząć od nowa, prawdopodobnie kopiować i wklejać kod źródłowy Unity (dla IPointerDownHandler itp.) I trochę go modyfikować nieco w swój sposób, co jest oczywiście dokładnym przykładem tego, jak nigdy nie należy wykonywać inżynierii oprogramowania.
2. Ale musisz "pamiętać, aby dodać demona"?
Zauważ, że jeśli przejdziesz do opcji "stwórz coś, co działa jak StandAloneInputModule", w rzeczywistości musisz to zrobić !!!!! Co jest dość dziwne; nie ma żadnej korzyści.
3. Musisz zadzwonić do wszystkich subskrybentów?
Jeśli wybierzesz opcję "stwórz coś, co działa jak StandAloneInputModule", Unity ma wywołanie "Execute ....", które ... właśnie to robi. To niewiele więcej niż makro dla "wywoływania subskrybentów" (co wszyscy robimy codziennie w każdym skrypcie innym niż najbardziej banalny); bez przewagi.
W rzeczywistości: Osobiście uważam, że w rzeczywistości jest o wiele lepiej, aby mieć połączenie subskrypcji, as Everts suggests here, po prostu mieć to jako jedno z połączeń interfejsu. Po prostu uważam, że jest to o wiele lepsza technika, niż próba "bycia" jak "whacky magiczny system Unity Unity (który tak naprawdę w ogóle nie działa - musisz" pamiętać o dołączeniu "StandAloneInputModule).
Podsumowując,
doszedłem do przekonania, że
(1) budynek na IPointerDownHandler, IDragHandler, IPointerUpHandler jest w rzeczywistości, na pewno właściwe podejście
To bezspornie złym pomysłem zacznij ponownie pisać kod, aby "stworzyć coś, co działa jak StandAloneInputModule"
(2) nie ma nic złego w dodawaniu demona
Jeśli spróbujesz "zrobić coś, co działa jak StandAloneInputModule" ... musisz "pamiętać, aby go dodać" tak czy inaczej, na miłość boską.
(3) nie ma w ogóle nic złego ze znalezieniem odbiorców, albo lepiej, mający subscribe zadzwonić
Jeśli spróbujesz „zrobić coś, co działa jak StandAloneInputModule” jest prawie nieistniejąca zaletą „Wykonaj ... "zadzwoń do Unity, która jest linią kodu w stosunku do twojej (krótsza, jaśniejsza, szybsza) linia kodu, aby" zadzwonić do subskrybenta ". Znowu jest o wiele bardziej oczywiste i oczywiste, że po prostu każdy programista spoza Unity po prostu to zrobi.
Tak więc dla mnie najlepszym podejściem w Unity jest dziś napisanie modułów/interfejsów, takich jak SingleFingerInputModule.cs, PinchInputModule.cs, FourCornerInputModule.cs, upuść go na obiekcie gry, gdzie chcesz mieć ich konsumenta - i jesteś skończony. "To takie proste."
public class Zoom:MonoBehaviour, ISingleFingerHandler, IPinchHandler
{
public void OnPinchZoom (float delta)
{
...
Pracowałem na coś takiego kilka miesięcy temu. Spadnie odpowiedź kiedy znajdę go. BTW, brakuje znacznika C#. – Programmer
Wiesz co jest bardziej śmieszne niż zdarzeń dotykowych Unity? Zdarzenia dotykowe Javascript w. Ale znając ich obu, rozumiem, że nie ma możliwości, aby jakakolwiek implementacja mogła zaspokoić dewelopera.Możesz łatwo napisać kod, który zadziałałby w większości typowych przypadków, takich jak tylko dwa dotknięcia, ale byłoby tak wiele przypadków, gdyby nie " t cover –