How do I refer to class linkage dynamically? - actionscript-3

I have 15 sounds called "Sound1", "Sound2", etc set in linkage properties. I want to dynamically refer to them in a loop. For example, instead of
currentMusic = new Sound2();
How can I do something like
currentMusic = new ("Sound" + i)();
Or what would be a better way of doing this?

Store the generated class name to a variable, retrieve the class from the application domain and instantiate the new object. Beware with exceptions!
var soundClassName:String = "Sound" + i;
var soundClass:Class;
var sound:Sound;
try {
soundClass = getDefinitionByName(soundClassName) as Class;
sound = new soundClass();
} catch (re:ReferenceError) {
trace("Class '" + soundClassName + "' not found");
} catch (te:TypeError) {
trace("Unable to instantiate the sound object");
}

Use the same procedure as you would to load a movie clip or font at runtime.
Assuming you have sounds in your library exported as "sound_0", "sound_1", ..., "sound_9", etc:
for(var i:uint = 0; i < 10; i++)
{
var soundClass:Class = getDefinitionByName("sound_" + i.toString()) as Class;
var sound:Sound = new soundClass();
}

Related

Duplicating/Cloning Vector.<MovieClip>

How do you duplicate the content of the Vector.?
private var slotMC:MovieClip;
private var slotV1:Vector.<MovieClip> = new Vector.<MovieClip>();
private var slotV2:Vector.<MovieClip>;
private var slotV3:Vector.<MovieClip>;
public function InputSlot():void {
registerClassAlias("MovieClip", MovieClip);
for (var i:int = 1; i < typeAmount + 1; i++) {
SlotClass = Main.queue.getLoader('main_uiMC').getClass('slot0' + i) as Class;
slotMC = new SlotClass();
slotMC.name = "slot" + i;
//push to vector before randomly add to stage
slotV1.push(slotMC);
}
slotV2 = clone(slotV1);
trace('slotv2', slotV2);
}
private function clone(source:Vector.<MovieClip>):Vector.<MovieClip> {
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return myBA.readObject() as Vector.<MovieClip>;
}
It returns null for the slotV2.
Or in this case would a multidimensional Vector would be a better way?
I'm not sure what other info I would add into the MC in the future though.
You cannot deep-clone a vector of DisplayObjects of any kind, unless the objects' classes implement IExternalizable. This is because deeply cloning a DisplayObject requires cloning all its references including the entire children list and stage, and you cannot make another stage. Thus, you'd better create your "deep clone" by creating more instances of SlotClass in the cycle and stuffing them into corresponding vectors.
for (var i:int = 1; i < typeAmount + 1; i++) {
SlotClass = Main.queue.getLoader('main_uiMC').getClass('slot0' + i) as Class;
slotMC = new SlotClass();
// slotMC.name = "slot" + i;
// drop name setting, you'd better use position in vector to refer to the clip
//push to vector before randomly add to stage
slotV1.push(slotMC);
slotMC = new SlotClass();
slotV2.push(slotMC);
slotMC = new SlotClass();
slotV3.push(slotMC);
}
Don't forget to initialize the vectors prior to running the cycle.
As a side point, yes it might be better to use a multidimensional vector, considering why do you need these several vectors.

If the class name is known as a string, is it possible to construct an instance of that class?

If the class name is known as a string, is it possible to construct an instance of that class? Example:
var className:String = "MyClass";
var obj:* = new getClass(className)();
It is possible. You have to use getDefinitionByName(name:String):Object
Make shure that the class you are referencing is available in the swf file
var ClassReference:Class = getDefinitionByName("flash.display.Sprite") as Class;
var instance:Object = new ClassReference();
While the given answers are correct, this might not work if you are using application domains.
To take into account application domain, use the following method:
public static function forName(name:String, applicationDomain:ApplicationDomain = null):Class {
applicationDomain ||= ApplicationDomain.currentDomain;
var result:Class;
while (!applicationDomain.hasDefinition(name)) {
if (applicationDomain.parentDomain) {
applicationDomain = applicationDomain.parentDomain;
} else {
break;
}
}
try {
result = applicationDomain.getDefinition(name) as Class;
} catch (e:ReferenceError) {
throw new ClassNotFoundError("A class with the name '" + name + "' could not be found.");
}
return result;
}
var myClass:Class = forName(className);
var instance:Object = new myClass();
This method and many other utilities are available in the AS3Commons Lang library
Try this:
import flash.utils.getDefinitionByName;
var className:String = "MyClass";
var obj:Object = new (getDefinitionByName(className) as Class)();
Yes it is!
var myClass:Class = getDefinitionByName(className) as Class;
var instance:Object = new myClass();

