Create your own tween manager class in AS3

I publish this tutorial with the permission of Krasimir Tsonev, from Bulgaria, who runs krasimirtsonev.com (take a look at the light bulbs: don’t you think it can be done using a basic Box2D rope?). It’s an amazing tutorial with clean and well commented code that will guide you through the process of the creation of a custom tween manager class with AS3.

There are some features in Flash that we can’t work without. Tween classes are among the most used ones. They give you ability to animate objects without using the timeline, to change the animation fast and easy. The idea of these classes is very simple. That’s why I think that it is a good idea to have your own tween manager that you can modify to fit into your needs.

The basic structure of our tween manager:

1
2
3
4
5
6
7
8
9
10
11
package {
	import flash.display.MovieClip;
	public class TweenManager extends MovieClip {
		private var _objectToModify:Object;
		private var _properties:Object;
		public function TweenManager(objectToModify:Object, properties:Object) {
			_objectToModify = objectToModify;
			_properties = properties;
		}
	}
}

We are going to pass the object that we want to modify and the properties that we want to change. The idea is to create a function that calls every frame. All the magic will be done there. As you can see in the code below I created a public function – start, which adds listener for ENTER_FRAME event. So now we have a repeated method, i.e. loop.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package {
	import flash.display.MovieClip;
	import flash.events.Event;
	public class TweenManager extends MovieClip {
		private var _objectToModify:Object;
		private var _properties:Object;
		public function TweenManager(objectToModify:Object, properties:Object) {
			_objectToModify = objectToModify;
			_properties = properties;
		}
		public function start():void {
			addEventListener(Event.ENTER_FRAME, loop);
		}
		public function loop(e:Event):void {
		}
	}
}

What are we going to pass as properties parameter? I think it is a good idea to use JSON object, because it’s really flexible and we can add/remove properties really fast without changing anything in our class. Here is an example of the creation of an object from our tween class.

1
2
3
4
5
6
7
8
9
10
11
package {
	import flash.display.MovieClip;
	public class App extends MovieClip {
		public var clip:MovieClip;
		public function App() {
			var properties:Object = {x:{start:50,end:390,steps:100}};
			var t:TweenManager = new TweenManager(clip,properties);
			t.start();
		}
	}
}

There are two things that you have to notice. The first one is that you should have a movie clip on the stage that you can use for the tween. The second thing is the properties object. As you can see we are going to change the “x” property of the clip from 50 to 390 for 100 steps, i.e. 100 frames. To be able to do that we need the value of “x” for each one of these 100 frames. The function “calculateValues” will do that for us:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package {
	import flash.display.MovieClip;
	import flash.events.Event;
	public class TweenManager extends MovieClip {
		private var _objectToModify:Object;
		private var _properties:Object;
		public function TweenManager(objectToModify:Object, properties:Object) {
			_objectToModify = objectToModify;
			_properties = properties;
		}
		public function start():void {
			for (var i:* in _properties) {
				_properties[i].values = calculateValues(_properties[i].start,_properties[i].end,_properties[i].steps);
			}
			addEventListener(Event.ENTER_FRAME, loop);
		}
		public function loop(e:Event):void {
		}
		private function calculateValues(startPos:Number, endPos:Number, steps:Number):Array {
			var values:Array = [];
			for (var i:int=0; i<steps+1; i++) {
				values.push(startPos + (((endPos - startPos) / steps)*i));
			}
			return values;
		}
	}
}

