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

jMonkeyEngine3 урок (11) — Hello audio

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

Предыдущий: 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());
    ...
Прикрепляйте AudioNode-ы к графу сцены, как и все узлы чтобы делать некоторые подвижные узлы актуальными. Если вы их не присоедините, они все равно будут слышны, и вы не получите сообщение об ошибке, но 3D-звук не будет работать, как должен. AudioNode-ы могут быть прикреплены непосредственно к корневому узлу, или они могут быть прикреплены к внутреннему узлу, который перемещается по сцене, и тогда 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). Объект 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, Andry

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

    Содержание

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