Update on Embedding Assets in AS3

A few weeks ago, I posted some info on embedding SWF-based assets in AS3.

One question that came up in the comments was how to embed a symbol from a Flash 9 SWF that has a custom class associated with it, and use it as an instance of that custom class. In other words, you create a movie clip in the Flash 9 IDE, give it a class of “Star”, and you have an actual class written, called “Star” that has some functionality that is really cool. Now, you embed that star symbol in your AS3 application, but you can only type it as Sprite or MovieClip. How to get it to be a “Star”?

I finally got to dig in to this a bit more, and sadly, I don’t think there is a way to do this. When you embed a Sprite asset, it comes in as an instance of SpriteAsset, which “is a subclass of the Flash Player’s Sprite class which represents vector graphic images that you embed in a Flex application.”

http://livedocs.macromedia.com/flex/2/langref/mx/core/SpriteAsset.html

Note: “represents vector graphic images”.

Even when I had a real Star class, using describeType, getQualifiedClassName, and getQualifiedSuperclassName show no sign of that class coming though with the embedded object. Any code that should execute from that class does not execute.

In fact, even if you put some code on the timeline of that symbol, say drawing some random lines. It executes fine when compiled in the IDE, but that code will not run in the embedded symbol, pointing up the fact that ALL that is embedded is the vector graphic info.

So, unless I’m missing something, that’s that. The one thing that is kind of odd though, is that if you publish a Flash 8 SWF and then embed an asset from it that has some code in it, it gives you a warning that the AS2 code will be ignored. If all code is discarded anyway, I’m not sure why that warning even exists.

This entry was posted in Flash. Bookmark the permalink.

