Ввод мыши

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

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

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

  • Состояния наведения для контекстных меню, а не действия по нажатию и удержанию
  • Щелкните правой кнопкой мыши, чтобы увидеть альтернативные действия, которые происходят при длительном нажатии или в контекстном меню.
  • Mouselook — это экшн-игры от первого или третьего лица, а не событие нажатия и перетаскивания.

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

Обработка ввода для Google Play Games на ПК идентична обработке ввода в 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 Games для ПК переключается в правильный режим при запуске игры. При работе в эмуляторе разработчика вам необходимо щелкнуть правой кнопкой мыши значок на панели задач, выбрать «Параметры разработчика» , а затем режим ПК (KiwiMouse), чтобы получить необработанный ввод с помощью мыши.

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

После этого о движении мыши сообщает View.onGenericMotionEvent с источником SOURCE_MOUSE указывающим, что это событие мыши.

Котлин

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

Ява

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 .

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

Котлин

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
}

Ява

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 :

Котлин

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

Ява

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

В этом примере действие обрабатывается аналогично событиям наведения:

Котлин

// 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
}

Ява

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 для горизонтального смещения. Например:

Котлин

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
}

Ява

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 . Например:

Котлин

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

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

Ява

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

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

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

Котлин

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

Ява

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

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