Need help with designing a Tween based animation system

Hi

I’m in the beginning of designing a client side view level system to play combat effects.

So basically when an entity perform a combat action like melee attack/ aoe attack /projectile attack /heal spell,…) at server side we are mainly playing with Stats, increasing some stats, decreasing other stats and so on and so forth. I have already come up with base implementation for this part.

But what happens in client side, actually we listen to those action entities and when ever an action for example MeleeAttackAction( Name:"BigSword", State:"Start" ) starts we play animation, sound, particle effect, …

So I want to design a flexible system at client side to do those.

Now there are bunch of ways come to my mind :

  • For each attack type (ex : “BigSword” Attack) I can create a groovy script which needs to play the animation/sound/particle effects for that attack.

  • Using Tweens, Lemur includes an abstract Tween based mechanism for playing tween effects.

I am interested for the later approach, but I am uncertain how best I should put things together.

Should I create one Tween for playing sound, one for playing particle, one for animation, and then mix them together to create final effect !?

Should I use EffectControl for playing those effects !?

How should I save the mixed tweens, should I serialize them to JSON !?

I want to hear from pros about their ideas, and what approach they would chose.

Feel free to share what you think about this.

Regards

I guess you’re asking for the best approach as opposed to how?

My experience tells me to always start simple and build on it to avoid unknowns. You always end up backtracking if something goes wrong otherwise. A simple appstate with tpf timings sounds like it would work fine. Why do you want to save them externally, though? That part is unclear to me. Why do you need to tween anything? Just for timings sake?

Yes, I want to first find out the good approach toward this, then will see how to implement it.

Yes, I want to have one simple app state watches for attack entities and whenever an attach starts it should play one or more attack animation sequence, one or more sound, one or more particles emitters but how should this app state know which animations/ sounds/particles it should play for each attack, notice these information are not something I can provide with ES, they are completely visual stuff and has nothing to do with ES so I can not save them in components on database, so I need to save the at client side some how.

Hmm… this is what also confusing me too! Many effects might be an instant effect which does not need to know about timing but they should keep order in which they should play, for example a particle should start after animation ends. I mean it is important to know if one effect should play in sequence or in parallel with other effects which can be done easily by mixing Tweens in Lemur.

Yeah. Actually tweens seem like a good idea now I understand. They can contain the information you would require and can be chained/mixed, and you pass the final tween to the appatate. I guess all you would need to do is provide a set of tweens to choose from.

1 Like

The “legacy” JME animation system can handle sound events and stuff. I don’t know about the upcoming system but I assume so.

Given that it’s all visual, the more you can bake into the models, the better I guess.

1 Like

If tweens suit your needs go for it.

^ I don’t follow why visual stuff has nothing to do with ES. Well, your game is client-server. Thus, I probably assume your ES in on server side. However, client side ES can have visual stuff the server ES doesn’t need to know about.

Thus even if you use tweens, you’ll probably store them somewhere, you can always store them on client-side ES.

But for this I need to back it with an embedded client side HSQL DB and I am not sure if this is a good idea specially if I want to target Android also.

I was considering to save Tweens to JSON using GSON library (which already comes with jme-plugins ) using this RuntimeTypeAdapterFactory I hope it would be as easy as calling
Tween tweens;
String jsonString = gson.toJson(tweens, Tween.class)
and read it with
Tween tween = gson.fromJson(jsonString, Tween.class)

Anyway I might give it a second thought.

@pspeed wanted to continue this here ,

Yeah, the thing is that for ‘instant’ things like this you can just use the call method action almost as easily as a separate tween. So I’m still trying to figure out what all value those sorts of things should add over callMethod() and callTweenMethod().

Can you elaborate this please.
Are you saying I should have an AnimationState, a ParticleEmitterState and a SoundState app state and I create one CallMethod tween for animation which internally calls AnimationState.playAnimation(args ...) then create another CallMethod tween for particle which internally calls ParticleEmitterState.emitParticle(args ...) and another one for playing sound ?

Let’s talk about sound for a second. Where does the Sound come from? Where is it located? What are its settings?

Presumably you already have some AudioNode which you’ve setup as you like.

So then if you want to play that audio in an animation you could call:
Tweens.callMethod(audioNode, “play”);
Or:
Tweens.callMethod(audioNode, “playInstance”);

