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

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

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

Предыдущий: 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;
  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 ActionListener actionListener = new ActionListener() {
    public void onAction(String name, boolean keyPressed, float tpf) {
      if (name.equals("Pause") && !keyPressed) {
        isRunning = !isRunning;
      }
    }
  };

  private AnalogListener analogListener = new AnalogListener() {
    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("Press P to unpause.");
      }
    }
  };
}

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

  • Нажмите пробел или кликните мышью, чтобы повернуть куб.
  • Нажмите 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));
nputManager.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 ActionListener actionListener = new ActionListener() {
    public void onAction(String name, boolean keyPressed, float tpf) {
      if (name.equals("Pause") && !keyPressed) {
        isRunning = !isRunning;
      }
    }
  };

  private AnalogListener analogListener = new AnalogListener() {
    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("Press P to unpause.");
      }
    }
  };

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

  private MyCombinedListener combinedListener = new MyCombinedListener();

  private static class MyCombinedListener implements AnalogListener, ActionListener {
    public void onAction(String name, boolean keyPressed, float tpf) {
      if (name.equals("Pause") && !keyPressed) {
        isRunning = !isRunning;
      }
    }

    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("Press P to unpause.");
      }
    }
  }
// ...
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. Все права сохранены.