Problem in displaying thumbnail images

I have created 3 movieClips and I added into another movieClip container named as mc.
for(i = 0;i<3;i++)
{
imgBox = new box();
mc.addChild(imgBox);
imgBox.name = "box" + i;
}
xml image names are image1.jpg, image2.jpg and imag3.jpg
and have stored in thumbArray.
for(i = 0;i<thumbArray.length; i++)
{
thumbLoader = new Loader();
thumbLoader.load(new URLRequest(thumbArray[i]));
thumbLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onThumbLoaded);
}
and I have loaded.
int count = -1;
function onThumbLoaded (e:Event):void
{
count++;
var img:Bitmap = Bitmap(e.target.content);
MovieClip(mc.getChildByName("box" +count)).addChild(img);
}
finally everthing loaded, but the display seaquence like this image3, image1 and image2. I want to have the seaquence image1, image2 and imag3.
What is the problem?
The problem is that you have no control over the speed at which each load occurs, so there is no guarantee that the first loader will complete first etc. Your best approach will probably to name the loaders, and use the name to reference the target. Something like this:
for(i = 0;i<thumbArray.length; i++)
{
thumbLoader = new Loader();
//assign a name that matches the target box name, with "Loader" on the end
thumbLoader.name = "box" + i + "Loader";
thumbLoader.load(new URLRequest(thumbArray[i]));
thumbLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onThumbLoaded);
}
function onThumbLoaded (e:Event):void
{
var img:Bitmap = Bitmap(e.target.content);
//slice the "Loader" off the name
var loadName:String = e.target.name.slice(0,-6);
MovieClip(mc.getChildByName(loadName)).addChild(img);
}

AS3, for loop create button inside movieclip and how to call it?

