Управление мышью

В этой статье рассказывается, как реализовать управление мышью в Google Play Games на компьютере для игр, в которых режим перевода не обеспечивает оптимальный игровой процесс.

У игроков на ПК обычно есть клавиатура и мышь, а не сенсорный экран, поэтому важно учитывать, поддерживает ли ваша игра управление мышью. По умолчанию Google Play Игры на компьютере преобразуют любое нажатие левой кнопки мыши в виртуальное касание. Этот режим называется "режим перевода при вводе".

Хотя этот режим позволяет запустить игру на ПК с минимальными изменениями, он не обеспечивает пользователям удобство, сравнимое с нативным. Для этого мы рекомендуем реализовать следующие меры:

  • Состояния наведения указателя для контекстных меню, а не для действий, выполняемых при нажатии и удерживании
  • Нажмите правой кнопкой мыши, чтобы выполнить альтернативные действия, которые обычно выполняются при долгом нажатии или в контекстном меню.
  • Режим обзора мышью для игр от первого или третьего лица, а не нажатие и перетаскивание.

Чтобы поддерживать шаблоны интерфейса, которые обычно используются на ПК, необходимо отключить режим перевода ввода.

Обработка ввода в Google Play Играх на компьютере аналогична ChromeOS. Изменения, необходимые для поддержки ПК, также улучшат игру для всех пользователей Android.

Как отключить режим перевода при вводе

В файле AndroidManifest.xml объявите функцию android.hardware.type.pc. Это означает, что в игре используется аппаратное обеспечение ПК и отключен режим преобразования ввода. Кроме того, добавление required="false" позволяет устанавливать игру на телефоны и планшеты без мыши. Пример:

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

Рабочая версия Google Play Игр на компьютере переключается в нужный режим при запуске игры. При запуске в эмуляторе разработчика необходимо нажать правой кнопкой мыши на значок на панели задач, выбрать Для разработчиков, а затем Режим ПК(KiwiMouse), чтобы получать управление мышью.

Скриншот контекстного меню, в котором выбран вариант &quot;Режим ПК(KiwiMouse)&quot;

После этого движение мыши будет передаваться с помощью метода View.onGenericMotionEvent с источником SOURCE_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;
});

Подробнее об управлении мышью см. в документации по ChromeOS.

Обработка движения мыши

Чтобы обнаружить движение мыши, прослушайте события ACTION_HOVER_ENTER, ACTION_HOVER_EXIT и ACTION_HOVER_MOVE.

Этот метод лучше всего подходит для обнаружения наведения указателя на кнопки или объекты в игре. Он позволяет показывать подсказки или выделять объекты, которые игрок собирается выбрать. Пример:

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

Обработка нажатий кнопок мыши

На компьютерах уже давно есть левая и правая кнопки мыши, которые позволяют выполнять основные и дополнительные действия с интерактивными элементами. В играх нажатия на кнопки лучше всего сопоставлять с нажатием левой кнопки мыши, а действия с удержанием – с нажатием правой кнопки мыши. В стратегиях в реальном времени левая кнопка мыши может использоваться для выбора, а правая – для перемещения. В шутерах от первого лица левая и правая кнопки мыши могут быть назначены на основную и дополнительную стрельбу. В бесконечном раннере левая кнопка мыши может отвечать за прыжок, а правая – за рывок. Мы не добавили поддержку события среднего клика.

Для обработки нажатий кнопок используйте ACTION_DOWN и ACTION_UP. Затем используйте getActionButton, чтобы определить, какая кнопка вызвала действие, или getButtonState, чтобы получить состояние всех кнопок.

В этом примере перечисление используется для отображения результата 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;
        }
    }
}

В этом примере действие обрабатывается так же, как события наведения указателя:

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

Как обрабатывать прокрутку колесиком мыши

Мы рекомендуем использовать колесо прокрутки мыши вместо жестов масштабирования или областей прокрутки с помощью касания и перетаскивания.

Чтобы считывать значения колесика прокрутки, прослушивайте событие ACTION_SCROLL. Разницу с предыдущим кадром можно получить с помощью getAxisValue с AXIS_VSCROLL (вертикальное смещение) и AXIS_HSCROLL (горизонтальное смещение). Пример:

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

Захват ввода с помощью мыши

Некоторым играм требуется полный контроль над курсором мыши, например экшенам от первого или третьего лица, в которых движение мыши сопоставляется с движением камеры. Чтобы получить эксклюзивный контроль над мышью, вызовите View.requestPointerCapture().

requestPointerCapture() работает, только когда фокус установлен на иерархии представлений, содержащей ваше представление. По этой причине захват указателя нельзя выполнить в обратном вызове onCreate. Чтобы захватить указатель мыши, дождитесь взаимодействия игрока с ним, например при работе с главным меню, или используйте обратный вызов onWindowFocusChanged. Пример:

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

События, зарегистрированные requestPointerCapture(), отправляются в представление, которое зарегистрировало OnCapturedPointerListener. Пример:

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

Чтобы освободить мышь, например для взаимодействия с меню паузы, вызовите View.releasePointerCapture().