Докуметация Cтарт Статьи Форум Лента Вход
Не официальное русскоязычное сообщество
Главная
    Документация jMonkeyEngine
        jMonkeyEngine Уроки и Документация
            jMonkeyEngine3: Привет мир, Обучающая Серия
                jMonkeyEngine 3 Урок (5) — Hello Input System

jMonkeyEngine 3 Урок (5) — Hello Input System

Опубликованно: 04.04.2017, 1:38
Последняя редакция, Andry: 25.03.2018 21:17

Предыдущий: Hello Loop, Следующий: Hello Material

По умолчанию SimpleApplication устанавливает управление камерой, которое позволяет управлять камерой WASD клавишами, клавишами со стрелками и мышью. Таким образом вы изначально можете использовать летающую камеру с видом от первого лица. Но что, если вам нужна камера от третьего лица, или вы хотите задать клавиши для вызова специальных игровых действий?

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

Пример кода

package jme3test.helloworld;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.math.ColorRGBA;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;

/** Пример 5 - как назначать клавишам и кнопкам мыши, действия */
public class HelloInput extends SimpleApplication {

    public static void main(String[] args) {
        HelloInput app = new HelloInput();
        app.start();
    }

    protected Geometry player;
    private boolean isRunning = true;

    @Override
    public void simpleInitApp() {
        Box b = new Box(1, 1, 1);
        player = new Geometry("Player", b);
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        player.setMaterial(mat);
        rootNode.attachChild(player);
        initKeys(); // загрузить мои пользовательские настройки сочетаний клавиш
    }

  /** Настройка кнопок: назначить названия действий для клавиш ввода */
    private void initKeys() {
    // Можно сопоставить один или несколько вводов одному названию действия
        inputManager.addMapping("Pause",  new KeyTrigger(KeyInput.KEY_P));
        inputManager.addMapping("Left",   new KeyTrigger(KeyInput.KEY_J));
        inputManager.addMapping("Right",  new KeyTrigger(KeyInput.KEY_K));
        inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE),
                                          new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
    // Добавить названия к слушателю действий.
        inputManager.addListener(actionListener, "Pause");
        inputManager.addListener(analogListener, "Left", "Right", "Rotate");

    }

    private final ActionListener actionListener = new ActionListener() {
        @Override
        public void onAction(String name, boolean keyPressed, float tpf) {
            if (name.equals("Pause") && !keyPressed) {
                isRunning = !isRunning;
            }
        }
    };

    private final AnalogListener analogListener = new AnalogListener() {
        @Override
        public void onAnalog(String name, float value, float tpf) {
            if (isRunning) {
                if (name.equals("Rotate")) {
                    player.rotate(0, value * speed, 0);
                }
                if (name.equals("Right")) {
                    Vector3f v = player.getLocalTranslation();
                    player.setLocalTranslation(v.x + value * speed, v.y, v.z);
                }
                if (name.equals("Left")) {
                    Vector3f v = player.getLocalTranslation();
                    player.setLocalTranslation(v.x - value * speed, v.y, v.z);
                }
            } else {
                System.out.println("Нажмите P, чтобы возобновить.");
            }
        }
    };
}

Соберите и запустите этот пример.

  • Нажмите пробел или кликните мышью, чтобы повернуть куб.
  • Нажмите J и K, чтобы переместить куб.
  • Нажмите P, чтобы приостановить или возобновить игру. Во время паузы, игра не должна реагировать на любой ввод, кроме P.

Описание сопоставлений и триггеров

Сначала вы регистрируете каждое сопоставляемое название действия с триггером(и) предназначенным(и) для него. Помните следующее:

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

Давайте взглянем на код:

  1. Вы регистрируете сопоставление названия действия Rotate с триггером клавиши Пробел.
    new KeyTrigger(KeyInput.KEY_SPACE)
  2. В той же строке, вы также регистрируете для Rotate альтернативный триггер — клик мыши
    new MouseButtonTrigger(MouseInput.BUTTON_LEFT)
  3. Вы сопоставили Pause, Left, Right с клавишами P, J, K.
    // Можно сопоставить один или несколько вводов одному названию действия
    inputManager.addMapping("Pause",  new KeyTrigger(KeyInput.KEY_P));
    inputManager.addMapping("Left",   new KeyTrigger(KeyInput.KEY_J));
    inputManager.addMapping("Right",  new KeyTrigger(KeyInput.KEY_K));
    inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE),
                                      new MouseButtonTrigger(MouseInput.BUTTON_LEFT));