13 Responses to Update on Embedding Assets in AS3

  1. spender says:

    I ran into a similar problem, and decided to use a Loader to load the resource swf into the current appdom at runtime and to use ApplicationDomain.currentDomain.getDefinition to fetch the class. I assume this grabs resource AND code (but haven’t tested). Alternatively, the include-library compiler option might provide a means of doing this, but I don’t have a set-up here at work to test.

  2. spender says:

    … but with include-library, you’d still need to use getDefinition

  3. kp says:

    yeah. loading an asset at run time does include the code.

  4. Julio Garcia says:

    The way I am doing it is as follows:
    First of all I have to note that I am loading my assets at runtime which in my application gives me more flexibility:
    I have an AssetLoader Class impelemented as a Singelton that extends EventDispatcher with a:
    private var ldr:Loader;

    and removing listeners, dispatchers and other utilities the relevant functions are:

    //Loads the library and sets the application domain
    public function loadLibrary($url:String):void{
    swfLib = $url;
    var req:URLRequest = new URLRequest($url);
    var context:LoaderContext = new LoaderContext();
    context.applicationDomain = ApplicationDomain.currentDomain;
    ldr.load(req, context);
    }

    //Gets the corresponding class name
    public function getClass(className:String):Class {
    try {
    return ldr.contentLoaderInfo.applicationDomain.getDefinition(className) as Class;
    }
    catch(e:Error) {
    throw new IllegalOperationError(className + ” definition not found in ” + swfLib);
    }
    return null;
    }

    So when I want to instance a class, for example “Triangle” from the asset library that I generated in Flash 9 I just call the following:

    var Triangle:Class = AssetLoader.getInstance().getClass(“Triangle”);
    wing = new Triangle();

    The important part is seting the LoaderContext to ApplicationDomain.currentDomain which is a really cool concept.

  5. Ray Greenwell says:

    I know the answer!

    Let’s say you have a custom class, Boogie extends MovieClip, and its all compiled up into a nice flash 9 SWF called Boogie.swf.

    In your flex app, use:

    [Embed(source=”Boogie.swf”, mimeType=”application/octet-stream”)]
    private var BOOGIE_CLASS :Class;

    Now, BOOGIE_CLASS will be a class that extends ByteArray. (Not MovieClip!)

    To reconstitute a Boogie instance, use:

    var boogieBytes :ByteArray = (new BOOGIE_CLASS() as ByteArray);
    var l :Loader = new Loader();
    l.loadBytes(boogieBytes, new LoaderContext(false, ApplicationDomain.currentDomain));
    // omitted for brevity: listening to the loader, waiting for COMPLETE
    var something :DisplayObject = l.content;
    trace(“something is a ” + something); // should output that it’s a [Object Boogie]

  6. Mark Walters says:

    In case anyone comes stumbling upon this post again like I did, I wanted to offer up the solution to associating a custom class with an embedded symbol.

    Instead of doing something like this:

    [Embed(source="/library.swf", symbol="customButton")]
    private var CustomButton:Class;

    var customButton:SimpleButton = new CustomButton();

    Do this instead:

    [Embed(source="/library.swf", symbol="customButton")]
    public class CustomButton extends SimpleButton

    Add the embed tag above the class declaration that you want the embedded symbol to be associated with instead of above a variable that you want associated with the class.

  7. kp says:

    Interesting. I’ll have to try that out Mark.

  8. PimpSleazy says:

    Well, my current project requires my my main swf to be able to communicate with loaded swfs and other swfs on the same system (in a web page). To do this, I am currently using LocalConnection to invoke methods by name. I had thought about implementing a type library scheme in which each swf would have a function that sends an array of custom function definition objects to the caller, but that isn’t really necessary for this project since all I really need to do is command an swf (via LocalConnection)to do this or that, and if it needs to return a value it does so via LocalConnection. However, I figure that if you REALLY want to be slick, declare some common interfaces in all swfs, and implement them as necessary. Then, when you get a class by using getDefinition(…), you can cast it to that interface and invoke methods that way. That is really the OOP way of doing things anyway. I’m probably gonna have to do a re-write and move on into interface-implementing classes.

  9. epologee says:

    Hey Keith. It’s about a year and a half after this article was posted. I’d be pretty interested in what your current preferred way of working is with the embedding issue. I’m running into a lot of casting issues with the Embed-statement on member variables. Mark Walters’ suggestion works a lot better in that division, but I don’t like the way the Embed-statements are scattered throughout the entire project like that.

    Would you like to share some insights about this?
    Cheers,
    Eric-Paul

  10. Stefan says:

    Has anyone run into the problem with casting a class loaded through a external swf to an interface that is defined in both the loaded and loading class?

    as in:

    AssetLoader.getInstance().getClass(”Triangle”);
    wing = new Triangle();

    var ims: IMyShapes = wing as IMyShapes

    ims is always null!!?

  11. Mike Myiers says:

    Mark, I tried your idea above and I get the folllowing error message while compiling:

    1131: Classes must not be nested.

    Here is the code:

    [Embed(source=”components/externalAsset.swf”)]
    public class ExtenalAsset extends MovieClip;

  12. Erwin Verdonk says:

    Mark Walters answer only works when you want the ActionScript code within a completely separate class from the embedded asset, because with his approach the ActionScript code is still not embedded. It is rather an extension of the symbol (class) you’re embedding (with no ActionScript code) and you add the code within the project that uses the asset. Not the typical approach if you ask me.

    I think Ray Greenwell (thanks a lot) has the correct answer. We got it working as follow:

    =========================

    [Embed(source="assets.swf", mimeType="application/octet-stream")]
    public var Assets:Class;
    public var assetLoader:Loader;

    public function init():void
    {
    assetLoader = new Loader();
    assetLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onAssetLoaderComplete);
    assetLoader.loadBytes(new Assets as ByteArray, new LoaderContext(false, ApplicationDomain.currentDomain));
    }

    public function onAssetLoaderComplete(event:Event):void
    {
    // Get symbol
    var Symbol:Class = getDefinitionByName("symbolName");

    // Instantiate symbol
    var symbolInstance:* = new Symbol();

    // Execute method within symbol
    symbolInstance.myMethod("arg1", "arg2");
    }

    =========================

  13. turbidity says:

    I’m getting some strange behavior from my embedded objects that I thought I’d share. In my library I’ve got symbols linked to Classes that embed themselves like so:
    (In the Flash CS3 IDE there is a symbol “Baddie” linked to the enemies.Baddie class)

    package enemies {
    [Embed(source='../../flas/library.swf', symbol='enemies.Baddie')]
    public class Baddie extends Enemy {
    ...

    I have another symbol in my library called Level and it contains multiple instances of the Baddie symbol.

    At runtime I parse the Level Symbol to find all of it’s children:

    public function parseLevel (container:DisplayObjectContainer) : void {
    var child:DisplayObject;
    for (var i:uint=0; i < container.numChildren; i++) {
    child = container.getChildAt(i);
    trace(child);
    if (child is Baddie) {
    enemyList.push(child);
    }
    }
    }

    This parses the Level symbol fine in the Flash CS3 IDE, but when I move over to FlashDevelop 3 RC2 and compile, the “Baddie” is now merely a MovieClip and will not be caught by the parseLevel() function. However, If I add a dummy Baddie object to the Level Class all instances of Baddie will be caught by if(child is Baddie)!

    in the Level Class:

    public var bad:Baddie;

Leave a Reply