Wprowadzanie myszą

W tym artykule opisujemy, jak zaimplementować obsługę myszy w Grach Google Play na PC w przypadku gier, w których tryb tłumaczenia danych wejściowych nie zapewnia idealnych wrażeń z gry.

Gracze na PC zwykle używają klawiatury i myszy, a nie ekranu dotykowego, dlatego ważne jest, aby sprawdzić, czy gra obsługuje mysz. Domyślnie Gry Google Play na PC przekształcają każde kliknięcie lewym przyciskiem myszy w pojedyncze wirtualne kliknięcie. Jest to tzw. „tryb tłumaczenia wpisywanego tekstu”.

Chociaż ten tryb sprawia, że gra działa z niewielkimi zmianami, nie zapewnia graczom na PC wrażeń, które są dla nich naturalne. W tym celu zalecamy wdrożenie tych rozwiązań:

  • Stany najechania w przypadku menu kontekstowych zamiast czynności „naciśnij i przytrzymaj”
  • Kliknij prawym przyciskiem myszy, aby wykonać alternatywne działania, które są wykonywane po przytrzymaniu lub w menu kontekstowym.
  • Mouselook w przypadku gier akcji z widokiem z perspektywy pierwszej lub trzeciej osoby zamiast zdarzenia naciśnięcia i przeciągnięcia

Aby obsługiwać wzorce interfejsu, które są powszechne na komputerach, musisz wyłączyć tryb tłumaczenia danych wejściowych.

Obsługa danych wejściowych w Grach Google Play na PC jest identyczna jak w przypadku ChromeOS. Zmiany, które obsługują komputery, ulepszają też grę dla wszystkich graczy na Androidzie.

Wyłączanie trybu tłumaczenia wpisywanego tekstu

W pliku AndroidManifest.xml zadeklaruj android.hardware.type.pc funkcję. Oznacza to, że gra korzysta ze sprzętu PC i wyłącza tryb tłumaczenia danych wejściowych. Dodatkowo dodanie required="false" pomaga zapewnić, że gra będzie nadal instalowana na telefonach i tabletach bez myszy. Na przykład:

<manifest ...>
  <uses-feature
      android:name="android.hardware.type.pc"
      android:required="false" />
  ...
</manifest>

Wersja produkcyjna Gier Google Play na PC przełącza się na odpowiedni tryb po uruchomieniu gry. Podczas korzystania z emulatora dla programistów musisz kliknąć prawym przyciskiem myszy ikonę na pasku zadań, wybrać Opcje programisty, a następnie Tryb PC(KiwiMouse), aby otrzymywać nieprzetworzone dane wejściowe myszy.

Zrzut ekranu z wybraną opcją „Tryb PC(KiwiMouse)” w menu kontekstowym

Po wykonaniu tej czynności ruch myszy jest zgłaszany przez View.onGenericMotionEvent ze źródłem SOURCE_MOUSE, co oznacza, że jest to zdarzenie myszy.

Kotlin

gameView.setOnGenericMotionListener { _, motionEvent ->
    var handled = false
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        // handle the mouse event here
        handled = true
    }
    handled
}

Java

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        // handle the mouse event here
        return true;
    }
    return false;
});

Więcej informacji o obsłudze danych wejściowych z myszy znajdziesz w dokumentacji ChromeOS.

Obsługa ruchu myszy

Aby wykryć ruch myszy, nasłuchuj zdarzeń ACTION_HOVER_ENTER, ACTION_HOVER_EXITACTION_HOVER_MOVE.

Ta funkcja najlepiej sprawdza się w przypadku wykrywania, czy użytkownik najeżdża kursorem na przyciski lub obiekty w grze. Dzięki temu możesz wyświetlić okienko z podpowiedzią lub wdrożyć stan najechania kursorem, aby wyróżnić element, który gracz zamierza wybrać. Na przykład:

Kotlin

gameView.setOnGenericMotionListener { _, motionEvent ->
   var handled = false
   if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
       when(motionEvent.action) {
           MotionEvent.ACTION_HOVER_ENTER -> Log.d("MA", "Mouse entered at ${motionEvent.x}, ${motionEvent.y}")
           MotionEvent.ACTION_HOVER_EXIT -> Log.d("MA", "Mouse exited at ${motionEvent.x}, ${motionEvent.y}")
           MotionEvent.ACTION_HOVER_MOVE -> Log.d("MA", "Mouse hovered at ${motionEvent.x}, ${motionEvent.y}")
       }
       handled = true
   }

   handled
}

Java

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_HOVER_ENTER:
                Log.d("MA", "Mouse entered at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
            case MotionEvent.ACTION_HOVER_EXIT:
                Log.d("MA", "Mouse exited at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
            case MotionEvent.ACTION_HOVER_MOVE:
                Log.d("MA", "Mouse hovered at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
        }
        return true;
    }
    return false;
});

Obsługa przycisków myszy

Komputery PC od dawna mają lewy i prawy przycisk myszy, co umożliwia interaktywnym elementom wykonywanie działań podstawowych i dodatkowych. W grze działania takie jak kliknięcie przycisku najlepiej mapować na kliknięcie lewym przyciskiem myszy, a działania polegające na dotknięciu i przytrzymaniu – na kliknięcie prawym przyciskiem myszy. W grach RTS możesz też używać lewego przycisku myszy do wybierania i prawego do przesuwania. W strzelankach FPP można przypisać strzelanie podstawowe i dodatkowe do kliknięcia lewym i prawym przyciskiem myszy. W grze typu infinite runner można używać lewego przycisku myszy do skakania, a prawego do sprintu. Nie dodaliśmy obsługi zdarzenia kliknięcia środkowym przyciskiem myszy.

