C # / XNA uzyskać sprzętową pozycję myszy

10

Używam C # i próbuję uzyskać pozycję myszy sprzętowej. Pierwszą rzeczą, którą wypróbowałem, była prosta funkcjonalność XNA, która jest prosta w użyciu

Vector2 position = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);

Następnie wykonuję również rysowanie myszy i w porównaniu z myszą sprzętową systemu Windows, ta nowa mysz z współrzędnymi podanymi przez xna „zwalnia”. Rozumiem przez to, że jest opóźniony o kilka klatek. Na przykład, jeśli gra działa z prędkością 600 kl./s, klątwa będzie reagować, ale przy 60 kl./s opóźnienie myszy programowej nie jest już dopuszczalne. Dlatego próbowałem użyć myszy, która moim zdaniem była sprzętową,

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetCursorPos(out POINT lpPoint);

ale wynik był dokładnie taki sam. Próbowałem też uzyskać kursor Windows, i to był ślepy zaułek - działał, ale z tym samym opóźnieniem. Mieszanie z funkcjonalnością xna:

GraphicsDeviceManager.SynchronizeWithVerticalRetrace = true/false
Game.IsFixedTimeStep = true/fale

zmienił czas opóźnienia z dość oczywistych powodów, ale sedno jest takie, że niezależnie od tego, że nadal był za domyślną myszą Windows. Widziałem w niektórych grach, że zapewniają one opcję dla myszy z akceleracją sprzętową, aw innych (tak myślę) jest już domyślnie. Czy ktoś może dać jakieś wskazówki, jak to osiągnąć.

Oddzielać się
źródło
2
Nie sądzę, że Mouse.GetState()jest zepsuty. Czy na pewno ustawiasz pozycję myszy w tej samej ramce podczas jej rysowania? Pamiętaj również, że kursor Windows ma włączone przyspieszanie / zwalnianie, więc jest bardziej responsywny. Jeśli chcesz zbliżyć się do sprzętu, powinieneś nacisnąć DirectInput, co da ci deltę ruchu zamiast absolutnej pozycji myszy. Ale uważam, że Mouse.GetState()nadal jest to właściwy sposób na zrobienie tego w XNA.
Panda Pajama,
Nie, Mouse.GetState()nie jest zepsuty i tak długo, jak IsFixedTimeStep == trueludzie nie powinni widzieć opóźnienia, chyba że są porównywane bezpośrednio z myszą z widocznymi oknami. Jednak ustawienie IsFixedTimeStep na wartość false przy niższej liczbie klatek na sekundę staje się widoczne dla wszystkich. O bezpośrednim wejściu uznałem, że jest to możliwość (w ostateczności).
Sunder,
Nie jestem pewien, czy to jest to, czego chcesz, ale co ze zmianą duszka kursora, gdy jest on w oknie, a następnie sprawi, że będzie zawsze widoczny?
William Mariager,
Dokładnie to, co robię, chodzi o to, że nie chcę tego tylko pokazywać, ale też znać dokładną pozycję myszy. Ale to, co otrzymuję, jest nieco opóźnione w stosunku do tego, co powinno być.
Sunder,

Odpowiedzi:

17

To, co widzisz, nie jest opóźnieniem wejściowym.

Aby uzyskać pozycję kursora w systemie Windows, możesz użyć WM_MOUSEMOVElub GetCursorPos. Oba zapewniają pozycję kursora po przetworzeniu go przez system Windows, stosując takie rzeczy, jak przyspieszenie. Jest to pozycja kursora używana przez system Windows, w tym do rysowania. I prawie zawsze to, czego chcesz użyć.

Tego używa XNA . W szczególności GetCursorPos( MSDN ). Oznacza to, że kiedy dzwonisz Mouse.GetState, otrzymujesz prawdziwą pozycję kursora, tak jak widzi ją Windows - z praktycznie zerowym opóźnieniem.

Teraz możesz użyć WM_INPUT(lub DirectInput, choć jest to przestarzałe). To daje surowe dane wskaźnika. Dzieje się tak, zanim system Windows może zastosować takie przyspieszenie, i zwykle nie jest tym, czego chcesz.

To, co widzisz, to opóźnienie wyjściowe.

Wynika to z „ kursora sprzętowego ”. Jest to mały duszek, który jest przeciągany przez ekran przez GPU na samym końcu jego potoku. I mam na myśli koniec . Pojawia się za buforem ramki - zostaje wstawiony do strumienia danych, gdy obraz jest wysyłany do monitora. Powodem, dla którego tak szybko reaguje, jest ustawienie pozycji przez bezpośrednie ustawienie rejestrów GPU. W planach nie ma w ogóle czekania.

Z drugiej strony twoja gra musi przejść przez procesor GPU. Daje to wiele opóźnień. Najbardziej problematycznym z nich jest podwójne buforowanie - czego (jestem prawie pewien) nie można wyłączyć w XNA 4. Nie rysujesz bezpośrednio do bufora ramki - przyciągasz do bufora backbuffera. Co jest prezentowane co ~ 16ms (przy 60 FPS). Oznacza to, że patrzysz na minimum 16 ms między otrzymaniem wyniku Mouse.GetStateotrzymania czegoś na podstawie tego wyniku na ekranie. Prawdopodobnie dłużej. Z pewnością znacznie wolniej niż kursor sprzętowy.

Więc jakie są rozwiązania?

Jednym z nich jest użycie SetCursor( MSDN ) do zmiany obrazu wyświetlanego przez kursor sprzętowy, aby był obrazem, który lepiej pasuje do twojej gry (lub po prostu kursor Windows).

Drugim (o wiele łatwiejszym) jest po prostu zaakceptowanie opóźnienia i ustawienie Game.IsMouseVisible = falseukrywania względnego opóźnienia. Wyświetl własny kursor w grze. Będzie o 16 ms wolniejszy niż ukryty kursor Windows - ale kogo to obchodzi? Gracze są do tego przyzwyczajeni. A 16ms jest prawie niewidoczna wizualnie (dlatego renderujemy rzeczy w 60 FPS).

Andrew Russell
źródło
Dziękuję za szczegółowe wyjaśnienie sprawy. Najprawdopodobniej po prostu będę trzymać się myszy XNA. Poczekaj chwilę, zanim zaakceptujesz, na wypadek, gdyby ktoś miał jakieś dodatkowe obejście tego problemu.
Sunder
Wydaje mi się, że zaakceptowanie opóźnienia, czy nie, jest pytaniem, na które należy odpowiedzieć poprzez testowanie.
Zonko,