- jMonkeyEngine 3.0 Cookbook
- Rickard Edén
- 705字
- 2021-09-03 10:00:48
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:
- Start off by creating a class called
InputAppState
, extendingAbstractAppState
, and implementingActionListener
andAnalogListener
. - The
InputAppState
class needs a couple of fields to be functional. First of all, we're going to keep a reference to the application'sInputManager
in a field calledinputManager
. We're also adding aGameCharacterControl
field calledcharacter
. This can be replaced by anyspatial
. 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. - 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; }
- We create a method called
addInputMappings
to add these toinputManager
and make sure it listens to them. To do this, we supply the name of theenum
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.
- 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()); }
- Now, once
AppState
is attached, it runs theinitialize
method (in a thread-safe way). Here, we get the reference to the application'sInputManager
object and run theaddMappings
method we just created, as follows:public void initialize(AppStateManager stateManager, Application app) { super.initialize(stateManager, app); this.inputManager = app.getInputManager(); addInputMappings(); }
- Once
InputManager
detects any of the actions and sends them our way, we will just forward them to theGameCharacterControl
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); } }
- 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 ofinputManager
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.
- Learning LibGDX Game Development(Second Edition)
- Angular UI Development with PrimeNG
- 數據結構習題精解(C語言實現+微課視頻)
- Java程序員面試算法寶典
- 基于差分進化的優化方法及應用
- 零基礎學Java(第4版)
- 匯編語言程序設計(第3版)
- Lighttpd源碼分析
- Android嵌入式系統程序開發:基于Cortex-A8(第2版)
- Django 3.0應用開發詳解
- Apache Camel Developer's Cookbook
- 創意UI:Photoshop玩轉APP設計
- C編程技巧:117個問題解決方案示例
- Web前端測試與集成:Jasmine/Selenium/Protractor/Jenkins的最佳實踐
- Go Systems Programming