As you can see we are adding dynamically a new array for every property in our properties object. “values” is an array with all the values of “x” for each of these 100 frames. The last thing that we should make is to pass the values to our clip, i.e. to write the content of the “loop” method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package {
	import flash.display.MovieClip;
	import flash.events.Event;
	public class TweenManager extends MovieClip {
		private var _objectToModify:Object;
		private var _properties:Object;
		public function TweenManager(objectToModify:Object, properties:Object) {
			_objectToModify = objectToModify;
			_properties = properties;
		}
		public function start():void {
			for (var i:* in _properties) {
				_properties[i].values = calculateValues(_properties[i].start,_properties[i].end,_properties[i].steps);
				_properties[i].valueIndex = 0;
				_properties[i].isItDone = false;
			}
			addEventListener(Event.ENTER_FRAME, loop);
		}
		public function loop(e:Event):void {// looping through the properties  
			for (var i:* in _properties) {// checking if the properties tween is done     
				if (! _properties[i].isItDone) {// checking if the current value is the last one            
					if (_properties[i].valueIndex == _properties[i].steps + 1) {// marking the tween as a done 
						_properties[i].isItDone = true;
					}
					else {// applying the value     
						_objectToModify[i] = _properties[i].values[_properties[i].valueIndex];// incrementing the values' index 
						_properties[i].valueIndex +=  1;
					}
				}
			}// checking if all the tweens are done   
			var areAllDone:Boolean = true;
			for (i in _properties) {
				if (! _properties[i].isItDone) {
					areAllDone = false;
				}
			}// if yes then remove the listener    
			if (areAllDone) {
				removeEventListener(Event.ENTER_FRAME, loop);
			}
		}
		private function calculateValues(startPos:Number, endPos:Number, steps:Number):Array {
			var values:Array = [];
			for (var i:int=0; i<steps+1; i++) {
				values.push(startPos + (((endPos - startPos) / steps)*i));
			}
			return values;
		}
	}
}

Please notice lines 18 and 19. We added a flag (isItDone) that indicates when the tween is finished and “valueIndex”, which we used to go through all the values.
When you run the flash you will see that the circle is moving from x=50 to x=390 for 100 frames. Let’s add some other properties and see how it looks:

1
2
3
4
5
6
7
8
9
10
11
package {
	import flash.display.MovieClip;
	public class App extends MovieClip {
		public var clip:MovieClip;
		public function App() {
			var properties:Object = {x:{start:50,end:390,steps:70},y:{start:50,end:390,steps:70},rotation:{start:0,end:180,steps:30},alpha:{start:1,end:0.5,steps:10}};
			var t:TweenManager = new TweenManager(clip,properties);
			t.start();
		}
	}
}

We can change as many properties as we want and obviously to set different steps for each one of them.

