combo_moves.adoc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. = Combo Moves
  2. :author:
  3. :revnumber:
  4. :revdate: 2016/03/17 20:48
  5. :keywords: keyinput, input, documentation
  6. :relfileprefix: ../../
  7. :imagesdir: ../..
  8. ifdef::env-github,env-browser[:outfilesuffix: .adoc]
  9. The ComboMoves class allows you to define combinations of inputs that trigger special actions. Entering an input combo correctly can bring the player incremental rewards, such as an increased chance to hit, an increased effectiveness, or decreased change of being blocked, whatever the game designer chooses. link:http://en.wikipedia.org/wiki/Combo_%28video_gaming%29[More background info]
  10. Combos are usually a series of inputs, in a fixed order: For example a keyboard combo can look like: “press Down, then Down+Right together, then Right.
  11. Usage:
  12. . Create input triggers
  13. . Define combos
  14. . Detect combos in ActionListener
  15. . Execute combos in update loop
  16. Copy the two classes ComboMoveExecution.java and ComboMove.java into your application and adjust them to your package paths.
  17. == Example Code
  18. * link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/input/combomoves/TestComboMoves.java[TestComboMoves.java]
  19. * link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/input/combomoves/ComboMoveExecution.java[ComboMoveExecution.java] ← required
  20. * link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/input/combomoves/ComboMove.java[ComboMove.java] ← required
  21. == Create Input Triggers
  22. First you <<jme3/advanced/input_handling#,define your game's inputs>> as you usually do: Implement the com.jme3.input.controls.ActionListener interface for your class, and add triggers mappings such as com.jme3.input.controls.KeyTrigger and com.jme3.input.KeyInput.
  23. For example:
  24. [source,java]
  25. ----
  26. inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_LEFT));
  27. inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_RIGHT));
  28. inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_UP));
  29. inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_DOWN));
  30. inputManager.addMapping("Attack1", new KeyTrigger(KeyInput.KEY_SPACE));
  31. ...
  32. inputManager.addListener(this, "Left", "Right", "Up", "Down", "Attack1");
  33. ----
  34. == Define Combos
  35. For each of your combo moves, you specify the series of inputs that will trigger it. The order in which you define them is the order the player has to press them for the step to be recorded. When all steps have been recorded, the combo is triggered.
  36. The following example shows how a fireball combo move is triggered by pressing the navigation keys for “down, down+right, right, in this order.
  37. [source,java]
  38. ----
  39. ComboMove fireball = new ComboMove("Fireball");
  40. fireball.press("Down").notPress("Right").done();
  41. fireball.press("Right", "Down").done();
  42. fireball.press("Right").notPress("Down").done();
  43. fireball.notPress("Right", "Down").done();
  44. fireball.setUseFinalState(false);
  45. ----
  46. Also create a ComboMoveExecution object for each ComboMove. You need it later to execute the detected combo.
  47. [source,java]
  48. ----
  49. ComboMoveExecution fireballExec = new ComboMoveExecution(fireball);
  50. ----
  51. === ComboMove Class Methods
  52. Use the following ComboMove methods to specify the combo:
  53. [cols="2", options="header"]
  54. |===
  55. a|ComboMove Method
  56. a|Description
  57. a|press(“A).done(); +press(“A,“B).done();
  58. a|Combo step is recorded if A is entered. +Combo step is recorded if A and B are entered simultaneously.
  59. a|notPress(“A).done(); +notPress(“A,“B).done();
  60. a|Combo step is recorded if A is released. +Combo step is recorded if A and B are both released.
  61. a|press(“A).notPress(“B).done();
  62. a|Combo step is recorded if A is entered, and not B
  63. a|press(“A).notPress(“B).timeElapsed(0.11f).done();
  64. a|Combo step is recorded a certain time after A and not B is entered. +etc, etc …
  65. a|setPriority(0.5f);
  66. a|If there is an ambiguity, a high-priority combo will trigger instead of a low-priority combo. This prevents that a similar looking combo step “hijacks another Combo. Use only once per ComboMove.
  67. a|setUseFinalState(false); +setUseFinalState(true);
  68. a|This is the final command of the series. +False: Do not wait on a final state, chain combo steps. (?) +True: This is the final state, do not chain combo steps. (?)
  69. |===
  70. The `press()` and `notPress()` methods accept sets of Input Triggers, e.g. `fireball.press(“A,“B,“C).done()`.
  71. The following getters give you more information about the game state:
  72. [cols="2", options="header"]
  73. |===
  74. a|ComboMove Method
  75. a|Usage
  76. a|getCastTime()
  77. a|Returns the time since the last step has been recorded. (?)
  78. a|getMoveName()
  79. a|Returns the string of the current combo
  80. a|getPriority()
  81. a|Returns the priority of this move
  82. |===
  83. == Detect Combos in ActionListener
  84. Now that you have specified the combo steps, you want to detect them. You do that in the onAction() method that you get from the ActionListener interface.
  85. Create a HashSet `pressMappings` to track curently pressed mappings, and a ComboMove object `currentMove` to track the current move.
  86. We also track the cast time of a combo to determine if it has timed out (see update loop below).
  87. [source,java]
  88. ----
  89. private HashSet<String> pressedMappings = new HashSet<String>();
  90. private ComboMove currentMove = null;
  91. private float currentMoveCastTime = 0;
  92. private float time = 0;
  93. ...
  94. public void onAction(String name, boolean isPressed, float tpf) {
  95. // Record pressed mappings
  96. if (isPressed){
  97. pressedMappings.add(name);
  98. }else{
  99. pressedMappings.remove(name);
  100. }
  101. // The pressed mappings have changed: Update ComboExecution objects
  102. List<ComboMove> invokedMoves = new ArrayList<ComboMove>();
  103. if (fireballExec.updateState(pressedMappings, time)){
  104. invokedMoves.add(fireball);
  105. }
  106. // ... add more ComboExecs here...
  107. // If any ComboMoves have been sucessfully triggered:
  108. if (invokedMoves.size() > 0){
  109. // identify the move with highest priority
  110. float priority = 0;
  111. ComboMove toExec = null;
  112. for (ComboMove move : invokedMoves){
  113. if (move.getPriority() > priority){
  114. priority = move.getPriority();
  115. toExec = move;
  116. }
  117. }
  118. if (currentMove != null && currentMove.getPriority() > toExec.getPriority()){
  119. return; // skip lower-priority moves
  120. }
  121. // If a ComboMove has been identified, store it in currentMove
  122. currentMove = toExec;
  123. currentMoveCastTime = currentMove.getCastTime();
  124. }
  125. }
  126. ----
  127. == Execute Combos in the Update Loop
  128. Now that you have detected the current move, you want to execute it. You do that in the update loop.
  129. [source,java]
  130. ----
  131. @Override
  132. public void simpleUpdate(float tpf){
  133. time += tpf;
  134. fireballExec.updateExpiration(time);
  135. // ... update more ComboExecs here....
  136. if (currentMove != null){
  137. currentMoveCastTime -= tpf;
  138. if (currentMoveCastTime <= 0){
  139. System.out.println("THIS COMBO WAS TRIGGERED: " + currentMove.getMoveName());
  140. // TODO: for each combo, implement special actions here
  141. currentMoveCastTime = 0;
  142. currentMove = null;
  143. }
  144. }
  145. }
  146. ----
  147. Test `currentMove.getMoveName()` and proceed to call methods that implement any special actions and bonuses. This is up to you and depends individually on your game.
  148. == Why Combos?
  149. Depending on the game genre, the designer can reward the players' intrinsical or extrinsical skills:
  150. * (intrinsical:) RPGs typically calculate the success of an attack from the character's in-game training level: The player plays the role of a character whose skill level is defined in numbers. RPGs typically do not offer any Combos.
  151. * (extrinsical:) Sport and fighter games typically choose to reward the player's “manual skills: The success of a special move solely depends on the player's own dexterity. These games typically offer optional Combos.