AS3 clone MovieClip - actionscript-3

The below is my code for trying to clone the MovieClip and it doesn't work.
We should see two cirles if the codes is working correctly.
/*The original MovieClip*/
var circle:MovieClip = new MovieClip();
circle.graphics.beginFill(0xAA0022);
circle.graphics.drawCircle(40, 40, 40);
circle.x=10
addChild(circle);
/*CLONE the MovieClip - IT DOES'T WORK FROM HERE DOWN*/
var cloneCirle:MovieClip = new MovieClip();
cloneCirle=circle
cloneCirle.x=60
addChild(cloneCirle);

When you do cloneCircle=circle, it's not copying or cloning anything. It's just saying that the variable cloneCircle is another name for your original circle MovieClip. What you need to do is use the Graphics.copyFrom() method.
Try it:
var cloneCircle:MovieClip = new MovieClip();
cloneCircle.graphics.copyFrom(circle.graphics);
cloneCircle.x = 60;
addChild(cloneCircle);

This is for creating a duplicate of a stage object that exists in the FLA library at compile time
The object must have a 'Export for Actionscript ticked in it's properties panel and a valid class name in the 'Class' box
If the symbol only has a single frame just add another so it registers as MovieClip() rather than Sprite()
private function cloneObject(source:DisplayObject):void
{
var objectClass:Class = Object(source).constructor;
var instance:MovieClip = new objectClass() as MovieClip;
instance.transform = source.transform;
instance.filters = source.filters;
instance.cacheAsBitmap = source.cacheAsBitmap;
instance.opaqueBackground = source.opaqueBackground;
source.parent.addChild(instance);
instance.x += 20; // just to show the duplicate exists!
}
http://snipplr.com/view/44734/

Adapted from here:
function copyClip( clip:MovieClip )
{
var sourceClass:Class = Object(clip).constructor;
var duplicate:MovieClip = new sourceClass();
return duplicate;
}

Related

How to add pictures to a container Sprite with forEach and progress bar?

