Add multiple movieclips, not replacing the old ones - actionscript-3

So, in short, my problem is this. I am using a variable which is a movieclip loaded from an external swf. I want to "spawn" multiple instances of the movieclip that all react to the same code, so for example if I say var1.x = 100, they all are at 100x. But my problem is when I run addChild(var1) multiple times(I'm not actually typing in addChild(var1) over and over, I just have it set to add them at random times), the new child just replaces the old one, instead of making multiple movieclips. Should I do something like
var var1:MovieClip
var var2:MovieClip = new var1 ?(which doesnt work for me btw, gives me errors)
Oh, heres the code, and also, I am pretty new to as3 fyi, still don't even know how arrays work, which was my second guess to the problem.
var zombieExt:MovieClip;
var ldr2:Loader = new Loader();
ldr2.contentLoaderInfo.addEventListener(Event.COMPLETE, swfLoaded2);
ldr2.load(new URLRequest("ZombieSource.swf"));
function swfLoaded2(event:Event):void
{
zombieExt = MovieClip(ldr2.contentLoaderInfo.content);
ldr2.contentLoaderInfo.removeEventListener(Event.COMPLETE, swfLoaded2);
//zombieExt.addEventListener(Event.ENTER_FRAME, moveZombie)
zombieExt.addEventListener(Event.ENTER_FRAME,rotate2);
function rotate2 (event:Event)
{
var the2X:int = playerExt.x - zombieExt.x;
var the2Y:int = (playerExt.y - zombieExt.y) * 1;
var angle = Math.atan(the2Y/the2X)/(Math.PI/180);
if (the2X<0) {
angle += 180;
}
if (the2X>=0 && the2Y<0) {
angle += 360;
}
//angletext.text = angle;
zombieExt.rotation = (angle*1) + 90;
}
playerExt.addEventListener(Event.ENTER_FRAME,spawn1);
function spawn1 (event:Event)
{
if(playerExt.y < 417)
{
var someNum:Number = Math.round(Math.random()*20);
if(someNum == 20)
{
addChild(zombieExt)
zombieExt.x = Math.round(Math.random()*100)
zombieExt.y = Math.round(Math.random()*100)
}
}
}
}

addChild() does not create new instances. It is used to add an already created instance to the display list. If you call addChild() multiple times on the same instance then you are just readding itself.
Also each instance is unique, you can not globally change the x position of an instance by changing another one of them. What you would do is as Henry suggests and add each new instance of a MovieClip into an array, then whenever you change something you can loop through the array and apply the changes to each instance.
You can not go var2:MovieClip = new var1 either since var1 is an instance and not a class.

Here's a different method of receiving loaded MovieClips, which i use when i need many copies of the item.
in the swf you are loading, give the target movieclip a linkage name in the library, for this example i will use "foo"
private var loadedSwfClass:Class
private var newZombie:MovieClip;
private var zombieArray:Array = new Array();
function swfLoaded2(event:Event):void
{
loadedSwfClass = event.target.applicationDomain.getDefinition("foo");
for(var n:int = 0; n<100; n++){
newZombie = new loadedSwfClass()
zombieArray.push(newZombie);
addChild(newZombie);
}
}
as per this tutorial
http://darylteo.com/blog/2007/11/16/abstracting-assets-from-actionscript-in-as30-asset-libraries/
although the comments say that
var dClip:MovieClip = this;
var new_mc = new dClip.constructor();
this.addChild(new_mc);
will also work.

It sounds like you might be accessing the same instance some how in your code. It would be helpful to see your code to figure this one out.
If I wanted to load in one swf files and add a MovieClip multiple times I would place it in the library of that SWF file. And then instantiate it and store it into an object pool or a hash or some list.
// after the library were finished loading
var list:Array = [];
for(var i:int=0; i<10; i++) {
var myCreation:MySpecialThing = new MySpecialThing();
addChild(myCreation);
list.push(myCreation);
}
where my library would contain a linkage to the class MySpecialThing.

Calling addChild(var1) multiple times on the same parent doesn't have any effect (unless you have added another child to the same parent in between, in which case it will change the child index and bring var1 to the top). If you call it on different parents, it will just change the parent of var1, doesn't duplicate. Call addChild(new MovieClassName()) at random times instead to add new copies of it. Use an array as suggested here to access them later.

Wow, thanks there henry, just using an array did exactly what I needed, and made things alot simpler.

when you load in using a loader you only get 1 instance, however you can do some funky reflection to determine what class type the given loader.content is, and then instantiate them using that. For Example:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loader_completeHandler);
loader.load(new URLRequest("ZombieSource.swf"));
var classType:Class;
function loader_completeHandler(evt:Event):void
{
var loadInfo:LoaderInfo = (evt.target as LoaderInfo);
var loadedInstance:DisplayObject = loadInfo.content;
// getQualifiedClassName() is a top-level function, like trace()
var nameStr:String = getQualifiedClassName(loadedInstance);
if( loadInfo.applicationDomain.hasDefinition(nameStr) )
{
classType = loadInfo.applicationDomain.getDefinition(nameStr) as Class;
init();
}
else
{
//could not extract the class
}
}
function init():void
{
// to make a new instance of the ZombieMovie object, you create it
// directly from the classType variable
var i:int = 0;
while(i < 10)
{
var newZombie:DisplayObject = new classType();
// your code here
newZombie.x = stage.stageWidth * Math.random();
newZombie.x = stage.stageHeight * Math.random();
i++;
}
}
Any problems let me know, hope this helps.

