Entrada del mouse

En este tema, se explica cómo implementar la entrada del mouse en Google Play Juegos para PC para juegos en los que el modo de traducción de entrada no le proporciona una experiencia ideal al jugador.

Por lo general, los jugadores de PC tienen un teclado y un mouse en lugar de una pantalla táctil, por lo que es importante pensar si tu juego se adapta a la entrada del mouse. De forma predeterminada, Google Play Juegos para PC convierte cualquier evento de clic con el botón izquierdo del mouse en un solo evento de toque virtual. Esto se conoce como "modo de traducción de entrada".

Si bien este modo hace que tu juego funcione con algunos cambios, los jugadores de PC no obtienen una experiencia de entorno nativo. Para lograrlo, te recomendamos que implementes lo siguiente:

  • Estados de colocar el cursor para los menús contextuales en lugar de acciones de mantener presionado un elemento
  • Clic con el botón derecho para realizar acciones alternativas que ocurren tras mantener presionado o en un menú contextual
  • Mouselook para juegos de acción en primera o tercera persona en lugar de un evento de presionar y arrastrar

Para admitir los patrones de la IU comunes en las PCs, debes inhabilitar el modo de traducción de entrada.

El control de entrada para Google Play Juegos para PC es idéntico al de ChromeOS. Los cambios que son compatibles con las PCs también mejoran el juego para todos los jugadores de Android.

Cómo inhabilitar el modo de traducción de entrada

En el archivo AndroidManifest.xml, declara la función android.hardware.type.pc. Esto indica que tu juego usa hardware de PC e inhabilita el modo de traducción de entrada. Además, agregar required="false" ayuda a garantizar que tu juego aún se pueda instalar en teléfonos y tablets sin un mouse. Por ejemplo:

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

La versión de producción de Google Play Juegos para PC cambia al modo correcto cuando se inicia un juego. Cuando utilizas el emulador de desarrolladores, debes hacer clic con el botón derecho en el ícono de la barra de tareas, seleccionar Opciones para desarrolladores y, luego, Modo PC (KiwiMouse) para recibir la entrada del mouse sin procesar.

Captura de pantalla de la opción &quot;Modo PC (KiwiMouse)&quot; seleccionada en el menú contextual

Después de esto, View.onGenericMotionEvent informa el movimiento del mouse con la fuente SOURCE_MOUSE, que indica que es un evento del mouse.

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;
});

Si deseas obtener más información para controlar la entrada del mouse, consulta la documentación de ChromeOS.

Cómo controlar el movimiento del mouse

Para detectar el movimiento del mouse, escucha los eventos ACTION_HOVER_ENTER, ACTION_HOVER_EXIT y ACTION_HOVER_MOVE.

Su uso se recomienda para detectar cuando el usuario coloca el cursor sobre objetos o botones en un juego, lo que te da la oportunidad de mostrar un cuadro de sugerencia o implementar un estado de desplazamiento del mouse para destacar lo que está a punto de seleccionar. Por ejemplo:

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;
});

Cómo controlar los botones del mouse

Las PCs tienen botones izquierdo y derecho hace tiempo. Estos botones proporcionan acciones principales y secundarias a los elementos interactivos. En un juego, es mejor asignar las acciones de presión, como presionar un botón, al clic con el botón izquierdo, mientras que las acciones de mantener presionado se sienten más naturales con el clic derecho. En los juegos de estrategia en tiempo real, también puedes usar el clic con el botón izquierdo para seleccionar y el clic con el botón derecho para moverte. En los juegos de disparos en primera persona, se pueden asignar el fuego primario y el secundario al clic con el botón izquierdo y con el derecho, respectivamente. En los juegos de corredores infinitos, se puede usar el clic con el botón izquierdo para saltar y el clic con el botón derecho para correr. No agregamos compatibilidad con el evento de clic con el botón central.

Para controlar las presiones de los botones, usa ACTION_DOWN y ACTION_UP. Luego, usa getActionButton para determinar qué botón activó la acción o getButtonState para obtener el estado de todos los botones.

En este ejemplo, se usa enum para ayudar a mostrar el resultado de 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;
        }
    }
}

En este ejemplo, la acción se controla de manera similar a los eventos de colocar el cursor sobre un elemento:

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;
});

Cómo controlar el desplazamiento de la rueda del mouse

Te recomendamos que uses la rueda del mouse en lugar de gestos de pellizcar para acercar o áreas de desplazamiento de tocar y arrastrar en tu juego.

Para leer los valores de la rueda del mouse, escucha el evento ACTION_SCROLL. El delta desde el último fotograma se puede recuperar mediante getAxisValue con AXIS_VSCROLL para el desplazamiento vertical y AXIS_HSCROLL para el desplazamiento horizontal. Por ejemplo:

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;
});

Cómo capturar la entrada del mouse

Algunos juegos necesitan tomar el control total del cursor del mouse, como los de acción en primera o tercera persona que asignan el movimiento del mouse al movimiento de la cámara. Para tomar el control exclusivo del mouse, invoca a View.requestPointerCapture().

requestPointerCapture() solo funciona cuando está enfocada la jerarquía de vistas que contiene la vista. Por este motivo, no puedes adquirir la captura del puntero en la devolución de llamada onCreate. Debes esperar a que la interacción del jugador capture el puntero del mouse, como cuando interactúa con el menú principal, o bien usar la devolución de llamada onWindowFocusChanged. Por ejemplo:

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();
    }
}

Los eventos que captura requestPointerCapture() se envían a la vista enfocable que registró OnCapturedPointerListener. Por ejemplo:

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;
});

Para liberar la captura exclusiva del mouse, por ejemplo, para permitir que los jugadores interactúen con un menú de pausa, invoca a View.releasePointerCapture().