| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]--><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="Asciidoctor 1.5.4"><meta name="keywords" content="keyinput, input, documentation"><title>Combo Moves</title><link rel="stylesheet" href="./asciidoctor.css">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css">
- <link rel="stylesheet" href="./coderay-asciidoctor.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css"><link rel="stylesheet" href="/home/travis/build/jMonkeyEngine/wiki/build/asciidoc/html5/jme3/advanced/twemoji-awesome.css"></head><body class="article toc2 toc-left"><div id="header"><div id="toolbar"><a href="https://github.com/jMonkeyEngine/wiki/edit/master/src/docs/asciidoc/jme3/advanced/combo_moves.adoc"><i class="fa fa-pencil-square" aria-hidden="true"></i></a><a href="https://github.com/jMonkeyEngine/wiki/new/master/src/docs/asciidoc/jme3/advanced/"><i class="fa fa-plus-square" aria-hidden="true"></i></a><input dir="auto" style="position: relative; vertical-align: top;" spellcheck="false" autocomplete="off" class="searchbox__input aa-input" id="doc-search" name="search" placeholder="Search in the doc" required="required" type="search"></div><h1>Combo Moves</h1><div class="details"><span class="author" id="author"></span><br><span id="revnumber">version ,</span> <span id="revdate">2016/03/17 20:48</span></div><div id="toc" class="toc2"><div id="toctitle">Table of Contents</div><ul class="sectlevel1"><li><a href="#example-code">Example Code</a></li><li><a href="#create-input-triggers">Create Input Triggers</a></li><li><a href="#define-combos">Define Combos</a><ul class="sectlevel2"><li><a href="#combomove-class-methods">ComboMove Class Methods</a></li></ul></li><li><a href="#detect-combos-in-actionlistener">Detect Combos in ActionListener</a></li><li><a href="#execute-combos-in-the-update-loop">Execute Combos in the Update Loop</a></li><li><a href="#why-combos">Why Combos?</a></li></ul></div></div><div id="content"><div id="preamble"><div class="sectionbody"><div class="paragraph"><p>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. <a href="http://en.wikipedia.org/wiki/Combo_%28video_gaming%29">More background info</a></p></div>
- <div class="paragraph"><p>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.</p></div>
- <div class="paragraph"><p>Usage:</p></div>
- <div class="olist arabic"><ol class="arabic"><li><p>Create input triggers</p></li><li><p>Define combos</p></li><li><p>Detect combos in ActionListener</p></li><li><p>Execute combos in update loop</p></li></ol></div>
- <div class="paragraph"><p>Copy the two classes ComboMoveExecution.java and ComboMove.java into your application and adjust them to your package paths.</p></div></div></div>
- <div class="sect1"><h2 id="example-code">Example Code</h2><div class="sectionbody"><div class="ulist"><ul><li><p><a href="http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/input/combomoves/TestComboMoves.java">TestComboMoves.java</a></p></li><li><p><a href="http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/input/combomoves/ComboMoveExecution.java">ComboMoveExecution.java</a> ← required</p></li><li><p><a href="http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/input/combomoves/ComboMove.java">ComboMove.java</a> ← required</p></li></ul></div></div></div>
- <div class="sect1"><h2 id="create-input-triggers">Create Input Triggers</h2><div class="sectionbody"><div class="paragraph"><p>First you <a href="../../jme3/advanced/input_handling.html">define your game’s inputs</a> 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.</p></div>
- <div class="paragraph"><p>For example:</p></div>
- <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Left</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_LEFT));
- inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Right</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_RIGHT));
- inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Up</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_UP));
- inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Down</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_DOWN));
- inputManager.addMapping(<span class="string"><span class="delimiter">"</span><span class="content">Attack1</span><span class="delimiter">"</span></span>, <span class="keyword">new</span> KeyTrigger(KeyInput.KEY_SPACE));
- ...
- inputManager.addListener(<span class="local-variable">this</span>, <span class="string"><span class="delimiter">"</span><span class="content">Left</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">Right</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">Up</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">Down</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">Attack1</span><span class="delimiter">"</span></span>);</code></pre></div></div></div></div>
- <div class="sect2"><h3 id="define-combos">Define Combos</h3><div class="paragraph"><p>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.</p></div>
- <div class="paragraph"><p>The following example shows how a fireball combo move is triggered by pressing the navigation keys for “down, down+right, right, in this order.</p></div>
- <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">ComboMove fireball = <span class="keyword">new</span> ComboMove(<span class="string"><span class="delimiter">"</span><span class="content">Fireball</span><span class="delimiter">"</span></span>);
- fireball.press(<span class="string"><span class="delimiter">"</span><span class="content">Down</span><span class="delimiter">"</span></span>).notPress(<span class="string"><span class="delimiter">"</span><span class="content">Right</span><span class="delimiter">"</span></span>).done();
- fireball.press(<span class="string"><span class="delimiter">"</span><span class="content">Right</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">Down</span><span class="delimiter">"</span></span>).done();
- fireball.press(<span class="string"><span class="delimiter">"</span><span class="content">Right</span><span class="delimiter">"</span></span>).notPress(<span class="string"><span class="delimiter">"</span><span class="content">Down</span><span class="delimiter">"</span></span>).done();
- fireball.notPress(<span class="string"><span class="delimiter">"</span><span class="content">Right</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">Down</span><span class="delimiter">"</span></span>).done();
- fireball.setUseFinalState(<span class="predefined-constant">false</span>);</code></pre></div></div>
- <div class="paragraph"><p>Also create a ComboMoveExecution object for each ComboMove. You need it later to execute the detected combo.</p></div>
- <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java">ComboMoveExecution fireballExec = <span class="keyword">new</span> ComboMoveExecution(fireball);</code></pre></div></div>
- <div class="sect2"><h3 id="combomove-class-methods">ComboMove Class Methods</h3><div class="paragraph"><p>Use the following ComboMove methods to specify the combo:</p></div>
- <table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 50%;"><col style="width: 50%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">ComboMove Method</th><th class="tableblock halign-left valign-top">Description</th></tr></thead><tbody><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>press(“A).done(); +press(“A,“B).done();</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Combo step is recorded if A is entered. +Combo step is recorded if A and B are entered simultaneously.</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>notPress(“A).done(); +notPress(“A,“B).done();</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Combo step is recorded if A is released. +Combo step is recorded if A and B are both released.</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>press(“A).notPress(“B).done();</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Combo step is recorded if A is entered, and not B</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>press(“A).notPress(“B).timeElapsed(0.11f).done();</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Combo step is recorded a certain time after A and not B is entered. +etc, etc …</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>setPriority(0.5f);</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>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.</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>setUseFinalState(false); +setUseFinalState(true);</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>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. (?)</p></div></div></td></tr></tbody></table>
- <div class="paragraph"><p>The <code>press()</code> and <code>notPress()</code> methods accept sets of Input Triggers, e.g. <code>fireball.press(“A,“B,“C).done()</code>.</p></div>
- <div class="paragraph"><p>The following getters give you more information about the game state:</p></div>
- <table class="tableblock frame-all grid-all spread"><colgroup><col style="width: 50%;"><col style="width: 50%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">ComboMove Method</th><th class="tableblock halign-left valign-top">Usage</th></tr></thead><tbody><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getCastTime()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Returns the time since the last step has been recorded. (?)</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getMoveName()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Returns the string of the current combo</p></div></div></td></tr><tr><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>getPriority()</p></div></div></td><td class="tableblock halign-left valign-top"><div><div class="paragraph"><p>Returns the priority of this move</p></div></div></td></tr></tbody></table></div></div>
- <div class="sect1"><h2 id="detect-combos-in-actionlistener">Detect Combos in ActionListener</h2><div class="sectionbody"><div class="paragraph"><p>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.</p></div>
- <div class="paragraph"><p>Create a HashSet <code>pressMappings</code> to track curently pressed mappings, and a ComboMove object <code>currentMove</code> to track the current move.</p></div>
- <div class="paragraph"><p>We also track the cast time of a combo to determine if it has timed out (see update loop below).</p></div>
- <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="directive">private</span> <span class="predefined-type">HashSet</span><<span class="predefined-type">String</span>> pressedMappings = <span class="keyword">new</span> <span class="predefined-type">HashSet</span><<span class="predefined-type">String</span>>();
- <span class="directive">private</span> ComboMove currentMove = <span class="predefined-constant">null</span>;
- <span class="directive">private</span> <span class="type">float</span> currentMoveCastTime = <span class="integer">0</span>;
- <span class="directive">private</span> <span class="type">float</span> time = <span class="integer">0</span>;
- ...
- public <span class="type">void</span> onAction(<span class="predefined-type">String</span> name, <span class="type">boolean</span> isPressed, <span class="type">float</span> tpf) {
- <span class="comment">// Record pressed mappings</span>
- <span class="keyword">if</span> (isPressed){
- pressedMappings.add(name);
- }<span class="keyword">else</span>{
- pressedMappings.remove(name);
- }
- <span class="comment">// The pressed mappings have changed: Update ComboExecution objects</span>
- <span class="predefined-type">List</span><ComboMove> invokedMoves = <span class="keyword">new</span> <span class="predefined-type">ArrayList</span><ComboMove>();
- <span class="keyword">if</span> (fireballExec.updateState(pressedMappings, time)){
- invokedMoves.add(fireball);
- }
- <span class="comment">// ... add more ComboExecs here...</span>
- <span class="comment">// If any ComboMoves have been sucessfully triggered:</span>
- <span class="keyword">if</span> (invokedMoves.size() > <span class="integer">0</span>){
- <span class="comment">// identify the move with highest priority</span>
- <span class="type">float</span> priority = <span class="integer">0</span>;
- ComboMove toExec = <span class="predefined-constant">null</span>;
- <span class="keyword">for</span> (ComboMove move : invokedMoves){
- <span class="keyword">if</span> (move.getPriority() > priority){
- priority = move.getPriority();
- toExec = move;
- }
- }
- <span class="keyword">if</span> (currentMove != <span class="predefined-constant">null</span> && currentMove.getPriority() > toExec.getPriority()){
- <span class="keyword">return</span>; <span class="comment">// skip lower-priority moves</span>
- }
- <span class="comment">// If a ComboMove has been identified, store it in currentMove</span>
- currentMove = toExec;
- currentMoveCastTime = currentMove.getCastTime();
- }
- }</code></pre></div></div></div></div>
- <div class="sect1"><h2 id="execute-combos-in-the-update-loop">Execute Combos in the Update Loop</h2><div class="sectionbody"><div class="paragraph"><p>Now that you have detected the current move, you want to execute it. You do that in the update loop.</p></div>
- <div class="listingblock"><div class="content"><pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@Override</span>
- <span class="directive">public</span> <span class="type">void</span> simpleUpdate(<span class="type">float</span> tpf){
- time += tpf;
- fireballExec.updateExpiration(time);
- <span class="comment">// ... update more ComboExecs here....</span>
- <span class="keyword">if</span> (currentMove != <span class="predefined-constant">null</span>){
- currentMoveCastTime -= tpf;
- <span class="keyword">if</span> (currentMoveCastTime <= <span class="integer">0</span>){
- <span class="predefined-type">System</span>.out.println(<span class="string"><span class="delimiter">"</span><span class="content">THIS COMBO WAS TRIGGERED: </span><span class="delimiter">"</span></span> + currentMove.getMoveName());
- <span class="comment">// TODO: for each combo, implement special actions here</span>
- currentMoveCastTime = <span class="integer">0</span>;
- currentMove = <span class="predefined-constant">null</span>;
- }
- }
- }</code></pre></div></div>
- <div class="paragraph"><p>Test <code>currentMove.getMoveName()</code> and proceed to call methods that implement any special actions and bonuses. This is up to you and depends individually on your game.</p></div></div></div>
- <div class="sect1"><h2 id="why-combos">Why Combos?</h2><div class="sectionbody"><div class="paragraph"><p>Depending on the game genre, the designer can reward the players' intrinsical or extrinsical skills:</p></div>
- <div class="ulist"><ul><li><p>(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.</p></li><li><p>(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.</p></li></ul></div></div></div></div><div id="footer"><div id="footer-text">Version <br>Last updated 2019-12-20 23:30:51 +00:00</div></div><script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script><script>docsearch({
- apiKey: 'a736b6d93de805e26ec2f49b55013fbd',
- indexName: 'jmonkeyengine',
- inputSelector: '#doc-search',
- debug: false // Set debug to true if you want to inspect the dropdown
- });</script></body></html>
|