Теперь нужно зарегистрировать ваши сопоставленные триггеры.

  1. Вы регистрируете действие пауза в ActionListener, потому что оно действие «вкл/выкл».
  2. Вы регистрируете действия движения в AnalogListener, потому что это последовательно повторяющиеся действия.
    // Добавим названия к слушателю действий.
    inputManager.addListener(actionListener,"Pause");
    inputManager.addListener(analogListener,"Left", "Right", "Rotate");

Этот код реализуется в методе simpleInitApp(). Но так как мы, скорее всего, добавим много клавиш, мы извлечем эти строки и разместим их во вспомогательного методе, initKeys(). Метод initKeys() не является частью интерфейса Input Control — вы можете назвать его как хотите. Только не забудьте вызывать ваш метод из метода simpleInitApp().


Реализация действий

Вы назначили названия действий триггерам ввода. Теперь вы задаёте сами действия.

Двумя важными методами здесь являются ActionListener с методом OnAction(), и AnalogListener со своим методом onAnalog(). В этих двух методах, вы протестируете каждое сопоставленное названия действия, и вызовете уже сами действия игры которые вы хотите осуществить нажатием клавиш.

В этом примере, мы вызвали следующие действия:

  1. Rotate сопоставляется с запуском действия player.rotate(0, value, 0).
  2. Left и Right сопоставляются с увеличение и уменьшение х координат player.
  3. Pause сопоставляется с изменением булевого значения isRunning.
  4. Мы также хотим проверить булевого значение isRunning перед тем как будет выполнено какое-либо действие (кроме отключения паузы).
    private final ActionListener actionListener = new ActionListener() {
        @Override
        public void onAction(String name, boolean keyPressed, float tpf) {
            if (name.equals("Pause") && !keyPressed) {
                isRunning = !isRunning;
            }
        }
    };

    private final AnalogListener analogListener = new AnalogListener() {
        @Override
        public void onAnalog(String name, float value, float tpf) {
            if (isRunning) {
                if (name.equals("Rotate")) {
                    player.rotate(0, value * speed, 0);
                }
                if (name.equals("Right")) {
                    Vector3f v = player.getLocalTranslation();
                    player.setLocalTranslation(v.x + value * speed, v.y, v.z);
                }
                if (name.equals("Left")) {
                    Vector3f v = player.getLocalTranslation();
                    player.setLocalTranslation(v.x - value * speed, v.y, v.z);
                }
            } else {
                System.out.println("Нажмите P, чтобы возобновить.");
            }
        }
    };

Вы также можете комбинировать оба слушателя в одном. Движок будет отправлять соответствующие события к каждому методу (OnAction или onAnalog).

Например:

private class MyCombinedListener implements AnalogListener, ActionListener {

    @Override
    public void onAction(String name, boolean keyPressed, float tpf) {
        if (name.equals("Pause") && !keyPressed) {
            isRunning = !isRunning;
        }
    }

    @Override
    public void onAnalog(String name, float value, float tpf) {
        if (isRunning) {
            if (name.equals("Rotate")) {
                player.rotate(0, value * speed, 0);
            }
            if (name.equals("Right")) {
                Vector3f v = player.getLocalTranslation();
                player.setLocalTranslation(v.x + value * speed, v.y, v.z);
            }
            if (name.equals("Left")) {
                Vector3f v = player.getLocalTranslation();
                player.setLocalTranslation(v.x - value * speed, v.y, v.z);
            }
        } else {
            System.out.println("Нажмите P, чтобы возобновить.");
        }
    }
}
// ...
inputManager.addListener(combinedListener, new String[]{"Pause", "Left", "Right", "Rotate"});

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


Аналоговый, нажатый или отпущенный?

Технически, каждый ввод может быть либо «аналоговым» либо «цифровым» действием. Вот как вы узнаете, какой слушатель является правильным для каждого типа ввода.

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

  • Параметры:
    1. JME дает вам доступ к названию триггера действии.
    2. JME дает вам доступ к постепенно нарастающему значению, показывая силу этого ввода. В случае с нажатием клавиш, это будет tpf значение, при котором она была нажата начиная с последнего кадра. Для других вводов, таких как с помощью джойстика, которые дают аналоговое управление, значение будет также указывать на силу ввода, оно будет умножается на tpf. Пример этого см. в дополнительном коде.

