Wprowadzanie myszą

Z tego artykułu dowiesz się, jak wdrożyć obsługę myszy w Grach Google Play na PC w grach, w których tryb tłumaczenia wprowadzania nie zapewnia idealnego efektu.

Gracze na PC zazwyczaj korzystają z klawiatury i myszy, a nie z ekranu dotykowego, dlatego warto zastanowić się, czy Twoja gra obsługuje mysz. Domyślnie Gry Google Play na PC przekształcają każde zdarzenie kliknięcia lewym przyciskiem myszy w pojedyncze zdarzenie wirtualnego dotknięcia. Jest to tzw. „tryb tłumaczenia tekstu”.

Chociaż ten tryb umożliwia granie z niewielkimi zmianami, nie zapewnia graczom na PC wrażenia zbliżonego do natywnej rozgrywki. W tym celu zalecamy wdrożenie tych rozwiązań:

  • stany kursora w menu kontekstowych zamiast działań polegających na przytrzymaniu,
  • Kliknij prawym przyciskiem myszy, aby wykonać alternatywne czynności po przytrzymaniu lub w menu kontekstowym
  • Mouselook w przypadku gier akcji z widokiem z poziomu pierwszej lub trzeciej osoby, a nie zdarzeń naciśnięcia i przeciągnięcia.

Aby korzystać z wzorców interfejsu często używanych na komputerach PC, musisz wyłączyć tryb tłumaczenia danych wejściowych.

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

Wyłączanie trybu tłumaczenia tekstu

W pliku AndroidManifest.xml zadeklaruj android.hardware.type.pc funkcję. Oznacza to, że gra używa sprzętu komputerowego i wyłącza tryb tłumaczenia danych wejściowych. Dodatkowo dzięki dodaniu required="false" możesz mieć pewność, że Twoja 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, gdy uruchamiasz grę. Aby uzyskać surowe dane wejściowe z myszy, gdy emulator działa w trybie deweloperskim, kliknij prawym przyciskiem ikonę na pasku zadań, wybierz Opcje programisty, a następnie Tryb komputera(KiwiMouse).

Zrzut ekranu pokazujący „Tryb komputera(KiwiMouse)” wybrany w menu kontekstowym

Po wykonaniu tej czynności ruch myszy jest raportowany przez View.onGeneralMotionEvent z źródłem SOURCE_MOUSE, które wskazuje, ż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;
});

Szczegółowe informacje o obsługiwaniu danych wejściowych 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 metoda najlepiej sprawdza się przy wykrywaniu, że użytkownik najeżdża kursorem na przyciski lub obiekty w grze. Dzięki temu można wyświetlić pole podpowiedzi lub wprowadzić stan najechania kursorem myszy, aby zaznaczyć, co gracz ma 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 mają od dawna zarówno lewy, jak i prawy przycisk myszy, co umożliwia elementom interaktywnym wykonywanie zarówno działań podstawowych, jak i dodatkowych. Działanie dotykowe w grze, takie jak dotknięcie przycisku, najlepiej jest zmapowane na kliknięcie lewym przyciskiem myszy, gdzie kliknięcie prawym przyciskiem myszy sprawia, że gesty naciśnięcia i przytrzymania. W grach RTS możesz też kliknąć lewym przyciskiem myszy, aby coś wybrać, a kliknąć prawym przyciskiem, aby to przenieść. W strzelankach FPP można przypisać lewy i prawy przycisk myszy do strzelania podstawowym i dodatkowym rodzajem broni. W nieskończonym runnerze do skoku można użyć lewego przycisku myszy, a do uderzenia – prawego. Nie dodaliśmy obsługi zdarzenia środkowego kliknięcia.

Aby obsłużyć naciśnięcie przycisku, użyj ACTION_DOWNACTION_UP. Następnie użyj getActionButton, aby określić, który przycisk wywołał działanie, lub getButtonState, aby wyświetlić stan wszystkich przycisków.

W tym przykładzie enum służy do wyświetlania wyniku funkcji 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 myszy zamiast gestów szczypania, aby powiększać obszary w grze, lub dotykania i przeciągania obszarów do przewijania.

Aby odczytać wartości kółka przewijania, nasłuchuj zdarzenia ACTION_SCROLL. Delta od ostatniej klatki może zostać pobrana za pomocą getAxisValueAXIS_VSCROLL dla przesunięcia pionowego i AXIS_HSCROLL dla przesunięcia poziomego. 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 danych wejściowych z użyciem myszy

Niektóre gry muszą przejąć pełną kontrolę nad kursorem myszy, np. gry akcji z perspektywy pierwszej lub trzeciej osoby, które mapują ruch myszy na ruch kamery. Aby przejąć wyłączną kontrolę nad myszą, wywołaj View.requestPointerCapture().

requestPointerCapture() działa tylko wtedy, gdy widok hierarchii zawierający Twój widok jest aktywny. Z tego powodu nie można pobrać przechwycenia wskaźnika w wywołaniu zwrotnym onCreate. Musisz albo poczekać, aż gracz wejdzie w interakcję z wskaźnikiem myszy (np. podczas korzystania z menu głównego), albo użyć 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 rejestrowane przez requestPointerCapture() są wysyłane do widoku z możliwością skupienia, który zarejestrował zdarzenie 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 odblokować wyłączne przechwytywanie kursora, na przykład w celu umożliwienia graczom interakcji z menu pauzy, wywołaj View.releasePointerCapture().