Wprowadzanie myszą

Z tego artykułu dowiesz się, jak wdrożyć w Grach Google Play na PC dane wejściowe za pomocą myszy w grach, w których tryb tłumaczenia danych wejściowych nie zapewnia użytkownikom idealnego komfortu korzystania z aplikacji.

Użytkownicy komputerów PC mają zwykle klawiaturę i mysz, a nie ekran dotykowy, dlatego ważne jest, by zastanowić się, czy Twoja gra obsługuje sterowanie myszą. Domyślnie Gry Google Play na PC konwertują dowolne zdarzenie kliknięcia lewym przyciskiem myszy w jedno wirtualne zdarzenie kliknięcia. Jest to tzw. „tryb tłumaczenia wejściowego”.

Chociaż ten tryb umożliwia działanie gry po wprowadzeniu kilku zmian, nie daje użytkownikom PC typowych wrażeń. Aby to zrobić, zalecamy wdrożenie następujących tagów:

  • Po najechaniu kursorem na menu kontekstowe zamiast opcji naciskania i przytrzymywania działania
  • Kliknij prawym przyciskiem myszy, aby zobaczyć działania alternatywne, które są wykonywane po przytrzymaniu lub w menu kontekstowym
  • Szukaj gier akcji z perspektywy pierwszej lub trzeciej osoby zamiast gry typu „naciśnij i przeciągnij”

Aby obsługiwać wzorce interfejsu, które często występują 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 dotyczące komputerów PC poprawiają też granie z myślą o wszystkich użytkownikach Androida.

Wyłącz tryb tłumaczenia danych wejściowych

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

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

Po uruchomieniu gry wersja produkcyjna Gier Google Play na PC przełącza się w prawidłowy tryb. Jeśli chcesz uruchomić emulator programisty, kliknij prawym przyciskiem myszy ikonę paska zadań, wybierz Opcje programisty, a następnie Tryb PC(KiwiMouse), aby otrzymać nieprzetworzone dane myszy.

Zrzut ekranu przedstawiający tryb PC(KiwiMouse) wybrany w menu kontekstowym

Gdy to zrobisz, ruch myszy będzie raportowany przez View.onGeneralMotionEvent, a źródło SOURCE_MOUSE będzie wskazywać, ż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łudze wprowadzania danych myszą znajdziesz w dokumentacji ChromeOS.

Obsługa ruchów myszy

Aby wykrywać ruch myszy, nasłuchuj zdarzeń ACTION_HOVER_ENTER, ACTION_HOVER_EXIT i ACTION_HOVER_MOVE.

Najlepiej sprawdza się przy wykrywaniu, że użytkownik najeżdża kursorem na przyciski lub obiekty w grze. Dzięki temu możesz wyświetlić pole podpowiedzi lub zaimplementować stan najechania kursorem myszy, aby podkreślić, co ma wybrać gracz. 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

Od dawna komputery PC mają lewy i prawy przycisk myszy oraz elementy interaktywne – zarówno główne, jak i dodatkowe. W grze dotknięcia, takie jak dotknięcie przycisku, najlepiej przyporządkować do kliknięcia lewym przyciskiem, tam gdzie dotknięcie i przytrzymanie działa najlepiej jak kliknięcie prawym przyciskiem myszy. W grach strategicznych w czasie rzeczywistym możesz też wybrać lewy przycisk myszy i kliknąć prawym przyciskiem, aby przenieść się w inne miejsce. W strzelankach z pierwszej ręki możemy przypisać ruch główny i dodatkowy do lewej i prawej strony. Czas na nieskończony bieganie może polegać na kliknięciu lewym przyciskiem myszy, by skakać, i prawego, by biegać. Nie obsługujemy zdarzenia „środkowego kliknięcia”.

Aby obsługiwać naciśnięcia przycisku, użyj elementów ACTION_DOWN i ACTION_UP. Następnie użyj getActionButton, aby określić, który przycisk wywołał działanie, lub getButtonState, aby sprawdzić stan wszystkich przycisków.

W tym przykładzie do wyświetlenia wyniku getActionButton użyto wyliczenia:

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 to jest obsługiwane podobnie jak w przypadku 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;
});

Przewijanie kółkiem myszy

Zalecamy, aby zamiast ściągania lub powiększania gestów używać kółka przewijania w grze, a także używać dotykania i przeciągania obszarów w grze.

Aby odczytać wartości kółka przewijania, nasłuchuj zdarzenia ACTION_SCROLL. delta, ponieważ ostatnią klatkę można pobrać za pomocą funkcji getAxisValue z AXIS_VSCROLL (przesunięciem w pionie) i AXIS_HSCROLL (odsunięciem 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 danych wejściowych myszy

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

requestPointerCapture() działa tylko wtedy, gdy hierarchia widoków, w której się znajduje, jest zaznaczona. Z tego powodu nie możesz pozyskać wskaźnika w wywołaniu zwrotnym onCreate. Poczekaj na interakcję użytkownika, aby przechwycić kursor myszy, np. podczas interakcji z menu głównym, lub skorzystaj z 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ć, 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 udostępnić specjalne funkcje przechwytywania ekranu, np. umożliwić graczom interakcję z menu wstrzymania, wywołaj View.releasePointerCapture().