Do obsługi naciśnięć przycisków używaj funkcji ACTION_DOWNACTION_UP. Następnie użyj funkcji getActionButton, aby określić, który przycisk wywołał działanie, lub funkcji getButtonState, aby uzyskać stan wszystkich przycisków.

W tym przykładzie wyliczenie jest używane do wyświetlania wyniku getActionButton:

Kotlin

enum class MouseButton {
   LEFT,
   RIGHT,
   UNKNOWN;
   companion object {
       fun fromMotionEvent(motionEvent: MotionEvent): MouseButton {
           return when (motionEvent.actionButton) {
               MotionEvent.BUTTON_PRIMARY -> LEFT
               MotionEvent.BUTTON_SECONDARY -> RIGHT
               else -> UNKNOWN
           }
       }
   }
}

Java

enum MouseButton {
    LEFT,
    RIGHT,
    MIDDLE,
    UNKNOWN;
    static MouseButton fromMotionEvent(MotionEvent motionEvent) {
        switch (motionEvent.getActionButton()) {
            case MotionEvent.BUTTON_PRIMARY:
                return MouseButton.LEFT;
            case MotionEvent.BUTTON_SECONDARY:
                return MouseButton.RIGHT;
            default:
                return MouseButton.UNKNOWN;
        }
    }
}

W tym przykładzie działanie jest obsługiwane podobnie jak zdarzenia najechania kursorem:

Kotlin

// Handle the generic motion event
gameView.setOnGenericMotionListener { _, motionEvent ->
   var handled = false
   if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
       when (motionEvent.action) {
           MotionEvent.ACTION_BUTTON_PRESS -> Log.d(
               "MA",
               "${MouseButton.fromMotionEvent(motionEvent)} pressed at ${motionEvent.x}, ${motionEvent.y}"
           )
           MotionEvent.ACTION_BUTTON_RELEASE -> Log.d(
               "MA",
               "${MouseButton.fromMotionEvent(motionEvent)} released at ${motionEvent.x}, ${motionEvent.y}"
           )
       }
       handled = true
   }

   handled
}

Java

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_BUTTON_PRESS:
                Log.d("MA", MouseButton.fromMotionEvent(motionEvent) + " pressed at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
            case MotionEvent.ACTION_BUTTON_RELEASE:
                Log.d("MA", MouseButton.fromMotionEvent(motionEvent) + " released at " + motionEvent.getX() + ", " + motionEvent.getY());
                break;
        }
        return true;
    }
    return false;
});

Obsługa przewijania kółkiem myszy

Zalecamy używanie kółka przewijania myszy zamiast gestów szczypania w celu powiększenia lub obszarów przewijania dotykowego w grze.

Aby odczytać wartości kółka przewijania, nasłuchuj zdarzenia ACTION_SCROLL. Różnicę od ostatniej klatki można pobrać za pomocą getAxisValueAXIS_VSCROLL w przypadku przesunięcia w pionie i AXIS_HSCROLL w przypadku przesunięcia w poziomie. Na przykład:

Kotlin

gameView.setOnGenericMotionListener { _, motionEvent ->
   var handled = false
   if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
       when (motionEvent.action) {
           MotionEvent.ACTION_SCROLL -> {
               val scrollX = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL)
               val scrollY = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL)
               Log.d("MA", "Mouse scrolled $scrollX, $scrollY")
           }
       }
       handled = true
   }
   handled
}

Java

gameView.setOnGenericMotionListener((view, motionEvent) -> {
    if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_SCROLL:
                float scrollX = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL);
                float scrollY = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL);
                Log.d("MA", "Mouse scrolled " + scrollX + ", " + scrollY);
                break;
        }
        return true;
    }
    return false;
});

Przechwytywanie sygnałów myszy

Niektóre gry wymagają pełnej kontroli nad kursorem myszy, np. gry akcji z perspektywy pierwszej lub trzeciej osoby, w których ruch myszy jest mapowany na ruch kamery. Aby przejąć wyłączną kontrolę nad myszą, wywołaj View.requestPointerCapture().

requestPointerCapture() działa tylko wtedy, gdy hierarchia widoków zawierająca Twój widok jest aktywna. Z tego powodu nie możesz uzyskać przechwytywania wskaźnika w wywołaniu zwrotnym onCreate. Poczekaj na interakcję gracza, aby przechwycić wskaźnik myszy, np. podczas interakcji z menu głównym, lub użyj wywołania zwrotnego onWindowFocusChanged. Na przykład:

Kotlin

override fun onWindowFocusChanged(hasFocus: Boolean) {
   super.onWindowFocusChanged(hasFocus)

   if (hasFocus) {
       gameView.requestPointerCapture()
   }
}

Java

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    if (hasFocus) {
        View gameView = findViewById(R.id.game_view);
        gameView.requestPointerCapture();
    }
}

Zdarzenia przechwycone przez requestPointerCapture() są wysyłane do widoku, który można zaznaczyć i który zarejestrował OnCapturedPointerListener. Na przykład:

Kotlin

gameView.focusable = View.FOCUSABLE
gameView.setOnCapturedPointerListener { _, motionEvent ->
    Log.d("MA", "${motionEvent.x}, ${motionEvent.y}, ${motionEvent.actionButton}")
    true
}

Java

gameView.setFocusable(true);
gameView.setOnCapturedPointerListener((view, motionEvent) -> {
    Log.d("MA", motionEvent.getX() + ", " + motionEvent.getY() + ", " + motionEvent.getActionButton());
    return true;
});

Aby zwolnić wyłączną kontrolę nad myszą, np. aby umożliwić graczom interakcję z menu pauzy, wywołaj funkcję View.releasePointerCapture().