Crafty icon indicating copy to clipboard operation
Crafty copied to clipboard

added TweenChain

Open mucaho opened this issue 11 years ago • 6 comments

TweenChain component:

  • sequentially arranges tweens (one tween plays after another)
  • optionally loops
  • can be called with relative offsets (think {x: -2*TILE_WIDTH} instead of {x: ent.x - 2*TILE_WIDTH}
  • does not interfere with already present Tweens or future Tweens
  • downside?: only one TweenChain at a time; possible to implement for more if needed

Example JSFiddle showing 1 TweenChain and 2 other Tweens animating at same time

This feature is listed in the Idea pool - Path Movement and should replace #515

Tests coming after I get the O.K. on the code

mucaho avatar Feb 06 '14 10:02 mucaho

Hmm, why a separate component, and not an additional capability of "Tween"?

It might be worth thinking about whether there's an easy way to abstract the notion of chainable actions out such that you could tie together Animations, Tweens, and any custom-made user events. On the other hand, maybe it's better to just have something that works right now, and abstract that bridge later.

starwed avatar Feb 06 '14 22:02 starwed

The way it is implemented right now:

  • decouples the "aggregated tweens" logic from the implementation of the Tween component itself
  • it's clear which tweens belong to which tween chain, see example1
  • lacks the ability to define sequential tweens seperately, see example2

So you would want something like this

var ent = Crafty.e("Tween");

// One execution context
ent.tween({x: 100}, 100ms, isSequential = false)
   .tween([x: 40, alpha: 0}, 100ms, isSequential = true) // ends after 200ms total
   .tween(loopLast = 2) // loop the last two specified tweens

// Another execution context
ent.tween({y: 100}, 100ms, isSequential = true/false?) // pass true or false here? do I know if I'm specifying the first tween in a chain?
// In another scope in the same execution context, ~2ms later
ent.tween([y: 40}, 100ms, isSequential = true) // this will start once the last specified tween finishes

rather than something like this

var ent = Crafty.e("TweenChain");

// One execution context
ent.tweenChain(relativeValues = false, looping = true, [ 
      [{x: 100}, 100], 
      [{y: 40, alpha: 0}, 100],
    ])

// Another execution context
// can't do the same as above  - tweens in a chain can not be declared separately

right?

mucaho avatar Feb 09 '14 06:02 mucaho

So I have been investigating how the user could reference mulitple chains (e.g. for cancelling them later):

  • Take the Tween approach: map every property (x,y,...) to the corresponding chain it was defined in. The user can then call cancelTweenChain('x') and it will cancel the chain where x is tweaked at least once. I do not like this approach, as any other chains that also tweek the same property will cause conflicts.
  • Return an automatically generated unique identifier when the user defines a tween chain. The user can then save the identifier and cancel the tween chain using this identifier. This approach is not coherent with the library's intention to allow method chaining.
  • Let the user specify an unique identifier each time he defines a tween chain. This clutters the API a bit, but the user can use a "natural language" identifier to cancel tween chains.
  • Use a combination of both: If user does not care about cancelling the tween chain, generate a identifier for him. If users cares about cancelling the tween chain, let him pass an argument for the optional parameter. Best of both worlds in my opinion.

Are you happy with how you define tweens with the Tween component currently? Would it be beneficial to use the approach as described in my last point above? (It would certainly help simplify the code from a developers viewpoint, but would it also benefit the user?)

mucaho avatar Feb 25 '14 18:02 mucaho

It might be worth thinking about whether there's an easy way to abstract the notion of chainable actions out such that you could tie together Animations, Tweens, and any custom-made user events. On the other hand, maybe it's better to just have something that works right now, and abstract that bridge later.

Yep, matthijsgroen discovered to use promises for chaining actions (either in parallel or sequence) together. See https://groups.google.com/forum/#!topic/craftyjs/DadBdot-NkA.

mucaho avatar Dec 14 '15 13:12 mucaho

For animations, I made a simple component: https://github.com/matthijsgroen/game-play/blob/master/app/scripts/components/generic/TweenPromise.coffee

Crafty.c 'TweenPromise',
  init: ->
    @requires 'Tween'

  tweenPromise: (args...) ->
    d = WhenJS.defer()
    @one('TweenEnd', -> d.resolve())
    @tween args...
    d.promise

It simply wraps the tween animation in a promise using WhenJS. And from there I can chain them using sequence, parallel, etc.

But since most of this power comes from an external library (WhenJS), and there are different flavours available, I'm not sure if Crafty should solve this problem (since solving it is quite easy)

matthijsgroen avatar Aug 25 '16 23:08 matthijsgroen

It simply wraps the tween animation in a promise using WhenJS. And from there I can chain them using sequence, parallel, etc.

I think Bluebird is a noteworthy promise library because it could be used to cancel a chain of promises. I don't know of any other promise library that supports this out-of-the box.

But since most of this power comes from an external library (WhenJS), and there are different flavours available, I'm not sure if Crafty should solve this problem (since solving it is quite easy)

Yeah, agreed, an external, nicely written component is the way to go here.

mucaho avatar Mar 29 '17 20:03 mucaho