官术网_书友最值得收藏!

Attaching an input AppState object

In this recipe, we will make an AppState object, which will handle the player input for a character. It's a great way to add functionality to the application in a modular way. The AppState object we create here could easily be added during the game and removed or disabled during cut scenes or while in the game menu.

Getting ready

We won't require any special assets for this recipe, but it will be beneficial to have a basic understanding of how AppState works and its purpose in jMonkeyEngine. This particular implementation of the recipe will use the character-control class created in the previous example. It can still be used to manipulate a spatial object directly without the GameCharacterControl class. This recipe will provide pointers on where to do this.

How to do it...

To attach an input AppState object, perform the following steps:

  1. Start off by creating a class called InputAppState, extending AbstractAppState, and implementing ActionListener and AnalogListener.
  2. The InputAppState class needs a couple of fields to be functional. First of all, we're going to keep a reference to the application's InputManager in a field called inputManager. We're also adding a GameCharacterControl field called character. This can be replaced by any spatial. Lastly, we're going to have a value that controls the sensitivity of the analog controls. We do this with a float called sensitivity. Add getters and setters for character and sensitivity.
  3. Next, we'll set up the kinds of input we're going to handle. Strings are used by jMonkeyEngine for the mappings, but enums can be easier to manage across classes. Here, we'll use an enum and supply the name of the value as the mapping. We use it to create some basic FPS controls as follows:
    public enum InputMapping{
      RotateLeft, RotateRight, LookUp, LookDown, StrafeLeft,
      StrafeRight, MoveForward, MoveBackward;
    }
  4. We create a method called addInputMappings to add these to inputManager and make sure it listens to them. To do this, we supply the name of the enum value as the mapping and bind it to a certain input as follows:
    private void addInputMappings(){
      inputManager.addMapping(InputMapping.RotateLeft.name(), new MouseAxisTrigger(MouseInput.AXIS_X, true));
      inputManager.addMapping(InputMapping.RotateRight.name(), new MouseAxisTrigger(MouseInput.AXIS_X, false));
      inputManager.addMapping(InputMapping.LookUp.name(), new MouseAxisTrigger(MouseInput.AXIS_Y, false));
      inputManager.addMapping(InputMapping.LookDown.name(), new MouseAxisTrigger(MouseInput.AXIS_Y, true));
      inputManager.addMapping(InputMapping.StrafeLeft.name(), new KeyTrigger(KeyInput.KEY_A), new KeyTrigger(KeyInput.KEY_LEFT));
      inputManager.addMapping(InputMapping.StrafeRight.name(), new KeyTrigger(KeyInput.KEY_D), new KeyTrigger(KeyInput.KEY_RIGHT));
      inputManager.addMapping(InputMapping.MoveForward.name(), new KeyTrigger(KeyInput.KEY_W), new KeyTrigger(KeyInput.KEY_UP));
      inputManager.addMapping(InputMapping.MoveBackward.name(), new KeyTrigger(KeyInput.KEY_S), new KeyTrigger(KeyInput.KEY_DOWN));
    
    }

    Note

    It's okay to assign several keys to the same mapping. For example, this recipe assigns both the arrow keys and the classical WASD pattern to the movement keys.

  5. Finally, in the same method, we tell InputManager to listen to the commands, or it won't actually fire on any of the inputs:
    for (InputMapping i : InputMapping.values()) {
      inputManager.addListener(this, i.name());
    }
  6. Now, once AppState is attached, it runs the initialize method (in a thread-safe way). Here, we get the reference to the application's InputManager object and run the addMappings method we just created, as follows:
    public void initialize(AppStateManager stateManager, Application app) {
      super.initialize(stateManager, app);
      this.inputManager = app.getInputManager();
      addInputMappings();
    }
  7. Once InputManager detects any of the actions and sends them our way, we will just forward them to the GameCharacterControl object by applying the sensitivity value to the analog input as follows:
    public void onAnalog(String name, float value, float tpf) {
      if(character != null){
        character.onAnalog(name, value * sensitivity, tpf);
      }
    }
    
    public void onAction(String name, boolean isPressed, float tpf) {
      if(character != null){
        character.onAction(name, isPressed, tpf);
      }
    }
  8. We're actually almost done with this recipe. We just need to make sure that we reset everything when AppState is not to be used anymore. We do this by overriding the cleanup method. Here, we remove all the mappings and remove this instance from listeners of inputManager as follows:
    public void cleanup() {
      super.cleanup();
      for (InputMapping i : InputMapping.values()) {
        if (inputManager.hasMapping(i.name())) {
          inputManager.deleteMapping(i.name());
        }
      }
      inputManager.removeListener(this);
    }

How it works...

The AppState object works with the application in a way that is similar to how Control works with spatial. They give extended functionalities in a modular way. Once it has been attached to stateManager, its update method will be called every cycle. This gives us access to the application's thread as well. It also has the stateAttached and stateDetached methods, which can be used to turn functionality on and off easily.

主站蜘蛛池模板: 隆子县| 济阳县| 泸定县| 斗六市| 延长县| 会理县| 乐业县| 庆城县| 新巴尔虎左旗| 许昌县| 齐齐哈尔市| 贵德县| 沛县| 灌阳县| 类乌齐县| 花莲市| 合江县| 慈利县| 进贤县| 伊金霍洛旗| 尤溪县| 阿拉善右旗| 杭锦旗| 太仓市| 龙陵县| 资中县| 阿克| 龙泉市| 改则县| 平顶山市| 阿拉善左旗| 咸丰县| 梁平县| 万盛区| 屏山县| 天等县| 随州市| 宁武县| 松江区| 那曲县| 万全县|