Related

Can anyone help me change this code from AS2 to AS3

This will be a simple clock. I've been using AS2 since I learned Flash in early 2000s. It's time to move on.
for (a=1; a<60; a++) {
duplicateMovieClip("dot0", "dot"+a, 10+a);
_root["dot"+a]._rotation = a*6;
_root["dot"+a].gotoAndStop(1);
}
Adobe published ActionScript 2.0 Migration that helped me tremendously back in the day.
Some specifics related to your code:
Properties aren't prefixed with underscores, ex _rotation is now rotation, and _root is now root.
root is not global, it is a property of display objects, and it is null if the display object is not in the display list.
duplicateMovieClip does not exist anymore. You should export your symbol to a class and use new operator and addChild() to create an instance and add it to the display, ex:
var dot:Dot = new Dot();
addChild(dot);
Display objects created in code are not automatically given a name and assigned to a property on its parent when added to the display. You can set the name and use getChildByName on its parent. Example:
var dot:Dot = new Dot();
dot.name = "dot" + i;
addChild(dot);
var n:int = 10;
var dot10:Dot = getChildByName("dot" + n) as Dot;
But this is a bit cumbersome, so in most cases it makes more sense to just store your display objects in your own array and reference them by index:
var dots:Array = [];
var dot:Dot = new Dot();
addChild(dot);
dots.push(dot);
var firstDot:Dot = dots[0];
That should get you started.
Thanks to help here and elsewhere, this is what works for me:
var i:Number = 1;
var dots:Array = [];
for (i=0; i<60; i++) {
var dot:Dot = new Dot;
addChild(dot);
dots.push(dot);
dot.x=683;
dot.y=436;
dot.rotation = i*6;
dot.gotoAndStop(1);
}
The clock I'm making don't have hands but the dots change color for the hours minutes and seconds. Thanks everybody who helped

AS3 - Using a For Loop to Update Multiple Points and Their Values in an Array

