VectorPath – a new plugin for Tween Engine

A wonderful new plug-in for Tween Engine, which gives you the possibility to boldly animate sprites along a complex path WITHOUT any worries concerning Besier curves. You just draw a path as #vectorShape, put it on the Stage and that is all. Below are some examples:

Example 1

We want to animate sprite 2 along a path. The path is on sprite 1. The minimum of code for this might be:
on beginSprite( me )
tweener = script( "TweenLite" ).getInstance()
tweener.activatePlugin( "VectorPathPlugin" )
tweener.to( sprite(2), 3, [ #vectorPath:sprite(1)] )
end beginSprite
In this particular case, for the sake of the code perspicuity, I have used

on beginSprite( me )
tweener = script( "TweenMax" ).getInstance()
tweener.activatePlugin( "VectorPathPlugin" )
tweener.to( sprite(2), 3, [ #vectorPath:sprite(1), #repeat:-1, #delay:1] )
end beginSprite

Example 1 demo

The animation sequence begins from the current position of sprite 2 and develops to the first point of the path.


Example 2

The #vectorPath property can take two types of values – sprite or #vectorShape type castmember. See how the same example will look, if the #vectorShape value is changed and instead of sprite 1 we indicate directly its castmember.
on beginSprite( me )
tweener = script( "TweenMax" ).getInstance()
tweener.activatePlugin( "VectorPathPlugin" )
tweener.to( sprite(2), 3, [ #vectorPath:member("path1"), #delay:1] )
end beginSprite

Example 2 demo

We do not need sprite 1 any more. This time the animation behaves as if the sprite 2 position was the initial point of the path.


Example 3

This plug-in can be integrated with TimelineLite and TimelineMax. Below is a example of this:

property myTimeline

on beginSprite( me )
tweener = script( "TweenMax" ).getInstance()
tweener.activatePlugin( "VectorPathPlugin" )

timelineSettings = [:]
timelineSettings.addProp( #delay, 1 )
timelineSettings.addProp( #onComplete, [ #handler:#reverseAnimation, #object:me ] )
timelineSettings.addProp( #onReverseComplete, [ #handler:#playAnimation, #object:me ] )
myTimeline = script( "TimelineLite" ).new( timelineSettings )

d = 4.25
e = "Sine.EaseOut"
p = sprite(1)
tweens = []
repeat with i = 2 to 8
tweens.append( tweener.to( sprite(i),d,[ #vectorPath:p, #orientToPath:true, #ease:e ] ) )
end repeat
myTimeline.appendMultiple( tweens, 0, "start", 0.1 )
end beginSprite

on playAnimation( me, args )
myTimeline.play()
end playAnimation

on reverseAnimation( me, args )
myTimeline.reverse()
end reverseAnimation

This is how the final result should look like:

Example 3 demo

Example 4

In order to animate 3D objects we directly use a #vectorShape castmember. For example:

on sceneReady( me, models )
tweener = script( "TweenMax" ).getInstance()
tweener.activatePlugin( "VectorPathPlugin" )
d = 1
repeat with i = 1 to count( models )
tweener.to( models[i], 3, [ #vectorPath:member("path1"), #repeat:-1, #orientToPath:true, #ease:"Sine.EaseInOut", #yoyo:true, #delay:d] )
d = d + 0.1
end repeat
end sceneReady

Example 4 demo

Here is the code of the plug-in itself. Add it as a #parent script under the name of “VectorPathPlugin” to the same cast where you have put the rest of the TweenEngine scripts.

Show code

[lingo]
— "VectorPathPlugin" parent script
on getPluginName( this )
return #VectorPath
end getPluginName

— @parent
property ancestor
property _RAD2DEG — #Float
property _orientData — #list
property _orient — #boolean
property _segments — #List
property _count — #Integer
property _future — #Proplist
property _target — #Instance
property _targetType — #symbol; change factor method – depends of the _target type – #sprite, #model, etc.
property _startRot — #float; Original 2D object rotation

— USAGE:
— tweener = script( "TweenMax" ).getInstance()
— tweener.activatePlugin( "VectorPathPlugin" )
— tweener.to( sprite(2), 5.25, [ #vectorPath:sprite(1), #orientToPath:true ] )
— OR
— tweener.to( sprite(2), 5.25, [ #vectorPath:member("path1"), #orientToPath:true ] )

on new( me, s )
ancestor = script( "TweenPlugin" ).rawNew()
callAncestor( #new, me )
me.propName = #vectorpath
me.overwriteProps = [ #x, #y ]
_future = [:]
_RAD2DEG = 180 / PI
_startRot = 0
return me
end new

on dispose( me )
callAncestor( #dispose, me )
_future.deleteAll()
_future = VOID
_target = VOID
_segments.deleteAll()
_segments = VOID
end dispose

on onInitTween( me, atarget, avalue, atween )
if( atarget.is( #SpriteWrapper ) ) then
_targetType = #two
_startRot = atween.target.getRotationZ()
else if( atarget.is( #NodeWrapper ) ) then
_targetType = #three
else
return FALSE
end if

if( ilk( avalue ) = #sprite ) then
if( avalue.member.type <> #vectorShape ) then return FALSE
vlist = avalue.member.vertexList.duplicate()
vlist.addAt( 1, me._screenToSprite( point( atarget.getX(), atarget.getY() ), avalue ) )

_offset = map( point( avalue.member.width *0.5, avalue.member.height *0.5 ), avalue.member.rect, avalue.rect )
repeat with i = 1 to count( vlist )
element = vlist[i]
if( voidP( element[#handle1] ) ) then
element.addProp( #handle1, point( 0, 0 ) )
end if
if( voidP( element[#handle2] ) ) then
element.addProp( #handle2, point( 0, 0 ) )
end if
p = map( element.vertex, avalue.member.rect, rect( 0, 0, avalue.rect.width, avalue.rect.height ) ) + _offset
h1 = p + map( element.handle1, avalue.member.rect, rect( 0, 0, avalue.rect.width, avalue.rect.height ) )
h2 = p + map( element.handle2, avalue.member.rect, rect( 0, 0, avalue.rect.width, avalue.rect.height ) )
element.vertex = p
element.handle1 = h1
element.handle2 = h2
end repeat

else if( ilk( avalue ) = #member ) then
if( avalue.type <> #vectorShape ) then return FALSE
vlist = avalue.vertexList.duplicate()
— get maximum x and y point coordinates
maxX = -(the maxinteger)
maxY = -(the maxinteger)
repeat with v in vlist
maxX = max( maxX, v.vertex.locH )
maxY = max( maxY, v.vertex.locV )
end repeat

— Modify the original vertexlist, addind maxX and maxY
— and the difference between target and the first point of the path
— to "translate" the coordinates to the new origin.
inv = 1
if( _targetType = #three ) then
inv = -1
end if

diff = point( atarget.getX(), atarget.getY() * inv ) – ( point( vlist[1].vertex.locH + maxX, vlist[1].vertex.locV + maxY ))
offsetx = diff.locH
offsety = diff.locV
repeat with i = 1 to count( vlist )
element = vlist[ i ]
if( voidP( element[#handle1] ) ) then
element.addProp( #handle1, point( 0, 0 ) )
end if
if( voidP( element[#handle2] ) ) then
element.addProp( #handle2, point( 0, 0 ) )
end if
element.vertex = point( element.vertex.locH + maxX, element.vertex.locV + maxY ) + point( offsetx, offsety )
element.handle1 = element.vertex + element.handle1
element.handle2 = element.vertex + element.handle2
end repeat
end if

if( ilk( vlist ) <> #list ) then return FALSE
me.init( atween, vlist, FALSE )
return TRUE
end onInitTween

on _screenToSprite( me, apoint, asprite )
p = apoint – point( asprite.left, asprite.top ) – point( asprite.member.width*0.5, asprite.member.height*0.5 )
return [#vertex: p, #handle1:point(0,0), #handle2:point(0,0) ]
end _screenToSprite

on init( me, tween, beziers )
_target = tween.target
_segments = me.parseVectorPath( beziers )
_count = count( _segments )
enumerables = tween.vars
if ( enumerables[ #orientToPath ] = TRUE ) then
if( _target.is( #SpriteWrapper ) ) then
_orientData = [ [ #x, #y, #rotationZ, 0, 0.01 ] ]
else if( _target.is( #NodeWrapper ) ) then
_orientData = [ [ #x, #y, #z, 0, 0.1 ] ]
end if
_orient = TRUE
else if ( ilk( enumerables[ #orientToPath ] ) = #list ) then
_orientData = enumerables[ #orientToPath ]
_orient = TRUE
end if
end init

on changeFactor( me, n )
if ( n < 0 ) then
segment = 1
else if ( n >= 1 ) then
segment = _count
else
segment = max( 1, ceil( _count * n ) )
end if

innerRatio = ( n – ( ( segment – 1 ) * ( 1.0 / _count ) ) ) * _count
section = _segments[ segment ]
r2 = innerRatio * innerRatio
r3 = r2 * innerRatio
p0 = section[1]
p1 = section[2]
p2 = section[3]
repeat with i = 1 to 2
prop = me.overwriteProps[i]
c = 3 * ( p1[prop] – p0[prop] )
b = 3 * ( p2[prop] – p1[prop]) – c
a = section[4][prop] – p0[prop] – c – b
xvalue = a * r3 + b * r2 + c * innerRatio + p0[prop]
if ilk( me._target ) = #instance then
call( prop, me._target, xvalue )
else
me._target[ prop ] = xvalue
end if
end repeat

if (_orient) then
i = count( _orientData )
curVals = [:]
repeat while ( i > 0 )
cotb = _orientData[ i ] –current orientToBezier Array
j = 3 — Iterate through cotb’s props – #x, #y, #z / #rotationZ
if( ilk( me._target ) = #propList ) then
repeat while( j > 0 )
curVals[cotb[j]] = me._target[cotb[j]]
j = j – 1
end repeat
else
repeat while( j > 0 )
curVals[cotb[j]] = call( symbol( "get" & cotb[ j ] ), me._target )
j = j – 1
end repeat
end if
i = i – 1
end repeat
oldTarget = me._target
me._target = _future
_orient = false
i = count( _orientData )

repeat while ( i > 0 )
cotb = _orientData[ i ] –current orientToBezier Array
me.changeFactor( n + max(cotb[4], cotb[5]) )
if( _targetType = #two ) then
toAdd = max( cotb[4], 0 )
dx = _future[cotb[1]] – curVals[cotb[1]]
dy = _future[cotb[2]] – curVals[cotb[2]]
call( cotb[ 3 ], oldTarget, atan( dy, dx ) * _RAD2DEG + toAdd + _startRot )
else if( _targetType = #three ) then
dx = _future[ cotb[1] ]
dy = _future[ cotb[2] ]
dz = _future[ cotb[3] ]
oldTarget._target.pointAt( vector( dx, dy, dz ), vector( 0, 0, 1 ) )
end if
i = i -1
end repeat
me._target = oldTarget
_orient = true
end if
end changeFactor

on parseVectorPath( me, v )
cnt = count( v ) – 1
t = []
inv = 1
if( _targetType = #three ) then
inv = -1
end if
repeat with i = 1 to cnt
section = []
a = v[ i ]
b = v[ i + 1 ]
p1 = a.vertex
p2 = b.vertex
h1 = a.handle1
h2 = b.handle2
section.append( [ #x:p1.locH, #y:p1.locV * inv ] ) — first point
section.append( [ #x:h1.locH, #y:h1.locV * inv ] ) — first handle
section.append( [ #x:h2.locH, #y:h2.locV * inv ] ) — second handle
section.append( [ #x:p2.locH, #y:p2.locV * inv ] ) — second point
t.append( section )
end repeat
return t
end parseVectorPath
[/lingo]

 
Comments

Watch Main Designed A Undertake Watch Functions Best. Catarina Rance Gilda

Leave a Reply