Hi everyone,
some time ago I wrote this help class for adding particle and sound effects to old system animations.
import com.jme3.anim.tween.Tween;
import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.AudioTrack;
import com.jme3.animation.EffectTrack;
import com.jme3.audio.AudioNode;
import com.jme3.effect.ParticleEmitter;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
public class TrackUtils {
public static void addAudioTrack(Spatial sp, AudioNode audio, String animName, float startOffset) {
Animation anim = sp.getControl(AnimControl.class).getAnim(animName);
AudioTrack track = new AudioTrack(audio, anim.getLength(), startOffset);
anim.addTrack(track);
}
public static void addEffectTrack(Spatial sp, ParticleEmitter emitter, String animName, float startOffset) {
Animation anim = sp.getControl(AnimControl.class).getAnim(animName);
EffectTrack track = new EffectTrack(emitter, anim.getLength(), startOffset);
anim.addTrack(track);
}
}
Taking a cue from the AudioTrack and EffectTrack classes, I created a custom “CallbackTrack” class to be able to invoke a method via reflection at a precise moment in time in the animation. I needed this mechanism to be able to inflict damage on the player at a precise moment in the enemy attack animation.
Here is a sample snippet:
public class EnemyControl extends AbstractControl {
private float damage = 5f;
@Override
public void setSpatial(Spatial sp) {
super.setSpatial(sp);
if (spatial != null) {
Animation anim = spatial.getControl(AnimControl.class).getAnim("ZombieAttack");
Tween tween = Tweens.callMethod(this, "inflictDamage", damage);
float startOffset = 1f;
CallbackTrack track = new CallbackTrack(tween, anim.getLength(), startOffset);
anim.addTrack(track);
}
}
public void inflictDamage(float damage) {
// apply damage to the player...
}
}
The CallbackTrack class:
package com.capdevon.anim;
import com.jme3.anim.tween.Tween;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.Track;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
/**
*
* @author capdevon
*/
public class CallbackTrack implements Track, JmeCloneable {
private Tween tween;
private float startOffset = 0;
private float length = 0;
private boolean initialized = false;
private boolean started = false;
//Animation listener to reset the tween when the animation ends or is changed
private class OnEndListener implements AnimEventListener {
@Override
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
stop();
}
@Override
public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
}
}
/**
* constructor for serialization only
*/
protected CallbackTrack() {
}
/**
* Creates an ActionTrack
*
* @param tween the Tween
* @param length the length of the track (usually the length of the
* animation you want to add the track to)
*/
public CallbackTrack(Tween tween, float length) {
this.tween = tween;
this.length = length;
}
/**
* Creates an ActionTrack
*
* @param tween the Tween
* @param length the length of the track (usually the length of the
* animation you want to add the track to)
* @param startOffset the time in second when the tween will be played after
* the animation starts (default is 0)
*/
public CallbackTrack(Tween tween, float length, float startOffset) {
this(tween, length);
this.startOffset = startOffset;
}
/**
* Internal use only
*
* @see Track#setTime(float, float, com.jme3.animation.AnimControl,
* com.jme3.animation.AnimChannel, com.jme3.util.TempVars)
*/
@Override
public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) {
if (time >= length) {
return;
}
if (!initialized) {
control.addListener(new OnEndListener());
initialized = true;
}
if (!started && time >= startOffset) {
started = true;
tween.interpolate(1);
}
}
private void stop() {
started = false;
}
/**
* Return the length of the track
*
* @return length of the track
*/
@Override
public float getLength() {
return length;
}
@Override
public float[] getKeyFrameTimes() {
return new float[] { startOffset };
}
/**
* Clone this track
*
* @return a new track
*/
@Override
public Track clone() {
return new CallbackTrack(tween, length, startOffset);
}
@Override
public Object jmeClone() {
try {
return super.clone();
} catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning", e);
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
// Duplicating the old cloned state from cloneForSpatial()
this.initialized = false;
this.started = false;
this.tween = cloner.clone(tween);
}
@Override
public void write(JmeExporter ex) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void read(JmeImporter im) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
}
.
How can I translate the same features with the new animation system?