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

jMonkeyEngine3 урок (11) — Hello audio

Опубликованно: 05.04.2017, 20:35
Последняя редакция, Andry: 05.07.2017 22:28

Предыдущий: Hello Terrain, Следующий: Hello Effects

В этом уроке объясняется, как добавлять 3D-звук в игру, и как воспроизводить звуки вместе с событиями, например таким как щелчок мыши. Вы узнаете, как пользоваться аудио слушателем и аудио узлом. Вы также будете использовать слушатель действия и MouseButtonTrigger из предыдущего Hello Input урока, чтобы заставить щелчок мыши вызывать звук выстрела из оружия.

Для того, чтобы использовать примеры игровых ресурсов в вашем новом проекте в jMonkeyEngine SDK, щелкните [ПК мыши] ваш проект, выберите Свойства, перейдите в раздел Библиотеки, нажмите кнопку [Добавить библиотеку] и добавьте библиотеку jme3-test-data.

Пример кода

package jme3test.helloworld;
 
import com.jme3.app.SimpleApplication;
import com.jme3.audio.AudioNode;
import com.jme3.audio.AudioData.DataType;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
 
/** Пример 11 - проигрывание звука. */
public class HelloAudio extends SimpleApplication {
 
  private AudioNode audio_gun;
  private AudioNode audio_nature;
  private Geometry player;
 
  public static void main(String[] args) {
    HelloAudio app = new HelloAudio();
    app.start();
  }
 
  @Override
  public void simpleInitApp() {
    flyCam.setMoveSpeed(40);
 
    /** Просто синий куб плавающая в пространстве */
    Box box1 = new Box(1, 1, 1);
    player = new Geometry("Player", box1);
    Material mat1 = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md");
    mat1.setColor("Color", ColorRGBA.Blue);
    player.setMaterial(mat1);
    rootNode.attachChild(player);
 
    /** Пользовательские методы инициализации, смотрите ниже */
    initKeys();
    initAudio();
  }
 