Ok, the Tween class looks good so far but it isn’t as cool as we wanted. It’s because there is no easing. We used only a linear type of motion. To add other types we’re going to use some maths by Robert Penner. We will just change the calculateValues method and it will support different types of motions. Here is the final version of the class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package {
	import flash.display.MovieClip;
	import flash.events.Event;
	public class TweenManager extends MovieClip {
		private var _objectToModify:Object;
		private var _properties:Object;
		public function TweenManager(objectToModify:Object, properties:Object) {
			_objectToModify = objectToModify;
			_properties = properties;
		}
		public function start():void {
			for (var i:* in _properties) {
				_properties[i].values = calculateValues(_properties[i].start,_properties[i].end,_properties[i].steps,_properties[i].method);
				_properties[i].valueIndex = 0;
				_properties[i].isItDone = false;
			}
			addEventListener(Event.ENTER_FRAME, loop);
		}
		public function loop(e:Event):void {// looping through the properties          
			for (var i:* in _properties) {// checking if the properies twee it's done 
				if (! _properties[i].isItDone) {// checking if the current value is the last one    
					if (_properties[i].valueIndex == _properties[i].steps + 1) {// marking the tween as a done          
						_properties[i].isItDone = true;
					}
					else {// applying the value 
						_objectToModify[i] = _properties[i].values[_properties[i].valueIndex];// incrementing the values' index     
						_properties[i].valueIndex +=  1;
					}
				}
			}// checking if all the tweens are done 
			var areAllDone:Boolean = true;
			for (i in _properties) {
				if (! _properties[i].isItDone) {
					areAllDone = false;
				}
			}// if yes then remove the listener  
			if (areAllDone) {
				removeEventListener(Event.ENTER_FRAME, loop);
			}
		}
		private function calculateValues(startPos:Number, endPos:Number, steps:Number, mtd:String):Array {
			var arr:Array = new Array();
			var calcEndPos:Number;
			for (var i:Number=0; i<=steps; i++) {
				calcEndPos = (endPos-startPos);
				switch (mtd) {
					case "InBack" :
						arr[i] = Ease.InBack(i,startPos,calcEndPos,steps,1.70158);
						break;
					case "OutBack" :
						arr[i] = Ease.OutBack(i,startPos,calcEndPos,steps,1.70158);
						break;
					case "InOutBack" :
						arr[i] = Ease.InOutBack(i,startPos,calcEndPos,steps,1.70158);
						break;
					case "OutBounce" :
						arr[i] = Ease.OutBounce(i,startPos,calcEndPos,steps);
						break;
					case "InBounce" :
						arr[i] = Ease.InBounce(i,startPos,calcEndPos,steps);
						break;
					case "InOutBounce" :
						arr[i] = Ease.InOutBounce(i,startPos,calcEndPos,steps);
						break;
					case "InCirc" :
						arr[i] = Ease.InCirc(i,startPos,calcEndPos,steps);
						break;
					case "OutCirc" :
						arr[i] = Ease.OutCirc(i,startPos,calcEndPos,steps);
						break;
					case "InOutCirc" :
						arr[i] = Ease.InOutCirc(i,startPos,calcEndPos,steps);
						break;
					case "In" :
						arr[i] = Ease.In(i,startPos,calcEndPos,steps);
						break;
					case "Out" :
						arr[i] = Ease.Out(i,startPos,calcEndPos,steps);
						break;
					case "InOut" :
						arr[i] = Ease.InOut(i,startPos,calcEndPos,steps);
						break;
					case "InElastic" :
						arr[i] = Ease.InElastic(i,startPos,calcEndPos,steps,0,0);
						break;
					case "OutElastic" :
						arr[i] = Ease.OutElastic(i,startPos,calcEndPos,steps,0,0);
						break;
					case "InOutElastic" :
						arr[i] = Ease.InOutElastic(i,startPos,calcEndPos,steps,0,0);
						break;
					case "InExpo" :
						arr[i] = Ease.InExpo(i,startPos,calcEndPos,steps);
						break;
					case "OutExpo" :
						arr[i] = Ease.OutExpo(i,startPos,calcEndPos,steps);
						break;
					case "InOutExpo" :
						arr[i] = Ease.InOutExpo(i,startPos,calcEndPos,steps);
						break;
					case "Linear" :
						arr[i] = Ease.Linear(i,startPos,calcEndPos,steps);
						break;
					case "InLinear" :
						arr[i] = Ease.InLinear(i,startPos,calcEndPos,steps);
						break;
					case "OutLinear" :
						arr[i] = Ease.OutLinear(i,startPos,calcEndPos,steps);
						break;
					case "InOutLinear" :
						arr[i] = Ease.InOutLinear(i,startPos,calcEndPos,steps);
						break;
					case "InQuad" :
						arr[i] = Ease.InQuad(i,startPos,calcEndPos,steps);
						break;
					case "OutQuad" :
						arr[i] = Ease.OutQuad(i,startPos,calcEndPos,steps);
						break;
					case "InOutQuad" :
						arr[i] = Ease.InOutQuad(i,startPos,calcEndPos,steps);
						break;
					case "InQuart" :
						arr[i] = Ease.InQuart(i,startPos,calcEndPos,steps);
						break;
					case "OutQuart" :
						arr[i] = Ease.OutQuart(i,startPos,calcEndPos,steps);
						break;
					case "InOutQuart" :
						arr[i] = Ease.InOutQuart(i,startPos,calcEndPos,steps);
						break;
					case "InQuint" :
						arr[i] = Ease.InQuint(i,startPos,calcEndPos,steps);
						break;
					case "OutQuint" :
						arr[i] = Ease.OutQuint(i,startPos,calcEndPos,steps);
						break;
					case "InOutQuint" :
						arr[i] = Ease.InOutQuint(i,startPos,calcEndPos,steps);
						break;
					case "InSine" :
						arr[i] = Ease.InQuint(i,startPos,calcEndPos,steps);
						break;
					case "OutSine" :
						arr[i] = Ease.OutQuint(i,startPos,calcEndPos,steps);
						break;
					case "InOutSine" :
						arr[i] = Ease.InOutQuint(i,startPos,calcEndPos,steps);
						break;
					default :
						arr[i] = Ease.Linear(i,startPos,calcEndPos,steps);
						break;
				}
			}
			return arr;
		}
	}
}