1 Like

Now a question rise !:roll_eyes:

How should I persist this tween then? which also probably is chained with a few other tweens.

Because when for example a SwordAttack rises I have no idea of which audioNodes or animations and in what order I should play. I just know that a SwordAttack should come with a SwordAttackTween ( Created in a tween editor gui) which has everything saved in itself and knows what audio nodes and animations clips in which order it should play. Am I wrong ?

MyAnimation.groovy

makeTheAnimation() {
    AudioNode node = new ......
    def tween = Tweens.callMethod(audioNode, "play");
}

There, I persisted it… in a groovy script.

I mean, “persist” is a HUUUUUUGE word.

since I have no idea what you are actually trying to do, I can’t comment.

How are you telling these “persisted” tweens what they are operating on?

Edit: put another way, I think you are seeing the complexity in the wrong place. If you know how you are going to fix all of this up then you know how you will persist it… because it’s those things that are doing the fixing up that are persisted and those are also creating the tweens. It’s not the tweens you are persisting… it’s the things creating them for some set of nodes.

1 Like

Yes, this is one way of doing it. So for each and every action, I create a groovy script and hard code in it.

I want to design it in a way that I can create/chain these tweens from an editor gui and save them. Is this possible ? And any reason why I should not do this ?

Edit:

taking that sound example, I can have something like this

public abstract class AbstractTween implements Tween {
     public abstract void init(Spatial spatial, AppStateManager manager) ;
 }

public class SoundTween extends AbstractTween {

   private String audioNodeName;
   private  transient Spatial spatial;  // not going to to serialize this
   private  transient SoundState soundState; // not going to to serialize this

   public SoundTween (String audioNodeName){
   this.audioNodeName = audioNodeName;
   }
   public void init (Spatial spatial, AppStateManager manager) { 
   this.spatial = spatial;
   this.soundState = manager.getState(SoundState.class);
   
   }
 
   protected boolean interpolate( double t ) {
   soundState.playSound(audioNodeName, spatial);
   return true;
   }

}

and then it will be possible to save the tween with gson and editor friendly I believe.

Any thoughts ?

So based on my above post, I save the name/path of sound node/animation name/particle.
For example in case of sound, I save the path for audioNode.j3o and SoundState will need to load and play it at the location of specified spatial. (Note about spatial, it is the character spatial that runs this action and should be passed by a AttackViewState in case of an attack action)

@pspeed I am not saying what I am doing is the right way. I am trying to consider all available solutions and select the suitable one. Will appreciate your comments on this. :slightly_smiling_face:

Ehmm…, read it twice, and I think I got what you mean.
So I need to design a set of tween builder things which themselves are not tween but can be chained like tweens and can be saved/loaded by my tween editor. Then I need to call a method on it like buildTween() to build the actual tweens object from it at runtime, Yes?

He means separate the data from the tween. It’s a bit like saving a control. The control deals with the data you give it. So you don’t save the control, you save the data you give it.

For example if I had a control that grew a tree from sapling to fully grown. It wouldn’t make sense to save the control when I left the game. I would save the data it needed and then pass it to the control on load. It’s a clear separation of data from the representation of it.

It’s the same here. The tween is processing the data you give it. So save the data, not the tween.

Then what about their orders ? A tween is not just one tween, it is a set of tweens chained together. Should I save their order as data also ?

If their order can change based on some variable then I guess it will be calculated at run time. If it’s constant then I guess so yeah. It’s only a short or even a byte of data :stuck_out_tongue:

Yes, that’s what I was thinking. Even in your Tween example you had stuff that a Tween doesn’t need to know like audionode name and stuff. That should be fixed up outside of the tween by the builder.

The Tween should already have its Node by the time it runs and not have to look it up.

1 Like

I see.

Really appreciate your helps Paul. :slightly_smiling_face:

1 Like

I am done with bare bone of my Tween Editor.
It looks like

It generates a TweenBuilder object and I serialize it to JSON.

Currently for each action I am creating a prefab entity and save the JSON data on a JsonString component on the entity. It’s easy to dynamically save it on database at server side and retrieve it with action name and generate tween in client side.

@pspeed do you think it is ok to save it on entity or I should save it as a file inside asset folder in client ?