Skip to content

Commit 8fbe5c2

Browse files
committed
deploy: 5c917f2
1 parent ad92e41 commit 8fbe5c2

File tree

4 files changed

+901
-932
lines changed

4 files changed

+901
-932
lines changed

docs/3.4/tutorials/beginner/hello_animation.html

Lines changed: 117 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,7 @@ <h1 class="page">jMonkeyEngine 3 Tutorial (7) - Hello Animation</h1>
10021002
<div id="preamble">
10031003
<div class="sectionbody">
10041004
<div class="paragraph">
1005-
<p>This tutorial shows how to add an animation controller and channels, and how to respond to user input by triggering an animation in a loaded model.</p>
1005+
<p>This tutorial shows how to add an animation controller and how to respond to user input by triggering an animation in a loaded model.</p>
10061006
</div>
10071007
<div class="imageblock text-center">
10081008
<div class="content">
@@ -1032,10 +1032,12 @@ <h2 id="sample-code"><a class="anchor" href="#sample-code"></a>Sample Code</h2>
10321032
<div class="content">
10331033
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">package jme3test.helloworld;
10341034

1035-
import com.jme3.animation.AnimChannel;
1036-
import com.jme3.animation.AnimControl;
1037-
import com.jme3.animation.AnimEventListener;
1038-
import com.jme3.animation.LoopMode;
1035+
import com.jme3.anim.AnimComposer;
1036+
import com.jme3.anim.tween.Tween;
1037+
import com.jme3.anim.tween.Tweens;
1038+
import com.jme3.anim.tween.action.Action;
1039+
import com.jme3.anim.tween.action.BlendSpace;
1040+
import com.jme3.anim.tween.action.LinearBlendSpace;
10391041
import com.jme3.app.SimpleApplication;
10401042
import com.jme3.input.KeyInput;
10411043
import com.jme3.input.controls.ActionListener;
@@ -1046,60 +1048,71 @@ <h2 id="sample-code"><a class="anchor" href="#sample-code"></a>Sample Code</h2>
10461048
import com.jme3.scene.Node;
10471049

