Докуметация Cтарт Статьи Форум Лента Вход
Не официальное русскоязычное сообщество

Application States

Опубликованно: 04.05.2017, 22:44
Последняя редакция, Andry: 17.08.2017 22:09

Класс com.jme3.app.state.AppState является настраиваемым интерфейсом jME3, который позволяет вам контролировать глобальную логику игры, общую механику игры. (Чтобы управлять поведением объекта Spatial, см. Раздел «Пользовательские элементы Control». Controls и AppStates можно использовать вместе.)

Обзор

Примеры использования

Во время разработки игры бывают ситуации, при которых вы думаете:

  • Ввод с мыши и клавиатуры обрабатывать по отдельности в игре или в главном меню? Можно ли сгруппировать набор параметров обработчика ввода, а затем активировать и деактивировать их все за один шаг?
  • У меня есть внутриигровая сцена, редактор персонажей и экран Quarters. Могу ли я сгруппировать набор узлов и поведения, и поменять местами их в одном шаге?
  • Когда я делаю паузу в игре, я хочу, чтобы анимация простоя продолжалась, но все остальные циклы и игровые события должны остановиться. Как задать, что происходит, когда игра приостанавливается/возобновляется?
  • У меня есть условный блок, который занимает много места в цикле simpleUpdate(). Могу ли я завершить этот блок кода, включить его или выключить за один шаг?
  • Могу ли я поместить в отдельный пакет(package) все, что относиться к игре, и все, что относиться к меню на экране, и переключаться между этими двумя «большими структурами за один шаг?

Ты можешь! Это то, для чего существуют AppStates. Класс AppState является подмножеством (или расширением) вашего приложения. Каждый класс AppState имеет доступ ко всем полям вашего основного приложения (AssetManager, ViewPort, StateManager, InputManager, RootNode, GuiNode и т. Д.) И перехватывает основной цикл обновления. AppState может содержать:

  • Подмножество полей классов, функций, методы (данные о состоянии игры и средства доступа),
  • Подмножество элементов GUI и их слушателей,
  • Подмножество обработчиков ввода и mappings,
  • Подмножество узлов, которые вы загружаете и прикрепляете к корневому узлу(rootNode),
  • Подмножество условных действий, к которым вы переходите в цикле simpleUpdate()
  • Подмножество других AppStates и элементов управления
  • … или их комбинации.

Поддерживаемые функции

Каждое AppState позволяет вам определить, что происходит с ним в следующих ситуациях:

  • AppState инициализируется: вы загружаете и инициализируете игровые данные, InputHandlers, AppStates и элементы управления и присоединяете узлы.
    AppState, если можно так выразиться, выполняет свой собственный метод simpleInitApp().
  • AppState был включен (сняли с паузы): это переключает логическое isEnabled() в true. Здесь вы прикрепляете узлы и слушатели, которые должны стать активными во время его работы.
  • Пока приложение AppState работает/пауза: вы можете запросить значение isEnabled(), чтобы определять паузу и снятия с паузы в игре в цикле update(). В update() вы опрашиваете и изменяете состояние игры, изменяете граф сцены и запускаете события. Проверьте, если IsEnabled() false, то напишите код, который пропускает выполнение ненужных разделов update() в AppState().
    Каждый AppState имеет свой собственный цикл обновления, который подключается к основному циклу simpleUpdate() (обратный вызов).
  • AppState был отключен (пуза): это переключает логическое isEnabled() в false. Здесь вы переключаете все объекты на их «приостановленное» поведение.
  • AppState очищается: здесь вы решаете, что произойдет, когда AppState отсоединяется. Сохраните это AppState игровое состояние, отмените регистрацию Controls и InputHandlers, отделите связанные AppStates, отсоедините узлы от корневого узла и т. Д.
AppStates очень удобны для переключений между пузой/возобновлением целых наборов других AppStates. Например, InGameState (загружает графический интерфейс в игре, активирует сопоставления входных данных с помощью щелчка мыши, внутри своего игрового контента, запускает игровой цикл) по сравнению с MainScreenState (останавливает игровой цикл, сохраняет и отсоединяет игровой контент, переключается в GUI на экран меню, переключается на Сопоставления входных данных по щелчку мыши).

Применение

Для реализации игровой логики:

  1. Создайте один экземпляр AbstractAppState для каждого набора игровой механики.
  2. Реализуйте поведение игры в методе update() AppState.
    • Вы можете передавать пользовательские данные в качестве аргументов конструктору.
    • AppState имеет доступ ко всему внутри области приложения через объект приложения app
  3. Создайте и присоедините AppState к AppStateManager (stateManager.attach(myAppState);) и инициализируйте его.
  4. Включение и отключение (возобновление и пауза) AppStates, что вам нужны во время игры.
  5. Отключите AppState от AppStateManager (stateManager.detach(myAppState);) и очистите его.

Когда вы добавляете несколько AppStates в одно Приложение и активируете их, их методы initialize() и update() выполняются в том порядке, в котором AppStates были добавлены в AppStateManager.

Образцы кода