I am successful in loading a single image and creating it using addChild(). Now I am trying to load multiple images into a sprite "Container" using a forEach loop increasing the X value for each image so they are displayed in a row. The imageloader is referenced to linkage within an XML document. If I testrun this code, this error pops up at the point when the image is loaded and I try to removeChild() the loadBar Animation.
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
at flash.display::DisplayObjectContainer/removeChild()
Here is the AS3:
private function loadBG():void {
var artGrab:Number = 0;
var artX:Number = 0;
for each (var albumData:XML in mainXML.artist[artistID].album) {
imgURL = new URLRequest(mainXML.artist[artistID].album[artGrab].art);
imgLdr = new Loader();
//if you're loading a bigger image or need a preloader
imgLdr.contentLoaderInfo.addEventListener(Event.COMPLETE,onBGLoaded);
imgLdr.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoading);
//add loader animation "All Loader"
ldrAnim = new AllLoader();
albumContainer.addChild(ldrAnim);
ldrAnim.x = artX;
ldrAnim.y = 200;
imgLdr.load(imgURL);
artGrab++;
artX + 481;
ldrAnim.x + 481;
}
}
private function onLoading(evt:ProgressEvent):void {
var bytesToLoad:Number = imgLdr.contentLoaderInfo.bytesTotal;
var numberLoaded:Number = imgLdr.contentLoaderInfo.bytesLoaded;
ldrAnim.progBar.scaleX = numberLoaded / bytesToLoad;
var loadedPercent = Math.round(numberLoaded / bytesToLoad * 100);
ldrAnim.progPercent.text = loadedPercent + " %";
trace("Loading..." + loadedPercent + "%");
}
private function onBGLoaded(evt:Event):void {
trace("image loaded!");
//image setup
addChildAt(imgLdr,0);
//now that its 100% loaded, you can resize it , etc.
removeChild(ldrAnim);
//use cross multiplying of fractions to maintain aspect ratio
var origW = imgLdr.contentLoaderInfo.width;
var origH = imgLdr.contentLoaderInfo.height;
trace("orig width: "+ origW + "orig height: " + origH);
//set new width
imgLdr.width = 481;
var newH:Number = 481 * origH / origW;
imgLdr.height = newH;
//may wish to do positioning AFTER resizing
imgLdr.x = stage.stageWidth / 2 - imgLdr.width / 2;
imgLdr.x = 0;
imgLdr.y = 0;
imgLdr.width = 480;
imgLdr.height = 480;
imgLdr.alpha = 1;
imgLdr.z = 0;
}
Bless you for reading this all, I don't understand what is causing this error so comments are appreciated!
You need to have an array of animations for your loaders to hold with the loaders themselves, then when another ProgressEvent.PROGRESS event will arrive, query the array for event.target index, grab corresponding animation and adjust that, and stop relying on single-valued global vars once you put a single listener onto multiple different objects!
var animations:Vector.<AllLoader>;
var loaders:Vector.<LoaderInfo>;
private function loadBG():void {
var artGrab:int=0;
var artX:int=0;
animations=new Vector.<AllLoader>();
loaders=new Vector.<LoaderInfo>;
for each (var albumData:XML in mainXML.artist[artistID].album) {
imgURL = new URLRequest(mainXML.artist[artistID].album[artGrab].art);
imgLdr = new Loader();
//if you're loading a bigger image or need a preloader
imgLdr.contentLoaderInfo.addEventListener(Event.COMPLETE,onBGLoaded);
imgLdr.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoading);
//add loader animation "All Loader"
ldrAnim = new AllLoader();
albumContainer.addChild(ldrAnim);
anomations.push(ldrAnim)
loaders.push(imgLdr.contentLoaderInfo); // fill the arrays
ldrAnim.x = artX;
ldrAnim.y=200;
imgLdr.load(imgURL);
artGrab++;
artX+=481;
}
}
private function onLoading(evt:ProgressEvent):void{
var bytesToLoad:Number=evt.target.bytesTotal;
var numberLoaded:Number=evt.target.bytesLoaded; // note it now refers to target of event
var index:int=loaders.indexOf(evt.target); // should be valid
var ldrAnim:AllLoader=animations[index]; // grab corresponding animation
ldrAnim.progBar.scaleX = numberLoaded/bytesToLoad;
var loadedPercent=Math.round(numberLoaded/bytesToLoad*100);
ldrAnim.progPercent.text = loadedPercent +" %";
trace("Loading..."+loadedPercent +"%");
}
Do the same trick with your onBGLoaded function yourself, as a lesson. Note, you have to retrieve imgLdr value correctly from the event.
What's happening is that you have populated the variable ldrAnim multipe times, creating a new AllLoader each time. When you call removeChild(), this works fine the first time (sort of--it will remove the last one you created, whether it matches the image that loaded or not). When you call removeChild() again, you're calling it for the same one you just removed (which is no longer a child of the object you're calling it on).
One way to fix this is to use a Dictionary and associate each AllLoader with the Loader for that image. When the COMPLETE event fires, you can then look up the Alloader based on the event's properties and remove it.
Another solution is to write a Class that wraps an AllLoader and a Loader and then handles the transition between the two itself when the Loader has finished loading.
That might look something like this:
public class LoadSwitcher extends Sprite{
protected var loader:Loader;
protected var allLoader:AllLoader;
public function loadSwitcher(url) {
super();
loader = new Loader();
var request:URLRequest = new URLRequest(url);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, switchLoaders);
allLoader = new AllLoader(loader);//assume AllLoader now has logic to watch the loader for % complete
loader.load(request);
addChild(allLoader);
}
protected function switchLoaders(e:Event):void {
removeChild(allLoader);
addChild(loader);
}
}
Then just create and position one of these for each one of your albums.

use 1 object multiple times in as3?