I'm a bit new with AS3 (but not really with coding) so please forgive my ignorance. I'm creating a small function that will be called by a Main Function to update the position of 52 Pointers that have the x and y position of multiple point objects (empty movie clips). It will also then update two global arrays with those values (one array for the x and one for the y).
The problem is, as there are 52 of them, and they will probably grow in quantity, I'd like to be able to use a FOR function to do it, but I can't seem to be able to figure it out.
I get this error: Access of undefined property _point.
Here is a piece of the code that dream about:
function happyFunc():void
{
var avpointers:int = 52;
var vpointx:Array = new Array();
var vpointy:Array = new Array();
for (aa=0; aa<vpointers; aa++)
{
vpointx[aa] = _point[aa].x;
vpointy[aa] = _point[aa].y;
}
}
And this is the code that I'm stuck with...
function reallySadFunc():void
{
_point1 = localToGlobal(new Point(point1.x,point1.y));
//...
_point52 = localToGlobal(new Point(point52.x,point1.y));
vpointx[0] = _point1.x;
vpointx[1] = _point2.x;
//...
//oh god there are 104 lines of this why do I have to suffer
}
Thank you!
If I read your question correctly, I think this is what you need:
public static const CLIP_COUNT:int = 52;
// ...
private function happyFunc(parentClip:DisplayObjectContainer):void
{
var vpointx:Vector.<Number> = new Vector.<Number>(CLIP_COUNT, true);
var vpointy:Vector.<Number> = new Vector.<Number>(CLIP_COUNT, true);
var clipPoint:Point = new Point ();
var globalPoint:Point;
for (var i:int = 0; i < CLIP_COUNT; i++)
{
var childClip:DisplayObject = parentClip.getChildByName("point" +
(i + 1).toString());
clipPoint.x = childClip.x;
clipPoint.y = childClip.y;
globalPoint = parentClip.localToGlobal(clipPoint);
vpointx[i] = globalPoint.x;
vpointy[i] = globalPoint.y;
}
// do something with vpointx and vpointy - they're local variables
// and will go out of scope unless you declare them as class members
// or somehow return them from this function.
}
This function works by taking the parent display object (the one that contains the 52 movie clips - this could be the Stage) and iterates through them by getting each movie clip by name. The code assumes that your movie clips are called point1, point2, ..., point52.
To speed up the local-to-global coordinate conversion, two Point objects are created and then reused during the for loop to avoid creating many temporary Point instances.
Instead of using Array, use Vector.<Number> - this class has better performance than Array does.

Error #2007: Parameter child must be non-null. When Child is added within for loop