JME3 поставляется с BulletAppState, который реализует поведение физики (используя библиотеку jBullet). Вы, например, могли написать AppState искусственного интеллекта для управления всеми вашими вражескими юнитами. Существующие примеры в базе кода включают:

AppState

Интерфейс AppState позволяет инициализировать наборы объектов и брать на себя набор непрерывно выполняющегося кода из основного цикла.

Методы AppState  Применение
initialize(asm,app)  Когда это AppState добавляется в игру, RenderThread инициализирует AppState и затем вызывает этот метод. Здесь вы можете изменить граф сцены (например, присоединить узлы). Чтобы получить доступ к основному приложению, вызовите:

super.initialize(stateManager, app);
this.app = (SimpleApplication) app;

cleanup()  Этот метод выполняется после удаления AppState из игры. Здесь вы реализуете код очистки , когда состояние становится отсоединить. Здесь вы можете изменить граф сцены (например, отсоединить узлы).
update(float tpf)  Здесь вы реализуете поведение, которое хотите привязать к циклу simpleUpdate(), пока он состояние привязано к игре. Здесь вы можете изменить граф сцены.
isInitialized()  Ваши реализации этого интерфейса должны возвращать правильное соответствующее логическое значение. (См. AbstractAppState)
setEnabled(true)
setEnabled(false) 
Временно разрешает или запрещает AppState. (См. AbstractAppState)
isEnabled()  Проверьте, включен ли или отключен AppState. Ваша реализация должна учитывать логическое значение. (См. Аннотация AppState)
stateAttached(asm)
stateDetached(asm) 
AppState знает, когда он подключен или отключен от AppStateManager, и запускает эти два метода. Не изменяйте граф сцены! (Обычно не используется.)
render(RenderManager rm)  Отображает состояние, плюс ваши дополнительные настройки. (Обычно не используется.)
postRender()  Вызывается после того, как все команды рендеринга будут сброшены, включая ваши дополнительные настройки. (Обычно не используется.)

AbstractAppState

Класс AbstractAppState уже реализует некоторые общие методы (isInitialized(), setEnabled(), isEnabled()) и упрощает создание настраиваемых AppStates. Рекомендуется расширять AbstractAppState и переопределять остальные методы AppState: initialize(), setEnabled(), cleanup().

Определение:

public class MyAppState extends AbstractAppState {

    private SimpleApplication app;

    private Node x = new Node("x");  // Некоторые поля пользовательских классов...
    public Node getX(){ return x; }  // Некоторые пользовательские методы...

    @Override
    public void initialize(AppStateManager stateManager, Application app) {
      super.initialize(stateManager, app);
      this.app = (SimpleApplication)app;          // переход к более конкретному классу

      // инициализация, не зависящая от состояния: ПАУЗА или ВЫПОЛНЯЕТСЯ
      this.app.getRootNode().attachChild(getX()); // изменение графа сцены...
      this.app.doSomething();                     // вызов пользовательских методов...
   }

   @Override
    public void cleanup() {
      super.cleanup();
      // Отменить регистрацию всех моих слушателей, отсоединить все мои узлы и.т.д.
      this.app.getRootNode().detachChild(getX()); // изменение графа сцены...
      this.app.doSomethingElse();                 // вызов пользовательских методов...
    }

    @Override
    public void setEnabled(boolean enabled) {
      // Пауза и возобновление
      super.setEnabled(enabled);
      if(enabled){
        // инициализация, которая используется, пока состояние ВЫПОЛНЯЕТСЯ
        this.app.getRootNode().attachChild(getX()); // изменение графа сцены...
        this.app.doSomethingElse();                 // вызов пользовательских методов...
      } else {
        // Убрать все, что не нужно, пока состояние ПАУЗА
        ...
      }
    }

    // Обратите внимание, что обновление вызывается только тогда, когда состояние подключено и включено.
    @Override
    public void update(float tpf) {
      // В то время как игра ВЫПОЛНЯЕТСЯ
      this.app.getRootNode().getChild("blah").scale(tpf); // изменение графа сцены...
      x.setUserData(...);                                 // вызов пользовательских методов...
    }

}

BaseAppState

Новый класс BaseAppState был представлен как часть обновлений, сделанных для интерфейса AppState.AbstractAppState — самая минимальная из минимальных реализаций интерфейса AppState. Вам по существу все еще нужно делать все самим, включая правильное включение/выключение/инициализацию/завершение логики. Теперь вы просто расширяете BaseAppState, и вы получаете onEnable() и onDisable() уже разработанные для вас.

Описание:

public class MyBaseAppState extends BaseAppState {       
    @Override   
    protected void initialize(Application app) {       
        //Технически безопасно выполнять всю инициализацию и очистку в методах         
        //onEnable()/onDisable(). Выбор использовать initialize() и      
        //cleanup() для этого, это вопрос специфики производительности для        
        //разработчика.       
        //TODO: Инициализация AppState, например, присоединение spatials к rootNode
    }

    @Override   
    protected void cleanup(Application app) {       
        //TODO: очистить то, что вы инициализировали в методе initialize,        
        //например, удалить все spatials из rootNode
    }