I'm trying to make something like bookmarks, I have 1 note on the stage and when the user clicks it, it starts to drag and the users drops it where they want. the problem is I want these notes to be dragged multiple times.. here is my code:
import flash.events.MouseEvent;
//notess is the instance name of the movie clip on the stage
notess.inputText.visible = false;
//delet is a delete button inside the movie clip,
notess.delet.visible = false;
//the class of the object i want to drag
var note:notes = new notes ;
notess.addEventListener(MouseEvent.CLICK , newNote);
function newNote(e:MouseEvent):void
{
for (var i:Number = 1; i<10; i++)
{
addChild(note);
//inpuText is a text field in notess movie clip
note.inputText.visible = false;
note.x = mouseX;
note.y = mouseY;
note.addEventListener( MouseEvent.MOUSE_DOWN , drag);
note.addEventListener( MouseEvent.MOUSE_UP , drop);
note.delet.addEventListener( MouseEvent.CLICK , delet);
}
}
function drag(e:MouseEvent):void
{
note.startDrag();
}
function drop(e:MouseEvent):void
{
e.currentTarget.stopDrag();
note.inputText.visible = true;
note.delet.visible = true;
}
function delet(e:MouseEvent):void
{
removeChild(note);
}
any help will be appreciated.
You need to create a new instance of your note class when you drop, copy the location and other variables from the note you were dragging, add your new note to the stage, and return the dragging note to its original position.
Something like:
function drop($e:MouseEvent):void
{
$e.currentTarget.stopDrag();
dropNote($e.currentTarget as Note);
}
var newNote:Note;
function dropNote($note:Note):void
{
newNote = new Note();
// Copy vars:
newNote.x = $note.x;
newNote.y = $note.y;
// etc.
// restore original note.
// You will need to store its original position before you begin dragging:
$note.x = $note.originalX;
$note.y = $note.orgiinalY;
// etc.
// Finally, add your new note to the stage:
addChild(newNote);
}
... this is pseudo-code really, since I don't know if you need to add the new note to a list, or link it to its original note. If you Google ActionScript Drag Drop Duplicate, you will find quite a few more examples.
I think you are not target the drag object in drag function and problem in object instantiation
for (var i:Number = 1; i<numberOfNodes; i++) {
note = new note();
addChild(note);
...
....
}
function drag(e:MouseEvent):void{
(e.target).startDrag();
}
If you are dragging around multiple types of objects (eg. Notes and Images), you could do something like this, rather than hard coding the type of object to be instantiated.
function drop(e:MouseEvent):void{
// Get a reference to the class of the dragged object
var className:String = flash.utils.getQualifiedClassName(e.currentTarget);
var TheClass:Class = flash.utils.getDefinitionByName(className) as Class;
var scope:DisplayObjectContainer = this; // The Drop Target
// Convert the position of the dragged clip to local coordinates
var position:Point = scope.globalToLocal( DisplayObject(e.currentTarget).localToGlobal() );
// Create a new instance of the dragged object
var instance:DisplayObject = new TheClass();
instance.x = position.x;
instance.y = position.y;
scope.addChild(instance);
}

as3 moving image from one mc to another

With the code below I created some imgMcA and some imgMcB then I loaded images into imgMcA ones. ImgMcBs have no image at that moment. So if one of imgMcA is clicked the image should be transferred to one of the empty imgMcBs (may be randomly) and if imgmcB is clicked later the image should move back to its imgMcA back. I could not find out how I can accomplish this.
Thanks in advance
function imageList(mcname, img, index){
var imgMcA:MovieClip=new MovieClip();
imgMcA.graphics.beginFill(0x000000);
imgMcA.graphics.drawRect(0,0,imgWidth,imgHeight);
imgMcA.graphics.endFill();
imgMcA.name=lemma;
imgMcA.addEventListener(MouseEvent.CLICK, moveImage);
var imgMcB:MovieClip=new MovieClip();
imgMcB.graphics.beginFill(0x000000);
imgMcB.graphics.drawRect(0,0,imgWidth,imgHeight);
imgMcB.graphics.endFill();
imgMcB.name=index;
addChild(imgMcB);
var imgLoader:Loader = new Loader();
imgLoader.load(new URLRequest(img));
imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, changeProperties);
imgLoader.mouseEnabled=false;
imgMcA.addChild(imgLoader);
}
function moveImage(evnt:MouseEvent){
}
You could linked mcA & mcB by adding them to the same parent.
function createBlock():void
{
var imgLoader:Loader = new Loader();
//add the loading code here...
var mcA:Sprite = new Sprite();
var mcB:Sprite = new Sprite();
// add Click event listeners for both Mcs here...
var parent:Sprite = new Sprite();
//add Children
parent.addChild(mcA);
parent.addChild(mcB);
addChild(parent);
}
function mouseClickHandler(event:MouseEvent)
{
var dispatcher:Sprite = event.currentTarget as Sprite;
//if you added a Loader to it
if( dispatcher.numChildren > 0)
{
//retrieve the image
var img:Loader = dispatcher.getChildAt(0);
//identify the parent
var parent:Sprite = dispatcher.parent;
var index:int = parent.getChildIndex(dispatcher);
//identify the receiving MC
//of course this only works with two children!!!
if(index > 0)
var receiver:Sprite = parent.getChildAt(0) as Sprite;
else
receiver = parent.getChildAt(1) as Sprite;
//add the image to the other MC
receiver.addChild(img);
}
}
The rest is not too complicated to achieve. You will need to use a Boolean and add a TextField. If the TextField contains text, set the Boolean to true.
It may be worth looking at Classes or you could use an Object as a container for the MovieClips, the TextField and the Boolean, although a Class will give you more flexibility...
With a Class, you wouldn't have to iterate in order to find what does what. Your Click listener would look something like this:
private function mouseClickHandler(event:MouseEvent)
{
receiver.addChild( image );
if( hasText)
imageReturn();
}