Here is my full code
import fl.controls.*;
var test:MovieClip = new MovieClip();
var btn:Button;
for(var i:int = 1; i<=7; i++){
btn = new Button();
btn.name = "btn" + i;
btn.x = i * 100;
test.addChild(btn);
btn.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent) { nothing(i); });
}
addChild(test);
function nothing(bla:int):void {
trace("You clicked " + bla);
}
Result:
You clicked 8
You clicked 8
You clicked 8...
Is there anyway, such that, I can use for loop to create different name button, and add it to an event listener?
Your problem is the function(evt:MouseEvent){} closure (JavaScript info applies to ActionScript too, as they're both ECMAScript). Here's what you can do:
function makeClickListener(i:int) {
return function(evt:MouseEvent) {
nothing(i);
};
}
...
for(...) {
...
btn.addEventListener(MouseEvent.CLICK, makeClickListener(i));
}
in your example your i is not doing what you think it's doing. You've declared it as a global variable and passing into the function as you're doing is meaningless. In the end your buttons are simply reporting the current value of i (which after the loop is always 8).
I like the other solution offered, but here's a more object oriented solution, which may be of some use depending on what you're doing with your button (or other objects in the future).
public class MyButton extends Button{
public var myIndex:Number;
}
Now you use MyButton instead of Button and in your loop throw in
btn.myIndex = i;
then attach generic event handler
btn.addEventListener(MouseEvent.CLICK, myHandler);
which would look like this:
function myHandler(evt:MouseEvent){
trace(evt.target.myIndex);
}
See... the target of the event will always be the object to which you attached the event. It is to that object that you should attach whatever values you wish it to retain. Personally I prefer this approach because then the information is with the object (and can be used by other elements that might need it) rather than in the handler and only in the handler.
var test:MovieClip = new MovieClip();
var btn:Button;
for(var i:int = 1; i<=7; i++){
btn = new Button();
btn.name = i.toString();
btn.x = i * 100;
test.addChild(btn);
btn.addEventListener(MouseEvent.CLICK, nothing);
}
function nothing(event:MouseEvent):void {
//trace("You clicked " + int( event.currentTarget.name ) );
trace("You clicked " + event.currentTarget.name );
}

trouble separating loader array and array for grid of images

My problem is that I can't figure out how to decouple the loader from this loadNewRow function. Ideally, I'd like to figure out how to load all the swf's give them dynamic names... like thmb1loaded, thmb2loaded, thmb3loaded.. push into an a ("loaded") array and then have that be used to build the grid.
Right now, its loading them every time the loadnewrow function fires. I'm sure its a simple matter of iterating a .name within a contentloaderinfo .onComplete when each swf is loaded... anyhelp would be appreciated this has been driving me crazy last couple of days..
var filePaths:Array=["thmb1.swf","thmb2.swf","thmb3.swf","thmb4.swf", "thmb5.swf", "thmb6.swf",
"thmb7.swf","thmb8.swf", "thmb9.swf", "thmb10.swf", "thmb11.swf" ];
function loadImages3(event:MouseEvent):void {
TweenLite.to(pic, 8, {alpha:0, ease:Expo.easeInOut});// dim image container
trace("loadImages3 fired ---------------------------------------------------------")
unloadImages3resize(); loadImages3Flag=true;
var randomScale:Number = .3 + Math.random();trace("in loadimages3 function randomScale is now.. " + randomScale);
var stage_w=stage.stageWidth;trace("in loadimages3 function stage_w is = " + stage_w);
var stage_h=stage.stageHeight;trace("in loadimages3 function stage_h is = " + stage_h);
var thumbWidth:Number=240;var thumbHeight:Number=155;//var randomScale:Number=.45;
var scaleThumbs:Number=randomScale;
var finalRandomScaledWidth:Number=thumbWidth*scaleThumbs;
var finalRandomScaledHeight:Number=thumbHeight*scaleThumbs;
var clipsFitX=Math.floor(stage_w/finalRandomScaledWidth);
trace("in loadimages3 function finalRandomScaledWidth is = " + finalRandomScaledWidth);
var clipsFitY=Math.floor(stage_h/finalRandomScaledHeight);
trace("in loadimages3 function finalRandomScaledHeight is = " + finalRandomScaledHeight);
var clipsFitXTotal:Number = clipsFitX+1; // add extra one on the horizontal
var clipsFitYTotal:Number = clipsFitY+2; // add extra two on the vertical
trace ("ClipsFitXtotal) = " +clipsFitXTotal, "(clipsFitYTotal) = " + clipsFitYTotal);
loadnewRow(clipsFitXTotal, clipsFitYTotal,randomScale,finalRandomScaledHeight, finalRandomScaledWidth );
}
function loadnewRow(clipsFitXTotal:Number, clipsFitYTotal:Number, randomScale:Number,
finalRandomScaledHeight:Number, finalRandomScaledWidth:Number):void {
trace("loadnewRow fired ---------------------------------------------------------");
trace("randomScale out of loop but in loadnewRow function loop =" + randomScale);
for (var z:Number =0; z < clipsFitYTotal; z++) {
for (var b:Number = 0; b < clipsFitXTotal; b++) {
var ldr:Loader = new Loader();
var randomSelection = Math.floor( (Math.random()*filePaths.length) );
ldr.load(new URLRequest (filePaths[randomSelection]));
ldr.contentLoaderInfo.addEventListener(Event.INIT, initHandler);
ldr.scaleX=ldr.scaleY=randomScale;//trace("randomScale in the loop is =" + randomScale);
ldr.y=finalRandomScaledHeight*z;//trace("finalRandomScaledHeight*z =" + ldr.y);
ldr.x=finalRandomScaledWidth*b;//trace("finalRandomScaledWidth*b =" + ldr.x);
ldr.alpha=.8;
//trace("[ldr] = number of children = " + ldr.numChildren);
//trace("[THIS] = number of children = " + this.numChildren);
//trace("[THIS] name is = " +this.name);
ldr.cacheAsBitmap = true;
//thumbholder.addChild(ldr);
addChildAt(ldr,1);
filePaths.splice(randomSelection, 1);
if (filePaths.length==0) {
filePaths.push("thmb1.swf","thmb2.swf","thmb3.swf","thmb4.swf","thmb5.swf",
"thmb6.swf","thmb7.swf","thmb8.swf", "thmb9.swf",
"thmb10.swf", "thmb11.swf");
}
}
}
}
I suggest you to use some library/framework to simplifiy things. As I use splinklibrary here is some exemplary code to solve your problem with the aid of splinklibrary:
public function Test() {
var filePaths : Array = ["thmb1.swf","thmb2.swf","thmb3.swf"];
var q : IQ = new Q();
q.register(QCompleteEvent.COMPLETE, onComplete);
for each (var path : String in filePaths) {
q.add(new QLoader(new URLRequest(path)));
}
q.start();
}
private function onComplete(e : QCompleteEvent) : void {
var displays : Array = new Array();
var q : IQ = IQ(e.getSource());
for each (var loader : QLoader in q.getItems()) {
displays.push(addChild(loader.getContent()));
}
arrange(displays);
show(displays);
}
private function arrange(displays : Array) : void {
// put in your code to arrange the loaded displayobjects
}
private function show(displays : Array) : void {
// put in your code to tween the loaded displayobjects
}
Of course you can reach your goal without a library too, but the point is that a library often helps to keep your code simple and concise.