Archive for February, 2007
AS3 Virtual Timeline Animation System :: v1.0
[Update: June 2, 2007]
Version 2.0 is now live.
[/Update]
Earlier on, I introduced a virtual timeline animation system for AS2 that simplified the process of creating a sequence of animations with code. The system has proven to be quite useful, so I figured it was time to get the thing ported over to AS3. The concept is pretty simple; create a timeline, create tweens, add the tweens to the timeline, and then use basic controls such as Play, PlayReverse, Stop, NextFrame, and PrevFrame to control the timeline. Enough talk; it's time for a demonstration.
In the example below, click and hold to play the animation forwards, and then release the mouse button to watch the animation play in reverse.
Creating this animation with the manager paradox would require a lot more effort. Below is some code straight out of the example file that created the animation above.
-
// Constructor
-
//
-
// Init this object.
-
public function VTASTestApp()
-
{
-
// Create a new timeline object.
-
// The parameter '60' is the frames per second you would like the timeline
-
// to play at. '60' also happens to be the default value if nothing is passed,
-
// but I wanted to pass it as an example.
-
m_objTimeline = new Timeline(60);
-
-
// Create a series of tween objects.
-
// When they are added to a timeline, they are cloned and then added, so you can
-
// create these seperately and add them to multiple timeline objects if you wanted to.
-
var objTween_1:Tween = new Tween(m_mcBox, "x", 400, 1, 60, Transitions.SINE_IN_AND_OUT);
-
var objTween_2:Tween = new Tween(m_mcBox, "y", 140, 40, 80, Transitions.SINE_IN_AND_OUT);
-
var objTween_3:Tween = new Tween(m_mcBox, "rotation", -180, 40, 80, Transitions.SINE_IN_AND_OUT);
-
var objTween_4:Tween = new Tween(m_mcBox, "x", 60, 61, 120, Transitions.SINE_IN_AND_OUT);
-
var objTween_5:Tween = new Tween(m_mcBox, "y", 90, 100, 140, Transitions.SINE_IN_AND_OUT);
-
var objTween_6:Tween = new Tween(m_mcBox, "rotation", 0, 100, 140, Transitions.SINE_IN_AND_OUT);
-
var objTween_7:Tween = new Tween(m_mcBox, "x", 400, 121, 180, Transitions.SINE_IN_AND_OUT);
-
var objTween_8:Tween = new Tween(m_mcBox, "y", 350, 160, 220, Transitions.SINE_IN_AND_OUT);
-
var objTween_9:Tween = new Tween(m_mcBox, "rotation", 90, 150, 190, Transitions.SINE_IN_AND_OUT);
-
var objTween_10:Tween = new Tween(m_mcBox, "rotation", 180, 191, 230, Transitions.SINE_IN_AND_OUT);
-
var objTween_11:Tween = new Tween(m_mcBox, "x", 60, 191, 270, Transitions.SINE_IN_AND_OUT);
-
var objTween_12:Tween = new Tween(m_mcBox, "scaleX", 0.5, 191, 230, Transitions.SINE_IN_AND_OUT);
-
var objTween_13:Tween = new Tween(m_mcBox, "scaleY", 0.5, 191, 230, Transitions.SINE_IN_AND_OUT);
-
var objTween_14:Tween = new Tween(m_mcBox, "scaleX", 1, 231, 270, Transitions.SINE_IN_AND_OUT);
-
var objTween_15:Tween = new Tween(m_mcBox, "scaleY", 1, 231, 270, Transitions.SINE_IN_AND_OUT);
-
-
// Add each tween to the timeline object.
-
m_objTimeline.AddTween(objTween_1);
-
m_objTimeline.AddTween(objTween_2);
-
m_objTimeline.AddTween(objTween_3);
-
m_objTimeline.AddTween(objTween_4);
-
m_objTimeline.AddTween(objTween_5);
-
m_objTimeline.AddTween(objTween_6);
-
m_objTimeline.AddTween(objTween_7);
-
m_objTimeline.AddTween(objTween_8);
-
m_objTimeline.AddTween(objTween_9);
-
m_objTimeline.AddTween(objTween_10);
-
m_objTimeline.AddTween(objTween_11);
-
m_objTimeline.AddTween(objTween_12);
-
m_objTimeline.AddTween(objTween_13);
-
m_objTimeline.AddTween(objTween_14);
-
m_objTimeline.AddTween(objTween_15);
-
-
// Add event listeners to the stage for the mouse down and up events.
-
stage.addEventListener(MouseEvent.MOUSE_DOWN, OnMouseDown);
-
stage.addEventListener(MouseEvent.MOUSE_UP, OnMouseUp);
-
}
-
-
// OnMouseDown
-
//
-
// Called when the mouse is down.
-
protected function OnMouseDown(objEvent:MouseEvent):void
-
{
-
// Play the timeline forwards.
-
m_objTimeline.Play();
-
}
-
-
// OnMouseUp
-
//
-
// Called when the mouse is up.
-
protected function OnMouseUp(objEvent:MouseEvent):void
-
{
-
// Play the timeline in reverse.
-
m_objTimeline.PlayReverse();
-
}
As you can see, the system is very intuitive. By listing out each tween and lining up the values in each column, you are essentially creating a dope sheet for the animation you are envisioning.
I was hoping to fix the bugs in the AS2 system before porting over to AS3, however I got anxious and wanted to put this out there for all of you to play with. A majority of the tweakiness occurs when looping is enabled, but I've been able to create some pretty cool stuff with no problems at all. Between this system and the manager system, you should have all the tools you need to create some pretty awesome animations with minimal effort.
So give it a try and let me know what you think. I'm probably aware of most of the bugs that exist, but feel free to post those as well. Enjoy!
Download 'boostworthy_animation_AS3.zip'
2 commentsAS2: Document Class
In talking with people about the AS3 document class feature, it seems that many are unaware that more elegant entry point solutions exist than simply passing the document scope to a static method of the main application class in AS2. Though this approach works just fine, there are better ways to accomplish this. I am certainly not the first to blog about such a topic, however I thought I would share my favorite approach with all of you.
First off, I have a document utility class titled 'DocumentUtil'; inside of which I have a static method named 'RegisterClass'. This is where the link is made between the document and the main application class (thus creating a document class of sorts). Have a look:
-
// RegisterClass
-
//
-
// Registers the passed document and class together, thus creating a document class.
-
// The document class constructor is invoked upon this method being called.
-
public static function RegisterClass(mcDocument:MovieClip, objClass:Object):Void
-
{
-
// Set the prototype reference to the document classes constructor
-
// prototype property.
-
mcDocument.__proto__ = Function(objClass).prototype;
-
-
// Invoke the document class' constructor within the scope of the document.
-
Function(objClass).apply(mcDocument, null);
-
}
Now, on the first frame of the root timeline you do the following to initialize the application:
-
// Import the document utility class.
-
import com.boostworthy.utils.DocumentUtil;
-
-
// Import the class in which you wish to register as the document class.
-
import BallerApp;
-
-
// Register this document with the specified class.
-
DocumentUtil.RegisterClass(this, BallerApp);
And that is it. The constructor of the class will get called immediately and the scope within the class will be that of the document. Not quite as charming as the AS3 document class, however it will certainly do.
2 commentsUpdates @ Jayson Whitmore
Fellow Full Sail grad Jayson Whitmore has updated his site with a new spring 07 demo reel. He's done some amazing work in the past year, especially the notorious iPod Nano commercial.
2 commentsAS3: Cellular Automata Generator