Add multiple movieclips, not replacing the old ones

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.

Finding (loaded) image size in AS3 (Action Script 3.0)

Im currently using the following function to load an image, however i could not figure out a way to find the width of the loaded image, which i intend to use before placing the next image using the same function.
Note that q is a a variable (a number) which is used to load differant images.
=X i need help obtainning the loaded image width...
function LoadImage(q)
{
var imageLoader:Loader = new Loader();
var image:URLRequest = new URLRequest("GalleryImages/Album1/"+q+".jpg");
imageLoader.load(image);
addChild (imageLoader);
imageLoader.x = 0 + CurrentXLength;
imageLoader.y = 0;
imageLoader.name = "image"+q;
trace(imageLoader.x)
}
You can't know the width of the bitmap until it's actually loaded:
function LoadImage(q)
{
var imageLoader:Loader = new Loader();
var image:URLRequest = new URLRequest("GalleryImages/Album1/"+q+".jpg");
imageLoader.contentLoader.addEventListener(Event.COMPLETE, ImageLoaded);
imageLoader.load(image);
addChild (imageLoader);
...
private function ImageLoaded(e:Event):void
{
var imageLoader:Loader = Loader(e.target.loader);
var bm:Bitmap = Bitmap(imageLoader.content);
var CurrentXLength = bm.width;
....
Alternativly this link might be helpful? Haven't tried it myself ...
I just asked for de width property of loader object:
var loader:Loader;
function loadImage(dir:String):void {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, placeImage);
var urlReq:URLRequest = new URLRequest(direccion);
loader.load(urlReq);
}
function placeImage(o:Event):void {
loader.x = (1360 - loader.width)/2;
loader.y = (768 - loader.height)/2;
addChild(loader);
}
Where 1360 and 768 are the canvas dimensions...
To access the width you must do it within the function assigned to handle Event.COMPLETE.
"You will want an array containing the items you wish to load. You should probably load this data in with XML so it is dynamic and scalable. Once the xml data is loaded it should be assigned to an array in whatever fashion you like. The reason that we must use an array in this situation, rather then just using the XML object, which is essentially an array, is because you need the know an objects width so that you can base the next objects X position off of the last objects X position plus its WIDTH.
With XML it is common to use a for loop and just iterate through "x" amount of times. We do not want this, in this case. To obtain the "WIDTH" property of the loaded asset, it must be accessed from within the function assigned to fire when the loader fires Event.COMPLETE. Once the image has completed it will remove the item from the array, set a variable as to the lastX and lastWidth, and then get the next item in the array and start all over. Eventually the array is empty and the process is complete.
-Note: I will skip loading the XML and just inject the data into the array myself.
package
{
import flash.display.Sprite;
import flash.display.Loader;
import flash.net.URLRequest;
public class DocumentClass extends Sprite
{
private var _array:Array;
private var _lastX:Number;
private var _lastWidth:Number;
public function DocumentClass():void
{
_array = new Array();
//Add Items to an array however you wish. I did it
//this way to make it easier to read on this site.
_array.push({name: "image1", path: "path/to/image1"});
_array.push({name: "image2", path: "path/to/image2"});
_array.push({name: "image3", path: "path/to/image3"});
loadImage();
}
private function loadImage():void
{
if(_array.length > 0)
{
var loader:Loader = new Loader();
addChild(loader);
var request:URLRequest = new URLRequest(_array[0].path); //Loads first item in the array each time.
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
loader.x = _lastX + _lastWidth;
laoder.load(request);
_lastX = loader.x; //We set after so we are ready for the next.
_array.shift(); //Remove the first item from the array.
}
}
function onImageLoaded(e:Event):void
{
_lastWidth = e.target.width;
loadImage(); //Attempt to load another image if the array isn't empty.
}
}
}
I hope this helps, the code isn't tested, but the concept seems valid.
Yeah I used scott's answer but it's worth noting that 'imageLoader.contentLoader' should be 'imageLoader.contentLoaderInfo' in the LoadImage() function. Solved the width prob for me-- thanks mang