And the usage:

1
2
3
4
5
6
7
8
9
10
11
package {
	import flash.display.MovieClip;
	public class App extends MovieClip {
		public var clip:MovieClip;
		public function App() {
			var properties:Object = {x:{start:50,end:390,steps:170,method:"OutBack"},y:{start:50,end:390,steps:170,method:"OutBounce"},rotation:{start:0,end:180,steps:300,method:"OutElastic"},alpha:{start:1,end:0.5,steps:100}};
			var t:TweenManager = new TweenManager(clip,properties);
			t.start();
		}
	}
}

Together with “start”, “end” and “steps” we are passing a new property “method” which is used by the “calculateValues” function. The class “Ease” contains mathematics methods that calculate the values. No need to understand it, just have to know how to use it.

And this is an example:

You may need to reload the page to see it in action.

Of course the class is not fully functional. I mean there are no checks if some of the properties like “start” or “end” are missing. We can also add an ability to call a callback method when some of the tweens finish. It’s just a basic manager that you can develop.

Download the source code.

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (7 votes, average: 4.86 out of 5)
Loading ... Loading ...
Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.
Be my fan on Facebook and follow me on Twitter! Exclusive content for my Facebook fans and Twitter followers

This post has 9 comments

  1. Onion

    on August 10, 2010 at 9:53 am

    Wonderful post once again, but you could and should definitely do something about:
    “You may need to reload the page to see it in action.”
    A little button that reloads the swf only would be great.

  2. Friedrich

    on August 10, 2010 at 12:05 pm

    I recently tried to write my own tweening class too, but this library fulfilled all my wishes : http://www.greensock.com/

    Its open-source and free of charges for flashgames without microtransactions.

    Cheers,
    Friedrich

  3. MC

    on August 12, 2010 at 10:07 am

    Since you can’t use “stop” and “gotoAndPlay” inside movieclips in AS3, i need one way to handle movieclip LOOPS in external classes (gotoAndPlay(“Walk”) or gotoandPlay(“Hurt”) with different _totalframes numbers)… how can i control this?

  4. Joshua

    on August 12, 2010 at 11:24 am

    Here’s an awesome tween library, that works well with code completion and runs faster than TweenLite

    http://code.google.com/p/actuate/

  5. Dharshan Venkadesan

    on August 15, 2010 at 4:58 am

    Really simple and nice. thank you !

  6. senthil kumaran chinnthambi

    on August 17, 2010 at 3:20 pm

    Really cool…….. i will write my own package………. emanuele rocks………

  7. Yarden Refaeli

    on August 24, 2010 at 9:49 am

    Why should I waste my time programming such a lib when I can use the great GreenSock Tweening Enging and spend my time creating great games…

    :)

  8. Matt

    on August 26, 2010 at 6:49 pm

    Yeah, it’s nice to understand how to manage tweens and it’ll definitely make you a better program to roll your own tween manager, at an exercise. But from a practical standpoint, there is every reason to NOT do that. TweenMax is extremely capable of handling whatever tweening you need to do, and it’s heavily tested, so you can trust it to work properly. The same can’t be said of a tween manager you write yourself. It’s kinda like building your own car. I’m sure you could if you really worked hard, and you’d learn a lot from it, but who’d want to ride in it?

  9. shachar oz

    on December 11, 2010 at 2:03 am

    great open source code, man!