  /** Мы создаем два аудио узла. */
  private void initAudio() {
    /* Звук выстрела из оружия должен быть запущен с помощью мыши. */
    audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", DataType.Buffer);
    audio_gun.setPositional(false);
    audio_gun.setLooping(false);
    audio_gun.setVolume(2);
    rootNode.attachChild(audio_gun);
 
    /* Звуки природы - продолжает играют циклично. */
    audio_nature = new AudioNode(assetManager, "Sound/Environment/Ocean Waves.ogg", DataType.Stream);
    audio_nature.setLooping(true);  // активировать непрерывное воспроизведение
    audio_nature.setPositional(true);   
    audio_nature.setVolume(3);
    rootNode.attachChild(audio_nature);
    audio_nature.play(); // Воспроизводить непрерывно!
 
  /** Объявление действия "Shoot", сопоставление его с триггером (Щелчок левой кнопкой мыши). */
  private void initKeys() {
    inputManager.addMapping("Shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
    inputManager.addListener(actionListener, "Shoot");
  }
 
  /** Определение действия "Shoot": проигрывание соответствующего звука. */
  private ActionListener actionListener = new ActionListener() {
    @Override
    public void onAction(String name, boolean keyPressed, float tpf) {
      if (name.equals("Shoot") && !keyPressed) {
        audio_gun.playInstance(); // играть каждый экземпляр один раз!
      }
    }
  };
 
  /** Перемещение слушателя с камерой - для 3D-аудио. */
  @Override
  public void simpleUpdate(float tpf) {
    listener.setLocation(cam.getLocation());
    listener.setRotation(cam.getRotation());
  }
 
}

При запуске образца, вы должны увидеть синий кубик. Вы должны услышать окружающие звуки природы. При нажатии кнопки мыши, вы услышите громкий выстрел.


Понимание кода

В методе initSimpleApp(), вы создаете простую геометрию синий куба, называющегося player и прикрепляете его на сцену — это просто произвольный образец контента, так что вы видите что-то при запуске аудио образца.

Давайте подробнее рассмотрим initAudio(), чтобы узнать, как использовать AudioNode.


AudioNode

Добавить звук в вашу игру довольно просто: сохранить ваши аудио файлы в assets/Sound каталоге. JME3 поддерживает форматы файлов Ogg Vorbis (.ogg) и Wave (.wav).

Для каждого звука, вы создаете AudioNode. Вы можете использовать AudioNode, как и любой узел графа сцены JME, например прикрепить его к другому узлу. Вы создаете один узел для звука огнестрельного оружия и один узел для звука окружающей среды т.е. природы.

  private AudioNode audio_gun;
  private AudioNode audio_nature;

Посмотрите на пользовательский метод initAudio(): Здесь вы инициализируете звуковые объекты и задаете их параметры.

audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", DataType.Buffer);
    ...
audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", DataType.Stream);

Эти две строки создают новые звуковые узлы из данных аудио файлов в AssetManager. Флажок DataType.Buffer означает, что вы хотите, чтобы эти звуки были буферизированы перед воспроизведением. (Если вы установите значение флага в DataType.Stream, звук будет потоковым, что имеет смысл для очень длинных звуков.)

Вы хотите, чтобы звук выстрела играл один раз (вы не хотите его зацикливания). Можно также указать его громкость как коэффициент усиления(при 0, звук отключен, при 2, он в два раза громче, и.т.д.).

    audio_gun.setPositional(false);
    audio_gun.setLooping(false);
    audio_gun.setVolume(2);
    rootNode.attachChild(audio_gun);
Обратите внимание, что setPositional(false) очень важно, когда вы используете стерео звук. Звуки определенного места всегда должны быть моно аудио файлы, иначе движок будет напоминать вам об этом треском.

Звуки природы отличаются: вы хотите, чтобы цикл продолжался непрерывно, в качестве фонового звука. Вот почему вы задаёте looping в true, и сразу вызываете метод play() на узле. Вы также можете установить его громкость в 3.

    audio_nature.setLooping(true);  // активировать непрерывное воспроизведение
    . . .   
    audio_nature.setVolume(3);
    rootNode.attachChild(audio_nature);
    audio_nature.play(); // Воспроизводить непрерывно!

Здесь вы создаете audio_nature позиционный звук, который исходит из определенного места. Для этого вы задаете узлу определенное преобразование, в данном примере, вы выбираете Vector3f.ZERO (которому соответствуют координаты 0.0f, 0.0f, 0.0f, центр сцены.) поскольку jME поддерживает 3D-звук, вы теперь можете услышать этот звук, исходящий из этого конкретного места. Создание позиционных звуков не является обязательным. Если вы не используете эти строки, окружающий звук исходит в любом направления.

    ...
    audio_nature.setPositional(true);
    audio_nature.setLocalTranslation(Vector3f.ZERO.clone());
    ...
Прикрепите AudioNodes к графу сцены, как и все узлы чтобы сделать некоторые подвижные узлы актуальными. Если вы их не присоедините их, они все равно будут слышны, и вы не получите сообщение об ошибке, но 3D-звук не будет работать, как должен. AudioNodes может быть прикреплен непосредственно к корневому узлу, или они могут быть прикреплены к внутреннему узлу, который перемещается по сцене, и тогда AudioNode и само место из которого исходит звук будут перемещатся вместе.
playInstance всегда воспроизводит звук из местоположения AudioNode, по этому можно спокойно делать много выстрелов из одного пистолета (к примеру), однако, если несколько пистолетов стреляют одновременно тогда AudioNode необходим для каждого из них.

Срабатывание Звука

Давайте подробнее рассмотрим initKeys(): Как вы узнали в предыдущих уроках, вы используете InputManager, чтобы реагировать на ввод данных пользователем. Здесь вы добавили сопоставление для щелчка [ЛК мыши], и называете это новое действие Shoot.

/** Объявление действия "Shoot", сопоставление его с триггером (Щелчок левой кнопкой мыши). */
  private void initKeys() {
    inputManager.addMapping("Shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
    inputManager.addListener(actionListener, "Shoot");
  }

Настройка ActionListener также должна быть знакома из предыдущих уроков. Вы указываете, что когда триггер (кнопка мыши) нажата и отпускается, вы хотите воспроизвести звук выстрела из пистолета.

  /** Определение действия "Shoot": проигрывание соответствующего звука. */
  private ActionListener actionListener = new ActionListener() {
    @Override
    public void onAction(String name, boolean keyPressed, float tpf) {
      if (name.equals("Shoot") && !keyPressed) {
        audio_gun.playInstance(); // играть каждый экземпляр один раз!
      }
    }
  };

Так как вы хотите иметь возможность выстрелить повторно быстро, вы не хотите ждать, пока предыдущий звук выстрела закончится воспроизводится и позволит воспроизводится следующему. Поэтому вы воспроизводите этот звук с помощью метода playInstance(). Это означает, что каждый щелчок запускает новый экземпляр звука, так что два экземпляра могут накладываться друг на друга. Вы устанавливаете этот звук не как цикл, поэтому каждый экземпляр будет проигрываться только один раз. Как и должно быть при огнестрельном выстреле.


Звук Окружающей среда или Ситуативный?

Два разновидности звуков, два различных варианта использования:

  • Пистолетный выстрел ситуативный. Вы хотите воспроизвести его только один раз, сразу после его запуска.
    • Вот почему вы задаете setLooping(false).
  • Звук природы является окружающим фоновым шумом. Вы хотите воспроизвести его с самого начала, и до тех пор пока идет игровой процесс.
    • Вот почему вы задаете setLooping(true).

Теперь каждый звук знает, цикличный ли он или нет.

Помимо разницы в булевых значений в looping, другое отличие заключается в том как play().playInstance() вызывается в этих узлах:

  • Вы начинаете воспроизводить фоновый звук природы сразу после того, как вы создали его, в методе initAudio().
  • audio_nature.play(); // Воспроизводить непрерывно!
    
  • Звук выстрела, однако, запускается ситуативно, один раз, только как часть действия ввода Shoot, которое вы определили в ActionListener.
  •   /** Определение действия "Shoot": проигрывание соответствующего звука. */
      private ActionListener actionListener = new ActionListener() {
        @Override
        public void onAction(String name, boolean keyPressed, float tpf) {
          if (name.equals("Shoot") && !keyPressed) {
            audio_gun.playInstance(); // играть каждый экземпляр один раз!
          }
        }
      };
    

Буферизированный или Потоковый?

В 3,1-альфа2, Enum в конструкторе AudioNode определяет буферизуется ли аудио или проигрывается потоком. Например:

audio_gunshot = new AudioNode(assetManager, "Sound/Effects/Gun.wav", DataType.Buffer); // буферизированный
...
audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", DataType.Stream); // потоковый

Как правило, потоковые длинные звуки, а в буферизированные короткие звуки.

Обратите внимание, что потоковые звуки не могут быть зациклены (т.е. setLooping не будет работать так, как вам хотелось бы). Проверьте getStatus на узле и, если он остановился то воссоздайте узел. (В 3.1-alpha2 это уже не так). Если вы все еще запустите 3.0, то все вышеперечисленное останется в силе, а вместо DataType будет использоваться простое логическое значение.


Play() или PlayInstance()?

 audio.play() audio.playInstance()
 Воспроизводит буферизированных звуков. Воспроизводит буферизированных звуков.
 Воспроизводит потоковые звуки. Невозможно воспроизвести потоковые звуки.
 Один и тот же звук не может воспроизводиться дважды в одно и то же время. Одни и те же звуки могут воспроизводиться несколько раз и накладываться друг на друга.

Ваше ухо в сцене

Чтобы создать 3D-аудиоэффект, JME3 должен знать положение источника звука, и положение ушей игрока. Уши представлены 3D объектом Audio Listener. Объект слушатель является объектом по умолчанию в SimpleApplication.

Чтобы максимально использовать эффект 3D-звука, вы должны использовать метод simpleUpdate() для перемещения и поворота слушателя (ушей игрока) вместе с камерой (глаза игрока).

  public void simpleUpdate(float tpf) {
    listener.setLocation(cam.getLocation());
    listener.setRotation(cam.getRotation());
  }

Если вы этого не сделаете, то результаты 3D-звука будут довольно случайными.


Глобальный, Направленный, Позиционный?

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

Есть смысл сделать сделать звук выстрела позиционным огнестрельный звук, а окружающие звуки приходящим со всех сторон. Как вам решить, какой тип 3D-звука использовать в следующих случаях?

  • В игре с движущимися врагами вы можете захотеть сделать пистолетный выстрел или звуки ходьбы позиционными звуками. В этом случае вам необходимо переместить AudioNode к местоположению противника, до запуска playInstance(). Таким образом, игрок со стерео динамиками будет слышать с какого направления идет враг.
  • Кроме того, у вас могут быть уровни игры, где вы хотите чтобы один фоновый звук играл глобально, т.е. по всему уровню. В этом случае, вы должны сделать AudioNode ни позиционным, ни направленным (установите оба значения как false).
  • Если вы хотите, чтобы звук «поглощался стенами», и транслировался только в одном направлении, вам нужно сделать AudioNode направленным. В данном руководстве не рассматриваются направленные звуки, вы можете прочитать расширенную Audio документацию здесь.

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


Вывод

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

Реализация звука JME также поддерживает более продвинутые эффекты, такие как реверберация и эффект Доплера. Используйте эти «профессиональные функции», чтобы сделать аудио звучание различным в зависимости от того, звучит ли он в коридоре, в пещере, на улице, или в комнате с ковровым покрытием. Узнайте больше об эффектах окружающей среды из примера кода, включенного в каталог jme3test и из расширенной Audio документации.

Хотите что бы с огнём и взрывами, шли ваши звуки? Тогда читайте дальше, чтобы узнать больше об эффектах.


Смотрите также:

  • Audio

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

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

    Содержание

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