I am having this error, not sure why it appears, i believe its something with scope problem. Because i have added Child already. I can't even remove Child, the child is added within the loop, and i am not sure how to manipulate the childs that were added within the loop from the outside(after the loop has ended).
I am trying to add the child from within the loop to a viewport, but i am demonstrating the problem with removeChild to reduce the complication. Because i am trying to locate the reason of why it is giving me this error, and trying to learn what i should do.
Thanks for your time!
for (var j:int = 5; j < somedata.length; j++)
{
if(somedata[j]){
var myLoader:Loader = new Loader();
var image:Bitmap;
var url:URLRequest = new URLRequest("http://www.rentaid.info/rent/"+somedata[j]);
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
myLoader.load(url);
function onImageLoaded(e:Event):void {
image = new Bitmap(e.target.content.bitmapData);
var mw:Number = bf.width*2;
var mh:Number = bf.height*1.2;
image.y = bf4.y+25;
/* if you set width and height image same with the stage use this */
image.width = mw;
image.height = mh;
var _contentHolder: Sprite;
addChild(_contentHolder);
_contentHolder.addChild(image);
}
}
removeChild(_contentHolder);
}
Edit:
function LoadImages() : void {
for (var j:int = 5; j < somedata.length; j++) {
var image:Bitmap;
myLoader = new Loader;
var urlRequest : URLRequest = new URLRequestfor ("http://www.rentaid.info/rent/"+somedata[j])
myLoader.load(urlRequest);
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, LoadComplete);
}
}
function LoadComplete(event : Event) {
_contentHolder.addChild(image);
}
Or something like this?
var loadedArray:Array = new Array();
var counter:int=0;
function loadImage():void{
var loader:Loader = new Loader();
var image:Bitmap;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
loader.load(new URLRequest("http://www.rentaid.info/rent/"+somedata[counter]));
function onImageLoaded(e:Event):void {
image = new Bitmap(e.target.content.bitmapData);
loadedArray.push(e.target.content);
if(counter == somedata.length-1){
for(var i:uint = 5; i < somedata.length; i++){
image[i].x = 0 + i * 100;
addChild(_contentHolder);
_contentHolder.addChild(image[i]);
currentY += _contentHolder.height + 10;
}
}
else{
counter++;
loadImage();
You don't define _contentHolder. You also do not, currently, addChild(_contentHolder). This will be why the removeChild line is throwing an error.
Also, you do not need to addChild(image) and then add image as a child of _contentHolder. One or the other will do. In fact a DisplayObject can only be in ONE place on the display 'tree'. If you want image as a child of _contentHolder only _contentHolder.addChild(image) is necessary.
If you want all of your image instances added to the SAME _contentHolder, it would be best to declare and instantiate _contentHolder OUTSIDE the for loop; Each foor loop iteration will then be adding your images to the same Sprite object. When you remove _contentHolder, the child images will also disappear (although they will still be children of _contentHolder).
Ideally, you should not have more than one Loader operating asynchronously, as you do in your for loop. Instead of using a for loop, create a variable to track which index value you are up to and, when each image loads, increment that value and use it to check if all images are loaded. If not, load the next. This will require the Loader section of your code to be in it's own function, which gets called initially AND from onImageLoaded, if another image needs to be loaded.
The variable that tracks which index you are up to will, likewise, need to be set initially and incremented when each image loads.
In response to your edited question:
Your first new example of code does not attempt to load the NEXT image once the current image is loaded. Also, just as a tip: Function names are not normally written starting with capital letters; This prevents confusion with Class definitions.
Also your first new example of code does not call the 'LoadImages' function to initially load the first image.
Your second example looks like it should work to me. Have you actually tried this version?
The only thing I'd suggest is defining your Loader with the other variables and removing the Event.COMPLETE listener in the 'loaded' function. When adding the event listener you could also add a:
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
listener and trace out the progress of each image.
I'd also recommend adding trace statements throughout you code to see where it's getting stuck.

AS3 reference movieclip by .name property

Yay, another simple noobie as3 question.
How can I reference a movieclip by its ".name" ?
I tried searching for a solution, but I couldn't find anything. Basically I have a set of movieclips added to the stage using a loop, so the way I found to diferentiate them was by giving them a .name of "something" + the Loop's "i". So now they are named something like "something1", "something2", "something3", and so on.
Now, I need to send some to a specific frame. Usually I would do something like:
something1.gotoAndStop(2);
But "something1" isnt the instance name, just the ".name". I cant find a way to reference it.
you want to use getChildByName("name") more info
import flash.display.MovieClip;
// create boxes
for(var i:int = 0 ; i < 4; i++){
var box:MovieClip = new myBox(); // myBox is a symbol in the library (export for actionscript is checked and class name is myBox
box.name = "box_" + i;
box.x = i * 100;
this.addChild(box);
}
// call one of the boxes
var targetBox:MovieClip = this.getChildByName("box_2") as MovieClip;
targetBox.gotoAndStop(2);
Accessing things by name is very prone to errors. It's not a good habit to get into if you're a newbie. I think a safer way to do this would be to store references to the things you're creating in the loop, for example in an array, and reference them by their indexes.
Example:
var boxes:Array = [];
const NUM_BOXES:int = 4;
const SPACING:int = 100;
// create boxes
for(var i:int = 0 ; i < NUM_BOXES:; i++){
var box:MovieClip = new MovieClip();
// You can still do this, but only as a label, don't rely on it for finding the box later!
box.name = "box_" + i;
box.x = i * SPACING;
addChild(box);
// store the box for lookup later.
boxes.push(box); // or boxes[i] = box;
}
// talk to the third box
const RESET_FRAME:int = 2;
var targetBox:MovieClip = boxes[2] as MovieClip;
targetBox.gotoAndStop(RESET_FRAME);
Note, I've also replaced many of the loose numbers with constants and vars which will also help your compiler notice errors.
You can use the parent to get the child by name. If the parent is the stage:
var something1:MovieClip = stage.getChildByName("something1");
something1.gotoAndStop(2);

Unloading a Loader with actionscript 3

Hello and thank you very much for looking at this. I've spent too many hours struggling.
The code below loads a slideshow of four images, along with thumbnails for those images. It works fine.
I've added a button called "invis_button", that when pressed is supposed to remove the 3 loaders that make up the slideshow, using the removeChild command for each loader.
But this is the problem, there are 3 loaders involved in the slide-show. The removeChild command successfully removes one of the loaders (named "loader3"), but not the other two ("container3", and "thumbLoader3"). It returns an error stating "access of undefined property thumbLoader3" or "Container3".
Can someone tell me why this is ? Or better still, how to make that button (invis_button) unload the entire slide-show.
var images3:Array = ["ad_bona1.jpg", "ad_bona2.jpg", "ad_darkhawk1.jpg", "ad_darkhawk2.jpg"];
var thumbX3:Number = -375;
var thumbY3:Number = 220;
var loader3:Loader = new Loader();
loader3.load(new URLRequest("assets/ad_bona1.jpg"));
addChild(loader3);
loader3.alpha = 0;
loadThumbs3();
function loadThumbs3():void
{
var thumbLoader3:Loader;
var container3:Sprite = new Sprite();
addChild(container3);
container3.buttonMode = true;
for(var i3:uint = 0; i3 < images3.length; i3++)
{
thumbLoader3 = new Loader();
thumbLoader3.load(new URLRequest("assets/thumbs/" + images3[i3]));
thumbLoader3.x = thumbX3;
thumbLoader3.y = thumbY3;
thumbX3 += 85;
container3.addChild(thumbLoader3);
thumbLoader3.addEventListener(MouseEvent.CLICK, thumbClicked3);
}
}
function thumbClicked3(event:MouseEvent):void
{
var path3:String = event.currentTarget.contentLoaderInfo.url;
path3 = path3.substr(path3.lastIndexOf("/") + 1);
loader3.load(new URLRequest("assets/" + path3));
}
///PROBLEM BELOW, button removes only "loader3" and not the other two for some reason
invis_button.addEventListener(MouseEvent.CLICK, unload_loaders);
function unload_loaders(event:MouseEvent):void{
removeChild(loader3);
removeChild(thumbLoader3);
removeChild(container3);
}
Not sure if this is the entire reason behind what you're observing... but for starters, "thumbloader3" and "container3" are scoped locally to the loadThumbs3() method, which means once you finish executing the function, Flash's handles to those objects are lost (not to mention being in an entirely different scope)... try creating class-level properties for those two. Once that's done you should be able to successfully remove them from the stage later on.
I hope that you're also properly destroying your objects, and for the sake of brevity you just chose to omit that code above.
I've edited the code you had above & put the properties into the proper scope. (the multiple copies of thumbLoader3 are now collected inside of a vector (specialized array) so that they can be properly addressed when it comes time to destroy them)
I also wrote you a proper destroy method. ;)
I haven't tried it on my own machine, but give it a spin & see how it goes.
var images3:Array = ["ad_bona1.jpg", "ad_bona2.jpg", "ad_darkhawk1.jpg", "ad_darkhawk2.jpg"];
var thumbX3:Number = -375;
var thumbY3:Number = 220;
// begin new instance properties..
// created a new property, allowing you to group (and hold on to) the multiple thumbLoaders
var thumbLoader3Vector:Vector.<Loader> = new Vector.<Loader>();
var container3:Sprite;
// end new instance properties
var loader3:Loader = new Loader();
loader3.load(new URLRequest("assets/ad_bona1.jpg"));
addChild(loader3);
loader3.alpha = 0;
loadThumbs3();
function loadThumbs3():void
{
// this is where container3 used to be declared
container3 = new Sprite();
addChild(container3);
container3.buttonMode = true;
for(var i3:uint = 0; i3 < images3.length; i3++)
{
var tPtr:int = thumbLoader3Vector.length;
thumbLoader3Vector.push(new Loader());
// this is where thumbLoader3 used to be declared & instantiated
thumbLoader3Vector[tPtr].load(new URLRequest("assets/thumbs/" + images3[i3]));
thumbLoader3Vector[tPtr].x = thumbX3;
thumbLoader3Vector[tPtr].y = thumbY3;
thumbX3 += 85;
container3.addChild(thumbLoader3Vector[tPtr]);
thumbLoader3Vector[tPtr].addEventListener(MouseEvent.CLICK, thumbClicked3);
}
}
function thumbClicked3(event:MouseEvent):void
{
var path3:String = event.currentTarget.contentLoaderInfo.url;
path3 = path3.substr(path3.lastIndexOf("/") + 1);
loader3.load(new URLRequest("assets/" + path3));
}
///PROBLEM BELOW, button removes only "loader3" and not the other two for some reason
invis_button.addEventListener(MouseEvent.CLICK, unload_loaders);
function unload_loaders(event:MouseEvent):void{
// since the thumbLoader3 Loaders are children of container3 in the display list, we need to remove them first
for(var $i:uint = 0;$i<thumbLoader3Vector.length;$i++)
{
removeChild(thumbLoader3Vector[$i]);
// also make sure you remove the listener, so that the object will be picked up by garbage collection
thumbLoader3Vector[$i].removeEventListener(MouseEvent.CLICK, thumbClicked3);
}
// and then just set the entire vector to null
thumbLoader3Vector = null;
// remove the loader3 object & set it to null
removeChild(loader3);
loader3 = null;
// remove the container3 object & set it to null
removeChild(container3);
container3 = null;
}