Для того чтобы узнать время, которое была нажата клавиша можно получить накопленное значение. Аналоговый слушатель можно также объединить со слушателем действий, что бы вы были уведомлены, о том что клавиша отпущена.

  • Пример: События перемещений (например влево, вправо, поворот, бежать, атаковать), ситуации, в которых вы постоянно взаимодействуете.

Сопоставления зарегистрированные в ActionListener являются либо цифровыми либо действиями — «Нажата» или «отпущена»? «Включен» или «выключен»?

  • Параметры:
    1. JME дает вам доступ к названиям триггера действия.
    2. JME дает вам доступ в булевому значению нажата ли клавиша или отпущена.
  • Пример: кнопка паузы, стрельба, выбор, прыжки, взаимодействия осуществляемые одним нажатием клавиши или одним кликом мыши.
Очень часто вы хотите, что бы действие запускалось только один раз когда клавиша отпускается. Например при открытии двери, меняется логическое состояния, или собирание вещей. Для достижения этой цели, вы используете ActionListener и тест … && !keyPressed. Для примера, рассмотрим код кнопки Pause:

      if (name.equals("Pause") && !keyPressed) {
        isRunning = !isRunning;
      }

Таблица триггеров

Вы можете найти список констант ввода в файлах src/core/com/jme3/input/KeyInput.java, JoyInput.java, и MouseInput.java. Вот краткий обзор наиболее распространенных констант триггеров:

 Триггер Код
 Кнопка мыши: левый клик MouseButtonTrigger(MouseInput.BUTTON_LEFT)
 Кнопка мыши: правый клик MouseButtonTrigger(MouseInput.BUTTON_RIGHT)
 Клавиатура: символы и цифры KeyTrigger(KeyInput.KEY_X)
 Клавиатура: пробел KeyTrigger(KeyInput.KEY_SPACE)
 Клавиатура: Return, Enter KeyTrigger(KeyInput.KEY_RETURN), KeyTrigger(KeyInput.KEY_NUMPADENTER)
 Клавиатура: Escape KeyTrigger(KeyInput.KEY_ESCAPE)
 Клавиатура: стрелки KeyTrigger(KeyInput.KEY_UP), KeyTrigger(KeyInput.KEY_DOWN), KeyTrigger(KeyInput.KEY_LEFT), KeyTrigger(KeyInput.KEY_RIGHT)
Если вы не можете вспомнить константу ввода во время разработки. Вы можете воспользоваться функцией автозавершения кода IDE: Поместите каретку после точки, например как KeyInput.| и автозавершение кода предложит вам список констант ввода.

Упражнения

  1. Добавьте сопоставления для перемещения игрока (куба) вверх и вниз клавишами H и L !
  2. Выключите flyCam и переопределите клавиши WASD.
    • Используйте flyCam.setEnabled(false);

  3. Измените сопоставления, так чтобы вы смогли также вызвать движения вверх, вниз движениями прокрутки колёсика мыши!
    • Используйте new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)

  4. В какой ситуации было бы лучше использовать переменные вместо литералов для определения MouseInput/KeyInput?
int usersPauseKey = KeyInput.KEY_P;
...
inputManager.addMapping("Pause", new KeyTrigger(usersPauseKey));
Ссылка на решения предлагаемые пользователями: Некоторые предлагаемые решения
Но, сначала, попытайтесь решить все сами!

Вывод

Теперь вы можете добавлять пользовательские взаимодействия в вашу игру: вы знаете, что сначала вы должны задать сопоставления клавиш, а затем действия для каждого сопоставления. Вы научились реагировать на события мыши и нажатия клавиш. Вы понимаете разницу между «аналоговым» (постепенно повторяющимися) и «цифровым» (вкл/выкл) вводами.

Теперь вы можете уже написать небольшую интерактивную игру! Но было бы круче, если бы удалось сделать эти стандартные кубы немного более интересными? Давайте продолжим изучение материалов.


Переведено для jmonkeyengine.ru, оригинал
Автор перевода: BuGaGa

Добавить комментарий

Содержание

jMonkeyEngine.ru © 2017. Все права сохранены.