Flash AS3: Why can’t I extend my AS3 class?

January 4th, 2010

As I have made the move from AS2 to AS3, I have run into several situations where Flash refused to let me extend an AS3 class. I have spent many hours pounding my head on the desk, screaming “Why doesn’t this work?!” Well, that may be an exaggeration, but I have definitely spent many hours on this issue.

The problem is related to using direct references to MovieClip instance names in an AS3 class. If you associate an AS3 class with a MovieClip using the linkage property of the MovieClip …AND… you have direct references to any of the MovieClip’s children instance names, you can NOT extend the class. I have created a packet of examples (click here to download the packet) to help demonstrate what I’m talking about:

Example 1 (marked basic_linkage_example in the packet): This is simply an example of the base scenario. The buttons on the stage are each instances of the MovieClip bounceButton, which has a base class linkage of BounceButtonTweenLite. This class handles the rollover functionality of the button (with the help of the amazing TweenLite library) and also provides some other basic functionality, like the ability to enable and disable a button. If you open up this class file, notice that only the child Movieclip with the instance name graphic_mc actually bounces. There is another child MovieClip that provides a hit area larger than the graphic to prevent any rollover “flickering” when the button bounces. This is a great example of needing to target a child MovieClip and doing it directly with the child clip’s instance name.

Example 2 (marked linkage_extension_example in the packet): This shows what happens when I try to extend the BounceButtonTweenLite class with a class called NavButton. notice that the NavButton class does not override any of the base class functionality - it simply adds an init() function and establishes a MouseEvent.CLICK listener. If you try to publish this file, you will see that it throws a couple of compiler errors and it doesn’t work. Again, this is because the BounceButtonTweenLite class contains a direct reference to graphic_mc, which is the instance name of one of its child MovieClips. I can’t extand this class as long as I am using the linkage property to associate the NavButton class with my bounceButton MovieClip.

Example 3 (marked composition_example in the packet): This shows how you can make it work by using a composition method of associating classes with MovieClips. When using composition, the linkage property of the MovieClip isn’t used. Instead, you send references to the MovieClip (and any needed children) into the class. Since the references are no longer tied to timeline instance names, you can extend it without any problems. In this example, I instantiated new instances of the NavButton class and sent references to each MovieClip and it’s child graphic. The code that handles this composition method of associating the MovieClips and their classes is on the main timeline. If this were a real project, I would place the code in another class, but I wanted to keep this simple.

Variations on this same issue keep popping up in my projects. I recently had a project which had several different bounceButton-style MovieClips that each contained different graphics. They all shared the same BounceButtonTweenLite base class. That was no problem. The problem came when I wrapped each of those in another set of MovieClips, each with a base class called SelectorButton. The SelectorButton class had a reference to the inner button’s instance name. It didn’t work. Why? Well, I had 8 DIFFERENT inner button MovieClips that shared a base class. So when the wrapper class tried to reference it, the flash compiler freaked out. It was basically saying: “Hey! You told me that these MovieClips were all the same, but they aren’t!” Even though they had the same basic structure, the MovieClips weren’t EXACTLY the same, so the compiler didn’t like it. Again, a composition method fixed the problem.

Using a MovieClip’s linkage is a great way to associate classes with MovieClips - it’s fast and easy. Frankly, I do it all the time and I’m not likely to quit anytime soon. Unfortunately, as you get better at class-based programming, you will find that the linkage property creates problems like these and that a composition-based structure is a lot cleaner. From an object-oriented programming perspective, composition also helps to separate actionscript code from graphics, making the entire project a whole lot more modular. Big Spaceship uses composition so that their projects can be passed from Flash to Flex and vice versa. They talk about it here (in fact, this post is where I got the idea to use composition to fix my problems with class extension).

I hope that this explanation makes some sense and saves you a little time. As usual, you can download the sample files here.

Flash AS3 PrintJob: Problems printing from Flash

October 12th, 2009

Flash has a feature called PrintJob that allows you to print MovieClips - either by creating PDFs or simply sending them directly to a printer. Unfortunately, PrintJob has a poorly documented bug that can cause a lot of headaches. Sometimes, it will print blank pages. Other times, it will scale the pages incorrectly. The behavior is seemingly random and it is different on different computers. I had a lot of trouble printing from Flash with a recent project at work, but there is a remarkably simple solution. Read on…

In my project, I allowed a user to save PDFs to a print queue for printing at a later time. This was done by simply pushing the MovieClips onto an array. When a user hit the print button, I created a PrintJob and then looped through the array of MovieClips, scaling each one as needed to fill the page and then sending them to the printer. The actionscript code looked like this:

//-prints all saved pdfs:
public function printAllPages():void {
	if (savedPdfsArray.length > 0) { //if there are pdfs to print...
		var pj:PrintJob = new PrintJob(); //create printjob
		if(pj.start()) {
			for (var i:int=0; i < savedPdfsArray.length; i++) {
				//scale it to fill the page (portrait orientation):
				var myScale:Number;
				myScale = Math.min(pj.pageWidth/savedPdfsArray[i].width, pj.pageHeight/savedPdfsArray[i].height);
				savedPdfsArray[i].scaleX = savedPdfsArray[i].scaleY = myScale;
				var printArea:Rectangle = new Rectangle(0, 0, pj.pageWidth/myScale, pj.pageHeight/myScale);

				pj.addPage(savedPdfsArray[i], printArea); //add page to print job
			}
			pj.send(); //send to printer
		}
		pj = null;
	}
	else { //if there are no pdfs in the queue...
		errorPopup.openMe("You do not have any pdfs saved in your queue.");
	}
}

This code worked perfectly on my PC, but occasionally gave me problems on an old iMac in the office. I had someone else test it on a newer Mac and it seemed fine, so I wrote it off as an outdated printer driver.

Then, when we sent it to the client, there were all sorts of problems. On some computers, it printed blank pages. On others, it scaled the pages incorrectly, so that the MovieClips didn’t fill the page. It happened on both Macs and PCs. After a lot of Googling around without much success, I found this thread on Kirupa forum. One of the responders suggested that the MovieClips must be added to the stage or they won’t print. I gave this a try and IT WORKED! It fixed all of the weird, unpredictable PrintJob behavior in my application. I quickly updated my code and the client was happy:

//-prints all saved pdfs:
public function printAllPages():void {
	if (savedPdfsArray.length > 0) { //if there are pdfs to print...
		var pj:PrintJob = new PrintJob(); //create printjob
		if(pj.start()) {
			for (var i:int=0; i < savedPdfsArray.length; i++) {

            			//========== printjob bug fix - prevent blank pages: ==========
                		savedPdfsArray[i].x = 2000; //keep it hidden to the side of the stage
                		stage.addChild(savedPdfsArray[i]); //add to stage - prevents blank pages
                		//=============================================================

				//scale it to fill the page (portrait orientation):
				var myScale:Number;
				myScale = Math.min(pj.pageWidth/savedPdfsArray[i].width, pj.pageHeight/savedPdfsArray[i].height);
				savedPdfsArray[i].scaleX = savedPdfsArray[i].scaleY = myScale;
				var printArea:Rectangle = new Rectangle(0, 0, pj.pageWidth/myScale, pj.pageHeight/myScale);

				pj.addPage(savedPdfsArray[i], printArea); //add page to print job
			}
			pj.send(); //send to printer
		}

        	//========== printjob bug fix - prevent blank pages: ==========
        	for (var j:int=0; j < savedPdfsArray.length; j++) {
        	    stage.removeChild(savedPdfsArray[j]); //don't forget to remove them from the stage
        	}
        	//=============================================================

		pj = null;
	}
	else { //if there are no pdfs in the queue...
		errorPopup.openMe("You do not have any pdfs saved in your queue.");
	}
}

I hope this post saves someone else a lot of hair-pulling. Thank you “orangehaze” and Kirupa forum!

Papervision3D: Max3DS vs. Collada performance

July 13th, 2009

Papervision3D is an incredible open-source library for building 3D applications in Flash. You can create basic 3D objects and animate them with Actionscript, but if you really want to create something cool, you need to import a 3D model made with a program like 3D Studio Max, Maya, or Blender.

Several different 3D file formats can be imported into Papervision - the most popular format seems to be Collada (*.DAE), which is an XML format. However, I recently noticed that *.3DS files could also be imported to Papervision using the Max3DS class. 3DS is an old file format for 3D Studio Max, but many 3D programs will still export this format. Since it is a binary format, it creates much smaller files than Collada, which can significantly decrease model load times, but I was curious if there was any noticeable performance difference once the models were imported. In theory, the models should perform identically once they are imported.  So I created a test to see if this was true.

I created a quick model of a 57 Chevy station wagon in 3D Studio Max 9 for my test. The finished model has 1833 vertices and 1802 faces. Please note that I’m a noob when it comes to 3D modeling and I know that this model has some issues, but I really only cared that it imported correctly to PV3D. Anyway, I exported the model to both Collada and 3DS formats. The exported file stats are:

wagon5.3DS = 103 KB
wagon5.DAE = 488 KB

I then created 2 Flash files that are identical except for the type of model that is imported for rendering. Click one of the images below to view the results in Papervision.

papervision_collada_test papervision_max3ds_test

 

As you can see, they look essentially identical. Each SWF loads the model 5 times and renders it to the screen with a FlatShadeMaterial. How did they perform? Well, that depends…

When I first created these SWFs, I tested them on my work computer. It’s a pretty fast Dell desktop PC with lots of RAM running Windows Vista and Flash Player 10. The 3DS format performed noticeably better on my work machine. The frame rates were similar for both models, but the memory usage was much lower than the Collada format. The 3DS model rotation also looked smoother than the Collada model and the 3DS file had much quicker file load time. It looked like a clear winner.

Then I spent some time cleaning up the files on my laptop at home and the performance tests didn’t show the same results. My laptop is a Lenovo PC that isn’t as fast as my work computer and I run Windows XP with Flash Player 9. While testing at home, the 3DS file still had lower memory usage and the animations both looked the same. The frame rate difference was still marginal.

So, which one performs better? It may be  dependent on the machine, the operating system, and the version of Flash you are running. I still give the 3DS format the advantage for it’s lower file size and memory usage. It may be a good idea to build a toggle into your Papervision project that allows you to import either format and do a lot of testing as you go before deciding on one format or the other. As always, keep your target audience (and their computers) in mind.

Feel free to use the Flash and Actionscript files to run your own tests or simply steal the code for your project. The methods that I used for loading and initializing the models may save you some time and frustration when dealing with the quirks of both the Collada and Max3DS parsers in Papervision. For example, the Max3DS parser would not allow me to initialize the model with a FlatShadeMaterial, so I had to use a ColorMaterial. But, once it was loaded, it would allow me to replace the ColorMaterial with a FlatShadeMaterial.

Please do not use the 3D model I have provided for production work. I have provided the model for you to use in these experiments, but it is copyrighted and I would really appreciate you not using it for anything else.

As always, you can download the source files here.

CSS sticky footer without javascript

July 3rd, 2009

Occasionally, a website design requires a “sticky footer” - that’s a footer that sticks to the bottom of the browser window when the content is not long enough to fill the browser. If the content is longer than the browser height, it should push the footer down below the content. It’s an easy task with a table-based layout, but it can be surprisingly tricky to pull off with pure CSS layouts.

Just the other day, someone asked me how to do this and I couldn’t remember how it was done off the top of my head, so… voila! a blog post was born. I have created 2 samples of the CSS-based sticky footer that work correctly in all major browsers for footers with a fixed height. No javascript is needed to pull off this effect.

CSS sticky footer

Example 1 shows the most basic scenario. Resize your browser and notice how the footer sticks to the bottom of the window until it hits the bottom of the content area. It is then forced below the content, exactly as it should be.

Example 2 is a slightly fancier version that shows you how to get a sticky footer on a centered layout. Again, the layout is CSS-based and has been tested in Firefox (2 & 3), Safari, Internet Explorer (6, 7 & 8), and Google Chrome.

You can download the source files here. If you’re a CSS ninja, there are enough comments in the files for you to figure it out. Otherwise, I’ll explain some of the details here:

First, open the basic example HTML and look at the top of the code.  The first line of code establishes the HTML doctype. You MUST have a valid doctype for the sticky footer to work. I have only tested it with the transitional doctype used in the examples. It should work with other valid doctypes, but you should test it thoroughly if you need to use another doctype.

Next, look at the line that declares the “main_styles.css” stylesheet. This stylesheet contains all of the css for the page. The next 3 lines specify a second stylesheet that contains only CSS overrides for Internet Explorer 6 and 7. This is the cleanest and easiest way I have found to deal with IE problems and it doesn’t require any hacks in the main stylesheet.

The DIV structure is pretty self-explanatory, but note that the footer is not contained inside of the “wrapper” div. It must be outside of the wrapper for the sticky footer.

Next, open “main_styles.css” - the comments should explain everything you need to know to make the sticky footer work. Note that the wrapper div has a min-height declaration. This is why we need a second stylesheet - IE 6 and 7 do not observe min-height correctly. If you open the ie_styles.css” file, you will see that the only style in it sets the height of the wrapper div. IE 6 and 7 treat height as if it was min-height, forcing the footer below the content area. IE 8 appears to follow the standards when it comes to min-height, so no override is needed for it.

Hopefully, the source files will provide you with templates that you can use to start building pages with a sticky footer. Good Luck!

Top