10481050
/** Sample 7 - how to load an OgreXML model and play an animation,
1049-
* using channels, a controller, and an AnimEventListener. */
1050-
public class HelloAnimation extends SimpleApplication
1051-
implements AnimEventListener {
1052-
private AnimChannel channel;
1053-
private AnimControl control;
1054-
Node player;
1055-
public static void main(String[] args) {
1056-
HelloAnimation app = new HelloAnimation();
1057-
app.start();
1058-
}
1051+
* using AnimComposer */
1052+
public class HelloAnimation extends SimpleApplication{
10591053

1060-
@Override
1061-
public void simpleInitApp() {
1062-
viewPort.setBackgroundColor(ColorRGBA.LightGray);
1063-
initKeys();
1064-
DirectionalLight dl = new DirectionalLight();
1065-
dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
1066-
rootNode.addLight(dl);
1067-
player = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
1068-
player.setLocalScale(0.5f);
1069-
rootNode.attachChild(player);
1070-
control = player.getControl(AnimControl.class);
1071-
control.addListener(this);
1072-
channel = control.createChannel();
1073-
channel.setAnim("stand");
1074-
}
1054+
private AnimComposer control;
1055+
private Action advance;
1056+
1057+
Node player;
1058+
public static void main(String[] args) {
1059+
HelloAnimation app = new HelloAnimation();
1060+
app.start();
1061+
}
10751062

1076-
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
1077-
if (animName.equals("Walk")) {
1078-
channel.setAnim("stand", 0.50f);
1079-
channel.setLoopMode(LoopMode.DontLoop);
1080-
channel.setSpeed(1f);
1063+
@Override
1064+
public void simpleInitApp() {
1065+
viewPort.setBackgroundColor(ColorRGBA.LightGray);
1066+
initKeys();
1067+
DirectionalLight dl = new DirectionalLight();
1068+
dl.setDirection(new Vector3f(-0.1f, -1f, -1).normalizeLocal());
1069+
rootNode.addLight(dl);
1070+
player = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
1071+
player.setLocalScale(0.5f);
1072+
rootNode.attachChild(player);
1073+
control = player.getControl(AnimComposer.class);
1074+
control.setCurrentAction("stand");
1075+
1076+
/* Compose an animation action named "halt"
1077+
that transitions from "Walk" to "stand" in half a second. */
1078+
BlendSpace quickBlend = new LinearBlendSpace(0f, 0.5f);
1079+
Action halt = control.actionBlended("halt", quickBlend, "stand", "Walk");
1080+
halt.setLength(0.5);
1081+
1082+
/* Compose an animation action named "advance"
1083+
that walks for one cycle, then halts, then invokes onAdvanceDone(). */
1084+
Action walk = control.action("Walk");
1085+
Tween doneTween = Tweens.callMethod(this, "onAdvanceDone");
1086+
advance = control.actionSequence("advance", walk, halt, doneTween);
10811087
}
1082-
}
10831088

1084-
public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
1085-
// unused
1086-
}
1089+
/**
1090+
* Callback to indicate that the "advance" animation action has completed.
1091+
*/
1092+
void onAdvanceDone() {
1093+
/*
1094+
* Play the "stand" animation action.
1095+
*/
1096+
control.setCurrentAction("stand");
1097+
}
10871098

1088-
/** Custom Keybinding: Map named actions to inputs. */
1089-
private void initKeys() {
1090-
inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
1091-
inputManager.addListener(actionListener, "Walk");
1092-
}
1093-
private ActionListener actionListener = new ActionListener() {
1094-
public void onAction(String name, boolean keyPressed, float tpf) {
1095-
if (name.equals("Walk") &amp;&amp; !keyPressed) {
1096-
if (!channel.getAnimationName().equals("Walk")) {
1097-
channel.setAnim("Walk", 0.50f);
1098-
channel.setLoopMode(LoopMode.Loop);
1099-
}
1100-
}
1099+
/**
1100+
* Map the spacebar to the "Walk" input action, and add a listener to initiate
1101+
* the "advance" animation action each time it's pressed.
1102+
*/
1103+
private void initKeys() {
1104+
inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
1105+
1106+
ActionListener handler = new ActionListener() {
1107+
@Override
1108+
public void onAction(String name, boolean keyPressed, float tpf) {
1109+
if (keyPressed &amp;&amp; control.getCurrentAction() != advance) {
1110+
control.setCurrentAction("advance");
1111+
}
1112+
}
1113+
};
1114+
inputManager.addListener(handler, "Walk");
11011115
}
1102-
};
11031116
}</code></pre>
11041117
</div>
11051118
</div>
@@ -1155,100 +1168,67 @@ <h2 id="animation-controller-and-channel"><a class="anchor" href="#animation-con
11551168
</div>
11561169
<div class="listingblock">
11571170
<div class="content">
1158-
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> private AnimChannel channel;
1159-
private AnimControl control;
1171+
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> private AnimComposer control;
11601172

11611173
public void simpleInitApp() {
11621174
...
11631175
/* Load the animation controls, listen to animation events,
11641176
* create an animation channel, and bring the model in its default position.
11651177
*/
1166-
control = player.getControl(AnimControl.class);
1167-
control.addListener(this);
1168-
channel = control.createChannel();
1169-
channel.setAnim("stand");
1178+
control = player.getControl(AnimComposer.class);
1179+
control.setCurrentAction("stand");
11701180
...
11711181
}</code></pre>
11721182
</div>
11731183
</div>
11741184
<div class="paragraph">
1175-
<p>This line of code will return NULL if the AnimControl is not in the main node of your model.</p>
1185+
<p>This line of code will return NULL if the AnimComposer is not in the main node of your model.</p>
11761186
</div>
11771187
<div class="listingblock">
11781188
<div class="content">
1179-
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">control = player.getControl(AnimControl.class);</code></pre>
1189+
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">control = player.getControl(AnimComposer.class);</code></pre>
11801190
</div>
11811191
</div>
11821192
<div class="paragraph">
11831193
<p>To check this, <b class="button">RMB</b> select your model and click &#8220;Edit in SceneComposer&#8221; if the models file extension is .j3o, or &#8220;View&#8221; if not. You can then see the tree for the model so you can locate the node the control resides in. You can access the subnode with the following code.</p>
11841194
</div>
11851195
<div class="listingblock">
11861196
<div class="content">
1187-
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">player.getChild("Subnode").getControl(AnimControl.class);</code></pre>
1188-
</div>
1189-
</div>
1190-
<div class="admonitionblock note">
1191-
<table>
1192-
<tr>
1193-
<td class="icon">
1194-
<i class="fa icon-note" title="Note"></i>
1195-
</td>
1196-
<td class="content">
1197-
<div class="paragraph">
1198-
<p>In response to a question about animations on different channels interfering with each other, <strong>Nehon</strong>, on the jME forum wrote,</p>
1199-
</div>
1200-
<div class="quoteblock">
1201-
<blockquote>
1202-
<div class="paragraph">
1203-
<p>You have to consider channels as part of the skeleton that are animated. The default behavior is to use the whole skeleton for a channel.</p>
1204-
</div>
1205-
<div class="paragraph">
1206-
<p>In your example the first channel plays the walk anim, then the second channel plays the dodge animation.</p>
1207-
</div>
1208-
<div class="paragraph">
1209-
<p>Arms and feet are probably not affected by the doge animation so you can see the walk anim for them, but the rest of the body plays the dodge animation.</p>
1210-
</div>
1211-
<div class="paragraph">
1212-
<p>Usually multiple channels are used to animate different part of the body. For example you create one channel for the lower part of the body and one for the upper part. This allow you to play a walk animation with the lower part and for example a shoot animation with the upper part. This way your character can walk while shooting.</p>
1213-
</div>
1214-
<div class="paragraph">
1215-
<p>In your case, where you want animations to chain for the whole skeleton, you just have to use one channel.</p>
1216-
</div>
1217-
</blockquote>
1218-
<div class="attribution">
1219-
&#8212; Nehon<br>
1220-
<cite>Team Leader: Retired</cite>
1221-
</div>
1197+
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">player.getChild("Subnode").getControl(AnimComposer.class);</code></pre>
12221198
</div>
1223-
</td>
1224-
</tr>
1225-
</table>
12261199
</div>
12271200
</div>
12281201
</div>
12291202
<div class="sect1">
12301203
<h2 id="responding-to-animation-events"><a class="anchor" href="#responding-to-animation-events"></a>Responding to Animation Events</h2>
12311204
<div class="sectionbody">
12321205
<div class="paragraph">
1233-
<p>Add <code>implements AnimEventListener</code> to the class declaration. This interface gives you access to events that notify you when a sequence is done, or when you change from one sequence to another, so you can respond to it. In this example, you reset the character to a standing position after a <code>Walk</code> cycle is done.</p>
1206+
<p>A Tween (part of an action sequence) can call a method on a class, allowing your application code to be informed of the animation state. In this example, you reset the character to a standing position after a <code>Walk</code> cycle is done.</p>
12341207
</div>
12351208
<div class="listingblock">
12361209
<div class="content">
12371210
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">public class HelloAnimation extends SimpleApplication
12381211
implements AnimEventListener {
12391212
...
12401213

1241-
public void onAnimCycleDone(AnimControl control,
1242-
AnimChannel channel, String animName) {
1243-
if (animName.equals("Walk")) {
1244-
channel.setAnim("stand", 0.50f);
1245-
channel.setLoopMode(LoopMode.DontLoop);
1246-
channel.setSpeed(1f);
1214+
@Override
1215+
public void simpleInitApp() {
1216+
...
1217+
Action walk = control.action("Walk");
1218+
Tween doneTween = Tweens.callMethod(this, "onAdvanceDone");
1219+
advance = control.actionSequence("advance", walk, halt, doneTween);
1220+
...
1221+
}
1222+
1223+
/**
1224+
* Callback to indicate that the "advance" animation action has completed.
1225+
*/
1226+
void onAdvanceDone() {
1227+
/*
1228+
* Play the "stand" animation action.
1229+
*/
1230+
control.setCurrentAction("stand");
12471231
}
1248-
}
1249-
public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
1250-
// unused
1251-
}
12521232
...
12531233
}</code></pre>
12541234
</div>
@@ -1290,45 +1270,38 @@ <h2 id="trigger-animations-after-user-input"><a class="anchor" href="#trigger-an
12901270
</div>
12911271
<div class="listingblock">
12921272
<div class="content">
1293-
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> private void initKeys() {
1294-
inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
1295-
inputManager.addListener(actionListener, "Walk");
1296-
}</code></pre>
1297-
</div>
1273+
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> /**
1274+
* Map the spacebar to the "Walk" input action, and add a listener to initiate
1275+
* the "advance" animation action each time it's pressed.
1276+
*/
1277+
private void initKeys() {
1278+
inputManager.addMapping("Walk", new KeyTrigger(KeyInput.KEY_SPACE));
1279+
1280+
ActionListener handler = new ActionListener() {
1281+
@Override
1282+
public void onAction(String name, boolean keyPressed, float tpf) {
1283+
if (keyPressed &amp;&amp; control.getCurrentAction() != advance) {
1284+
control.setCurrentAction("advance");
1285+
}
1286+
}
1287+
};
1288+
inputManager.addListener(handler, "Walk");
1289+
}</code></pre>
12981290
</div>
1299-
<div class="paragraph">
1300-
<p>To use the input controller, you need to implement the actionListener by testing for each action by name, then set the channel to the corresponding animation to run.</p>
13011291
</div>
13021292
<div class="ulist">
13031293
<ul>
13041294
<li>
1305-
<p>The second parameter of setAnim() is the blendTime (how long the current animation should overlap with the last one).</p>
1306-
</li>
1307-
<li>
1308-
<p>LoopMode can be Loop (repeat), Cycle (forward then backward), and DontLoop (only once).</p>
1295+
<p>By default, the animation will loop, there is an overloaded <code>setCurrentAction</code> method that allows you to set the loop mode.</p>
13091296
</li>
13101297
<li>
1311-
<p>If needed, use channel.setSpeed() to set the speed of this animation.</p>
1298+
<p>If needed, use Action::setSpeed to set the speed of this animation.</p>
13121299
</li>
13131300
<li>
1314-
<p>Optionally, use channel.setTime() to Fast-forward or rewind to a certain moment in time of this animation.</p>
1301+
<p>Optionally, use AnimComposer::.setTime to Fast-forward or rewind to a certain moment in time of this animation.</p>
13151302
</li>
13161303
</ul>
13171304
</div>
1318-
<div class="listingblock">
1319-
<div class="content">
1320-
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> private ActionListener actionListener = new ActionListener() {
1321-
public void onAction(String name, boolean keyPressed, float tpf) {
1322-
if (name.equals("Walk") &amp;&amp; !keyPressed) {
1323-
if (!channel.getAnimationName().equals("Walk")){
1324-
channel.setAnim("Walk", 0.50f);
1325-
channel.setLoopMode(LoopMode.Cycle);
1326-
}
1327-
}
1328-
}
1329-
};</code></pre>
1330-
</div>
1331-
</div>
13321305
</div>
13331306
</div>
13341307
<div class="sect1">
@@ -1342,7 +1315,7 @@ <h3 id="exercise-1-two-animations"><a class="anchor" href="#exercise-1-two-anima
13421315
<div class="olist arabic">
13431316
<ol class="arabic">
13441317
<li>
1345-
<p>Create a second channel in the controller.</p>
1318+
<p>Create a second layer in the controller.</p>
13461319
</li>
13471320
<li>
13481321
<p>Create a new key trigger mapping and action. (see: <a href="hello_input_system.html" class="xref page">Hello Input</a>)</p>
@@ -1361,7 +1334,7 @@ <h3 id="exercise-1-two-animations"><a class="anchor" href="#exercise-1-two-anima
13611334
</div>
13621335
<div class="listingblock">
13631336
<div class="content">
1364-
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">for (String anim : control.getAnimationNames()) {
1337+
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">for (String anim : control.getAnimClipsNames()) {
13651338
System.out.println(anim);
13661339
}</code></pre>
13671340
</div>
@@ -1400,22 +1373,18 @@ <h3 id="exercise-3-revealing-the-skeleton-2"><a class="anchor" href="#exercise-3
14001373
</div>
14011374
<div class="listingblock">
14021375
<div class="content">
1403-
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> import com.jme3.scene.debug.SkeletonDebugger;
1404-
import com.jme3.material.Material;</code></pre>
1376+
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> import com.jme3.scene.debug.custom.ArmatureDebugAppState;
1377+
import com.jme3.anim.SkinningControl;</code></pre>
14051378
</div>
14061379
</div>
14071380
<div class="paragraph">
14081381
<p>Add the following code snippet to <code>simpleInitApp()</code> to make the bones (that you just read about) visible!</p>
14091382
</div>
14101383
<div class="listingblock">
14111384
<div class="content">
1412-
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> SkeletonDebugger skeletonDebug =
1413-
new SkeletonDebugger("skeleton", control.getSkeleton());
1414-
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
1415-
mat.setColor("Color", ColorRGBA.Green);
1416-
mat.getAdditionalRenderState().setDepthTest(false);
1417-
skeletonDebug.setMaterial(mat);
1418-
player.attachChild(skeletonDebug);</code></pre>
1385+
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> ArmatureDebugAppState armatureDebugAppState = new ArmatureDebugAppState();
1386+
armatureDebugAppState.addArmatureFrom(player.getControl(SkinningControl.class));
1387+
this.getStateManager().attach(armatureDebugAppState);</code></pre>
14191388
</div>
14201389
</div>
14211390
<div class="paragraph">

0 commit comments

Comments
 (0)