Crafty
Crafty copied to clipboard
added TweenChain
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
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.
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?
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 callcancelTweenChain('x')
and it will cancel the chain wherex
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?)
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.
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)
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.