    //onEnable()/onDisable() может использоваться для управления вещами, которые должны    
    //существовать только при включенном state. Основным примером были    
    //прикрепленный граф сцены или прикрепленный слушатель ввода.  
    @Override   
    protected void onEnable() {       
        //Вызывается, когда state полностью включено, то есть: установлено и         
        //isEnabled() является истинным или когда статус setEnabled() изменяется после    
        //прикрепления state.
    }
    
    @Override   
    protected void onDisable() {       
        //Вызывается, когда state было ранее включено, но теперь отключено         
        //либо потому, что вызывается setEnabled (false), либо состояние      
        //очищается.    
    }       

    @Override   
    public void update(float tpf) {       
        //TODO: реализовать поведение во время выполнения
    }
    
}

Вот следующие известные изменения BaseAppState:

  • Вам больше не нужно вызывать super.initialize(stateManager, app), потому что теперь он вызывается для васe при инициализации BaseAppStat.
  • Вам больше не нужно использовать SimpleApplication для доступа к AssetManager, AppStateManager, и вы даже можете получить State напрямую. Геттеры getApplication(), getAssetManager(), getState(type) и их методы доступны вам сразу же. Тем не менее, вам все равно придется использовать SimpleApplication для получения rootNode.
  • Вам больше не нужно вызывать super в cleanup, теперь это делается за вас.
  • Теперь безопасно выполнять всю инициализацию и cleanup в методах onEnable()/onDisable().
  • Cleanup и setEnabled теперь встроены в журнал.

Вы используете BaseAppState, или если хотите AbstractAppState, кроме упомянутого выше, и какой вы используете, полностью зависит от вас. Однако BaseAppState упрощает вашу жизнь и рекомендуется к использованию теперь.

См. BaseAppState для получения дополнительной информации.

Пауза и приостановка

Вы определяете, что делает AppState при паузе или возобновлении, в методах setEnabled() и update(). Вызовите myState.setEnabled(false) во всех состояниях, которые вы хотите приостановить. Вызовите myState.setEnabled(true) для всех состояний, которые вы хотите возобновить.

AppStateManager

Com.jme3.app.state.AppStateManager содержит список AppStates для приложения. AppStateManager гарантирует, что активные AppStates могут изменять граф сцены, и что выполняются циклы update() активных AppStates. Для каждого приложения имеется один AppStateManager. Обычно вы прикрепляете несколько AppStates к одному AppStateManager, но одно и то же состояние может быть присоединено только один раз.

Методы AppStateManager  Применение
hasState(myState)  Связан ли объект AppState с myState?
getState(MyAppState.class)  Возвращает первое присоединенное состояние, являющееся экземпляром подкласса MyAppState.class.

Методы рендеринга AppStateManager(), postRender(), cleanup() являются внутренними, их игнорируют, пользователи никогда не вызывают их напрямую.

  • Если отсоединенный AppState присоединить, тогда initialize() будет вызываться при следующем прохождении рендера.
  • Если присоединенный AppState отсоединен, то cleanup() будет вызван при следующем прохождении рендера.
  • Если вы присоедините уже прикрепленную AppState, то второе вложение будет no-op и вернет false.
  • Если вы подключаете и отсоединяете два AppState в одном фрейме, то ни инициализация(), ни очистка() не будут вызываться, хотя, если будет вызывать по одному из них, оба будут.
  • Если вы оба отсоедините, а затем снова присоедините к AppState в одном фрейме, то при следующем обновлении будут вызываться методы cleanup() и initialize() в этом порядке.

Рекомендации

Связь между AppStates

Доступ к другим AppStates (чтение и запись в них) возможен только из определенных мест: из метода update() элемента Control, из метода update() AppState и из цикла SimpleUpdate() SimpleApplication. Не связывайтесь с AppState из других мест, потому что из других методов вы не можете контролировать порядок модификаций; Игра может выйти из синхронизации, потому что вы не можете знать, когда (в какой полузавершенный шаг изменения другого состояния) будет выполнена ваша модификация.

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

this.app.getStateManager().getState(MyAppState.class).doSomeCustomStuffInThisState();

Инициализировать Поля Класса Familiar

Чтобы получить доступ к полям класса Simple Application так, как вы привыкли, инициализируйте их локальными переменными, как показано в следующем шаблоне AppState:

private SimpleApplication app;
private Node              rootNode;
private AssetManager      assetManager;
private AppStateManager   stateManager;
private InputManager      inputManager;
private ViewPort          viewPort;
private BulletAppState    physics;

public class MyAppState extends AbstractAppState {
  @Override
  public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    this.app = (SimpleApplication) app; // can cast Application to something more specific
    this.rootNode     = this.app.getRootNode();
    this.assetManager = this.app.getAssetManager();
    this.stateManager = this.app.getStateManager();
    this.inputManager = this.app.getInputManager();
    this.viewPort     = this.app.getViewPort();
    this.physics      = this.stateManager.getState(BulletAppState.class);
  }
}

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

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

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