With the greatly increased performance of Flash 9, it is fun to experiment in ways that previous versions of Flash didn't so much allow. The generation of cellular automata is of particular interest to me. What intrigues me about these systems is the fact that complexity very similar to that seen all throughout nature is generated using simple rulesets. As you can see in the image above, the pattern appears to be quite random, however the rules are well-defined.
Below is the application I wrote to generate that imagery. You can change the ruleset constant to experiment and discover other interesting patterns that emerge.
-
// =========================================================================================
-
// Class: CAGenerator
-
//
-
// Ryan Taylor
-
// February 15, 2007
-
// http://www.boostworthy.com
-
// =========================================================================================
-
//
-
// + + + + + + + + +
-
//
-
// =========================================================================================
-
-
// PACKAGE /////////////////////////////////////////////////////////////////////////////////
-
-
package
-
{
-
// IMPORTS /////////////////////////////////////////////////////////////////////////////
-
-
import flash.display.Sprite;
-
import flash.utils.Timer;
-
import flash.events.TimerEvent;
-
import flash.display.BitmapData;
-
import flash.display.Bitmap;
-
-
// CLASS ///////////////////////////////////////////////////////////////////////////////
-
-
public class CAGenerator extends Sprite
-
{
-
// =================================================================================
-
// CLASS DECLARATIONS
-
// =================================================================================
-
-
// CONSTANTS ///////////////////////////////////////////////////////////////////////
-
-
// The Wolfram ruleset to use for the cellular automata.
-
private static const RULESET:Number = 30;
-
-
// A random seed value for generating the initial condition.
-
private static const RANDOM_SEED:Number = 0.23;
-
-
// MEMBERS /////////////////////////////////////////////////////////////////////////
-
-
// The bitmap data object for storing pixel data to be drawn to a bitmap
-
// and added to the display.
-
protected var m_objBitmap:BitmapData;
-
-
// Stores the current row of generation.
-
protected var m_uRow:uint;
-
-
// =================================================================================
-
// EVENT FUNCTIONS
-
// =================================================================================
-
-
// Constructor
-
//
-
// Init this object.
-
public function CAGenerator()
-
{
-
// Create a new bitmap data object and set it to the size of the stage.
-
m_objBitmap = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0xFFFFFF);
-
-
// Generate the random initial condition.
-
GenerateInitialCondition();
-
-
// Set the current row to '1'.
-
m_uRow = 1;
-
-
// Create a new timer.
-
// It will be called every 10 milliseconds for the height of the stage.
-
var objTimer:Timer = new Timer(10, stage.stageHeight);
-
-
// Add an event listener for the timer event.
-
objTimer.addEventListener(TimerEvent.TIMER, OnTimer);
-
-
// Start the timer, this beginning the generation process.
-
objTimer.start();
-
}
-
-
// OnTimer
-
//
-
// Event handler for the timer event.
-
protected function OnTimer(objEvent:TimerEvent):void
-
{
-
// Generate the current row, then advance to the next row.
-
GenerateRow(m_uRow++);
-
}
-
-
// =================================================================================
-
// GENERATION FUNCTIONS
-
// =================================================================================
-
-
// GenerateRow
-
//
-
// Generates the specified row.
-
protected function GenerateRow(uRow:uint):void
-
{
-
// Loop through all pixels in the row.
-
for(var i:Number = 0; i < stage.stageWidth; i++)
-
{
-
// The number of bits to rotate the ruleset by.
-
var nBits:Number = 0;
-
-
// Calculate the bit rotation by checking the neighboring pixels.
-
nBits += (m_objBitmap.getPixel(i - 1, uRow - 1) == 0x000000) ? 4 : 0;
-
nBits += (m_objBitmap.getPixel(i, uRow - 1) == 0x000000) ? 2 : 0;
-
nBits += (m_objBitmap.getPixel(i + 1, uRow - 1) == 0x000000 )? 1 : 0;
-
-
// Check the pattern against the ruleset to determine if
-
// the pixel should be drawn.
-
if(RULESET >> nBits & 1)
-
{
-
// Draw the pixel.
-
m_objBitmap.setPixel(i, uRow, 0x000000);
-
}
-
}
-
-
// Remove the previous bitmap.
-
removeChildAt(0);
-
-
// Add a new bitmap to the display with the current bitmap data.
-
addChildAt(new Bitmap(m_objBitmap), 0);
-
}
-
-
// GenerateInitialCondition
-
//
-
// Generates a random initial condition.
-
protected function GenerateInitialCondition():void
-
{
-
// Loop through all pixels in the first row.
-
for(var i:Number = 0; i < stage.stageWidth; i++)
-
{
-
// Use the random seed to determine if the current pixel
-
// should be drawn.
-
if(Math.random() < RANDOM_SEED)
-
{
-
// Draw the pixel.
-
m_objBitmap.setPixel(i, 0, 0x000000);
-
}
-
}
-
-
// Add a new bitmap to the display with the current bitmap data.
-
addChildAt(new Bitmap(m_objBitmap), 0);
-
}
-
}
-
}
1 comment
Adobe Flash Platform User Group Of Atlanta
Those of you who live in the Atlanta area and are interested in Flash, Flex, Apollo, etc. should come to the monthly AFPUG meetings. They are the second Tuesday of every month at Roundbox Global.
Tonight, Jesse Warden spoke about dynamic data in the worlds of Flex and Flash. He showed some various examples of projects that consumed data from sources such as Amazon and Yahoo and also shared some tips regarding Flex animation performance. It was pretty basic material however he's a very entertaining speaker and we had a good talk afterwards.
It's a great way to meet people from all the different studios around town and there are some cool topics scheduled to be covered in the months to come, so definitely try and make it if you can.
No commentsUpdates @ ISO50
Scott Hansen has updated his site with some new content. He is such a huge inspiration to me; his design and musical creations are always top-notch.
No commentsOn Flash Lite And The Internet
Adobe's Flash Lite, the Flash Player runtime designed specifically for mobile phones, is in the news today. It was announced that Flash Lite 3, scheduled to be released sometime between now and June, will support Flash video. Though many will disagree with what I am about to say, it still needs to be said.
Why? Why develop for Flash Lite? Am I the only one that sees this? Right now, the internet experience on mobile devices is basically worthless. This is due to the fact that accessing the internet through your service provider is insanely slow and also because phones are terrible devices for browsing the internet. Modern phones were not designed for the internet and they are on the verge of being extinct.
The iPhone is just around the corner and with it will come many other phones of it's caliber, from Apple and otherwise. Safari on the iPhone is just the beginning; I won't be surprised at all when I see a version of Firefox running on a mobile phone this year. It's too obvious to ignore...designing special software for phones isn't the solution; designing phones that use the internet the way we are used to on our computers is.
People argue that only the high-end phones will provide this luxury, and to that I argue right back - for how long? And not only that, but clearly the cheaper phones are for people that just want a phone and not a computer in their pocket. You don't buy a Honda and expect it to perform like a Ferrari. So when are people going to realize that we need to design internet applications for the internet in general, not some for computers, some for phones, etc. If a device has a super tiny screen and terrible controls for browsing the internet, why why whyyy try and make an internet application for it? It just doesn't make sense.
The bottom line is, we don't need to accommodate for those devices; those devices need to accomodate for us. I have little doubt that Flash Lite 3 will be the final version of Flash Lite. No offense to the hard-working teams at Adobe, but what the world really needs is the regular Flash Player runtime on the next generation of mobile phones.
2 commentsAS2: URL Object
Here's a gem that will make working with URLs much easier. Common tasks such as extracting the protocol, host, domain, port, path and hash are now simply a method call away. In the case of queries, you can get them returned as either one big string or a hash map containing the data as key and value pairs. Enough talking, here's an example.
-
// Import the necessary classes.
-
import com.boostworthy.datatypes.URL;
-
import com.boostworthy.collections.HashMap;
-
import com.boostworthy.collections.iterators.Iterator_I;
-
-
// Create a new URL object and set it's URL.
-
var objURL:URL = new URL("https://www.boostworthy.com:1010/index.html#sample_hash?a=123&b=456");
-
-
// Example traces of various information about the URL.
-
trace("URL: " + objURL.valueOf()); // https://www.boostworthy.com:1010/index.html#sample_hash?a=123&b=456
-
trace("Protocol: " + objURL.GetProtocol()); // https:
-
trace("Host: " + objURL.GetHost()); // www.boostworthy.com
-
trace("Domain: " + objURL.GetDomain()); // boostworthy.com
-
trace("Port: " + objURL.GetPort()); // 1010
-
trace("Path: " + objURL.GetPath()); // index.html
-
trace("Hash: " + objURL.GetHash()); // sample_hash
-
trace("Is Secure: " + objURL.IsSecure()); // true
-
trace("Query As String: " + objURL.GetQueryAsString()); // a=123&b=456
-
trace("Query As HashMap: ");
-
-
// Store the returned hash map.
-
var objHashMap:HashMap = objURL.GetQueryAsHashMap();
-
-
// Get an iterator instance from the hash map.
-
var objIterator:Iterator_I = objHashMap.GetIterator();
-
-
// Iterate through the contents of the hash map.
-
while(objIterator.HasNext())
-
{
-
// Get each key.
-
var strKey:String = String(objIterator.Next());
-
-
// Output the key and it's associated value.
-
trace("Key: " + strKey + " -> Value: " + objHashMap.Get(strKey));
-
-
// Key: a -> Value: 123
-
// Key: b -> Value: 456
-
}
Hopefully you will find this to be as handy as I have. If you can think of any other common tasks that would be appropriate for this object, definitely let me know. Enjoy!
Download 'boostworthy_url_object.zip'
No commentsSteve Jobs: Thoughts On Music
DRM systems don't work; it's no secret. I would buy a lot more music off of the iTunes store if I didn't have to worry about how many computers I can have it on or how many CDs I can burn it to. Finally, Steve Jobs himself speaks out against the music industry and how ridiculous it is that they force Apple, Sony, and Microsoft to use these systems on downloadable music.
http://www.apple.com/hotnews/thoughtsonmusic/
I think the era of DRM-protected music is about to end. What do you think?
2 comments