I am attempting to use the 'File' function in ActionScript 3 to save the following information:
I have varying draggable display objects in the scene, the amount and type can vary. I want to save the amount and their position and then load them back in a future session.
I am struggling to use File to save anything, I have searched the Adobe documentation and cannot get my head round how to use it.
I have not yet developed any code using it.
Any help would be appreciated.
Thank you.
You are trying to write a DisplayObject into the file directly, this is prevented by Flash engine due to the way Flash handles default serialization of any object. In order to save a DisplayObject into the external resource, you need to employ IExternalizable on that object's class and any class of objects you will plan to store as well. The implementation of writeExternal should save all data required to rebuild the said object from scratch, and readExternal should also employ methods to restore the integrity of said DisplayObject by performing addChild() on nested display objects, or adding them into other internal structures that object might contain.
Note, other answers contain valid points for doing a custom serialization with XML or JSON, and also contain links to requires import, in particular, flash.utils.registerClassAlias and flash.utils.getDefinitionByName are gravely needed to recreate the structure from a serialized data chunk.
An example: Let's say you have a drawing board in a Board class, and a set of rectangles that you can drag by using mouse, that differ by size and color. Rectangles are custom made MovieClips and don't have a class of their own, but each MovieClip is also assigned a color property to simplify their distinction. This means you need to implement IExternalizable on Board class only. Let's also assume Board class has a pieces array that contains all links to nested rectangles, and a method to create a new properly sized rectangle based on width, height and color supplied as parameters. (There might be more requirements to the data structure of Board to meet in your case, so watch closely) So, the process of serializing Board will be to collect all the data from nested MCs and stuff it in order into IDataOutput supplied, and the process of restoring an instance of Board should retrieve stored data, parse it to find what is where, create the nested MCs to be the same like they've been stored, position them properly, addChild() to self and rebuild thepieces` array.
public class Board extends Sprite implements IExternalizable {
private var pieces:Array;
public function createRectangle(_width:Number,_height:Number,color:uint):MovieClip {
var mc:MovieClip=new MovieClip();
mc.graphics.beginFill(color);
mc.graphics.drawRect(0,0,_width,_height);
mc.graphics.endFill();
mc.color=color;
pieces.push(mc);
return mc;
}
A refinement to data structure is already visible - you need to store the passed _width and _height in the MC somewhere, because the actual width of that MC will differ from what's passed by the default line thickness (1, 0.5 on either side). x and y are properly retrieved from MC's properties, though. So, adding both lines into createRectangle is necessary.
mc._width=_width;
mc._height=_height;
With this, serializing the Board becomes more easy.
public function writeExternal(output:IDataOutput):void {
var pl:int=pieces.length; // cache
output.writeInt(pl); // assuming we keep this array in integral state
for (var i:int=0;i<pl;i++) {
var _mc:MovieClip=pieces[i];
output.writeDouble(_mc.x); // this is usually not rounded when dragging, so saving as double
output.writeDouble(_mc.y);
output.writeDouble(_mc._width);
output.writeDouble(_mc._height);
output.writeInt(_mc._color);
}
// if anything is left about the "Board" itself, write it here
// I'm assuming nothing is required to save
}
To restore, you need to read the data out of IDataInput in the very same order as it was written in writeExternal and then process to rebuilding the display list we've stored.
public function readExternal(input:IDataInput):void {
// by the time this is called, the constructor has been processed
// so "pieces" should already be an instantiated variable (empty array)
var l:int;
var _x:Number;
var _y:Number;
var _width:Number;
var _height:Number;
var _color:uint;
// ^ these are buffers to read data to. We don't yet have objects to read these into
input.readInt(l); // get pieces length
for (var i:int=0;i<l;i++) {
input.readDouble(_x);
input.readDouble(_y);
input.readDouble(_width);
input.readDouble(_height);
input.readInt(_color);
// okay we got all the data representing the rectangle, now make one
var mc:MovieClip=createRectangle(_width,_height,_color);
mc.x=_x;
mc.y=_y;
addChild(mc); // createRectangle does NOT have addchild call
// probably because there are layers for the parts to be added to
// I'm assuming there are no layers here, but you might have some!
// pieces array is populated inside createRectangle, so we leave it alone
}
// read all the data you have stored after storing pieces
}
In case your nested MCs have a class that also implements IExternalizable, you can save the entire array in a single instruction, writeObject(pieces), this will make Flash walk through the array, find all data it contains and call writeObject on any nested object, essentially calling that class's writeExternal function for each of the instance in the array. Restoring such an array should include rebuilding the display list by walking the array and calling addChild() on each of the restored instances.
And last but not the least, registerClassAlias() should be called prior to doing any serialization or deserialization of custom objects. Best place to call these is probably your main object's constructor, as this will surely be called before any other code your application contains.
Assuming all your objects to save belong to the same parent, you could dosomething along these lines:
First, create a class file (let's call is SaveData.as and put it in the root of your project directory). This will describe the data you want to save:
package
{
import flash.geom.Rectangle;
public class SaveData
{
public var bounds:Rectangle; //to save where an object is on the stage
public var classType:Class; //to save what kind of object it is
//you could add in more proterties, like rotation etc
public function SaveData() {
}
}
}
Next, on your save function, do something like this:
//this will hold all your data
//a vector is the same as an array only all members must be of the specified type
var itemList:Vector.<SaveData> = new Vector.<SaveData>();
//populate the array/vector with all the children of itemContainer
var tmpItem:SaveData;
//loop through all children of item container
for (var i:int = 0; i < itemContainer.numChildren; i++) {
tmpItem = new SaveData(); //create a new save record for this object
tmpItem.bounds = itemContainer.getChildAt(i).getBounds(itemContainer); //save it's bounds
tmpItem.classType = getDefinitionByName(itemContainer.getChildAt(i)) as Class; //save it's type
itemList.push(tmpItem); //add it to the array
}
//Now you have an array describing all the item on screen
//to automatically serialize/unserialize, you need this line (and you need to register every class nested in SaveData that isn't a primitive type - which would just be Rectangle in this case
registerClassAlias("SaveData", SaveData);
registerClassAlias("flash.geom.Rectangle", Rectangle);
//create a new File to work with
var file:File = File.applicationStorageDirectory; //or whatever directory you want
file.resolvePath("saveData.data"); //or whatever you want to call it
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeObject(itemList); //write the array to this file
fileStream.close();
Now, to load it back in:
var itemContainer:Sprite = new Sprite(); //however you initialize this
addChild(itemContainer);
var file:File = File.applicationStorageDirectory;
file.resolvePath("saveData.data");
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.READ);
var itemList:Vector.<SaveData> = fileStream.readObject() as Vector.<SaveData>;
fileStream.close();
//now that you've read in the array of all items from before, you need to recreate them:
var tmpItem:DisplayObject;
var tmpClass:Class;
//loop through all items in the array, and create a object
for (var i:int = 0; i < itemList.length; i++) {
tmpClass = itemList[i].classType; //The type of item
tmpItem = new tmpClass() as DisplayObject; //create the item
//now move the item to it's former position and scale
tmpItem.x = itemList[i].x;
tmpItem.y = itemList[i].y;
tmpItem.width = itemList[i].width;
tmpItem.height = itemList[i].height;
//add the item back to the parent
itemContainer.addChild(tmpItem);
}
If you're not sure of the imports, here they are:
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.net.registerClassAlias;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;
var bytes:ByteStream;
var filename:String = "mySaveFile.sav";
//[...] //initialize byte stream with your data
//get a reference to where you want to save the file
//(in this example, in the application storage directory,
//which is fine if you don't need to move the save file between computers
var outFile:File = File.applicationStorageDirectory;
outFile = outFile.resolvePath(fileName);
//create a file output stream, which writes the byte stream to the file
var outStream:FileStream = new FileStream();
outStream.open(outFile, FileMode.WRITE);
outStream.writeBytes(bytes, 0, bytes.length);
outStream.close();
//to load the file:
var inFile:File = File.applicationStorageDirectory;
inFile = inFile.resolvePath(fileName);
bytes = new ByteArray();
var inStream:FileStream = new FileStream();
inStream.open(inFile, FileMode.READ);
inStream.readBytes(bytes);
inStream.close();
I usually use SharedObject, by saving the number of objects with their locations ,scale ,rotation , .. etc. as an array (usually multidimensional array).
this example is tested :
first make a movie clip giving it "mc" as a name in the ActionScript Linkage
add any graphics you like
(this MovieClip will be the objects to be saved later )
then add the following script
////////// get random values for each object
var speed:Number ;
var yPosition:Number ;
var size:Number ;
this.width = size;
this.height = size;
this.y = yPosition ;
//// Moving the MovieClip from Left to right
function moving(e:Event):void
{
this.x += speed ;
if(this.x > 550)
{
this.removeEventListener(Event.ENTER_FRAME,moving);
MovieClip(parent).removeChild(this);
}
}
this.addEventListener(Event.ENTER_FRAME,moving);
in the root stage of the project add :
import flash.events.MouseEvent;
import flash.display.MovieClip;
var num:int = 0 ;
var mmc:MovieClip ;
var mySharedObj:SharedObject = SharedObject.getLocal("SavingStatus"); //// SharedObject to save info
function init()
{
if (!mySharedObj.data.savedArray)
{
///// first run No datat saved
this.addEventListener(Event.ENTER_FRAME,addingmcs)
}else {
///// Laoding previusly saved data
loading();
}
}
init() ;
/////////////// adding MovieClips to stage /////
function addingmcs(e:Event):void
{
num +=1 ;
if(num > 20){
num = 0 ;
mmc = new mc ;
mmc.speed = 2 + (5 * Math.random()) ;
mmc.yPosition = 500 * Math.random() ;
mmc.size = 50 + 10 * Math.random() ;
this.addChild(mmc);
}
}
///////////////////////////////////////////
///////////////////////////////////////////////
var obj:* ; //// to hold children MovieClips of the stage
var savingArr:Array = new Array ; //// the array to be saved , Contains all info of the children
////////////// Save all MovieClips with their parameters ////////////
function saving(e:MouseEvent):void
{
this.removeEventListener(Event.ENTER_FRAME,addingmcs)
for (var i:int=0;i<this.numChildren;i++)
{
if (this.getChildAt(i)is MovieClip) { ///// add all MovieClips of the stage to the array with their info (position - size - speed ... etc)
obj = this.getChildAt(i);
savingArr.push([obj , obj.x , obj.y , obj.speed , obj.size]); //// add the info in 3 dimentional array
obj.speed = 0 ;
}
}
////////////////saving array externally
mySharedObj.data.savedArray = savingArr ;
mySharedObj.flush ();
}
save_btn.addEventListener(MouseEvent.CLICK,saving)
////////////// Load all saved parameters ////////////
load_btn.addEventListener(MouseEvent.CLICK,loading)
function loading(e:MouseEvent =null):void
{
savingArr = mySharedObj.data.savedArray ;
for (var i:int=0;i<savingArr.length ; i++)
{
mmc = new mc ;
mmc.x = savingArr[i][1] ; ///// Get saved x
mmc.yPosition = savingArr[i][2] ; ///// Get saved y
mmc.speed = savingArr[i][3] ; ///// Get saved speed
mmc.size = savingArr[i][4] ; ///// Get saved size
addChild(mmc);
}
this.addEventListener(Event.ENTER_FRAME,addingmcs) ;
}
You already have some answers here but from your question, maybe you are missing the larger context.
So the File class represents a path to a file on disk and the FileStream class enables reading and writing data to that file. These are easy to use and there are many examples on the web. Here is one tutorial from Adobe: Reading and writing files
But what data to write and what is the format and data type? Those are the more important and more interesting questions.
The simplest approach is to use a text based format like XML or JSON where you read and write whatever properties of Sprites (or other objects) you want. One advantage of this is that the resulting file is a human readable/editable text file. A minor disadvantage is that you need to specify which properties to save and restore and deal with simple data type conversions (string to int, etc).
A more robust approach is to use what is called Serialization where the state of an entire object is saved and restored. This is more complicated and while not hard, is probably overkill for your project needs. There are good examples and discussion here , here and here.
For your current project and skill level, I'd suggest using XML orJSON Here's a tutorial using XML: Loading and Processing External XML Files
Related
http://jacksondunstan.com/articles/1642 I followed this to the T, yet I'm running into issues. I'm trying to save a bunch of icons. They have a custom class "Iconn" and are stored in a Vector.<Iconn> for future use. Once the user adds a new icon, I open up a filestream and use it to write the entire vector.
public function addShortcut(f:File):void
{
//Display on side
icons.reverse(); //Add to the front so it displays on the top.
icons.push(new Iconn(f)); //Use 16x16 bitmap
addChild(icons[icons.length - 1]);
icons.reverse();
//Save object
fs = new FileStream();
fs.open(shortcuts, FileMode.WRITE);
fs.writeObject(icons);
fs.close();
reorder(); //Reorganizes the icons on the screen.
}
This works all and well with no errors, but when I try to re-launch the application with some icons saved, the vector doesn't even exist.
icons = new Vector.<Iconn>();
if (shortcuts.exists)
{
trace("Shortcuts exist..adding");
fs = new FileStream();
fs.open(shortcuts, FileMode.READ);
icons = fs.readObject() as Vector.<Iconn>;
fs.close();
trace("icons = " + icons); //TypeError: Error #1009: Cannot access a property or method of a null object reference.
trace("icons length = " + icons.length); //TypeError: Error #1009: Cannot access a property or method of a null object reference.
}
I tried adding registerClassAlias("Vector.<Iconn>", Vector.<Iconn>);, but then I get a compiler error
1067: Implicit coercion of a value of type __AS3__.vec:Vector.<Iconn> to an unrelated type Class.
Edit: Here is my Iconn class http://pastebin.com/5TujzpvR
There's a few reasons this isn't working.
When objects are unserialized, you cannot pass parameters to their constructors. This means that you either need to take out all constructor arguments of ALL classes nested inside your object, or make them all optional by giving them default values.
You have to register every single non-primitive class you use in the object being serialized (including the object's class itself). This includes nested classes. In your case, this includes at least:
flash.display.Bitmap;
flash.display.Sprite;
flash.events.MouseEvent;
flash.filesystem.File;
fl.transitions.Tween;
fl.transitions.easing.Strong;
Plus anything referenced inside of those classes.
Serializing display objects just doesn't work* (it is technically possible with a custom class that implements IExternalizable, but it's quite involved). If you want an example, there is one here.
The best way to serialize display objects, is to create a very very basic class that contains the bare minimum data needed to reconstruct your display object.
In your case, it looks like all you really need is bitmap data. So you could make a simple class like the following:
package
{
import flash.geom.Rectangle;
import flash.utils.ByteArray;
public class SaveData
{
public var imgBytes:ByteArray;
public var bounds:Rectangle;
public var transparent:Boolean;
}
}
All it stores is the raw bitmap data (a byte array of pixel values), and bounds of the bitmap.
First, you need to register all the classes used. That would look like this:
//you need to register your class that you're using writeObject on
registerClassAlias("SaveData", SaveData);
//you also need to register the two classes that are referenced in your SaveData class
registerClassAlias("flash.utils.ByteArray", ByteArray);
registerClassAlias("flash.geom.Rectangle", Rectangle);
//you ALSO need to register Point since it's referenced inside the Rectangle class
registerClassAlias("flash.geom.Point", Point);
Now, to save it, do mostly what you're already doing:
//create a vector to store your list of items to save
var saveArray:Vector.<SaveData> = new Vector.<SaveData>();
//loop through all your icons and do the following in the loop (where bitmap is the icon's bitmap):
var saveData:SaveData = new SaveData();
saveData.bounds = bitmap.getBounds(bitmap);
saveData.imgBytes = bitmap.bitmapData.getPixels(saveData.bounds);
saveData.transparent = bitmap.bitmapData.transparent;
saveArray.push(saveData);
//write the Vector to the file
fs = new FileStream();
fs.open(shortcuts, FileMode.WRITE);
fs.writeObject(saveArray);
fs.close();
Now, when you're ready to load it all back in:
fs = new FileStream();
fs.open(shortcuts, FileMode.READ);
var saveArray:Vector.<SaveData> = fs.readObject() as Vector.<SaveArray>;
fs.close();
//now loop through your array and re-create all your icons.
for(var i:int=0;i<saveArray.length;i++){
var bmd:BitmapData = new BitmapData(saveArray[i].bounds.width, saveArray[i].bounds.height, saveArray[i].transparent);
bmd.setPixels(saveArray[i].bounds, saveArray[i].imgBytes);
var bitmap:Bitmap = new Bitmap(bmd);
//instead of passing in a file to your Iconn class, tweak it so you pass in a bitmap:
var icon:Iconn = new Iconn(bitmap);
//do what you need to do with he icon
}
I am using following function to Save the output from SWF and send it to a PHP page so that it creates JPG, my problem if there is not INTERNET connection and the SWF is in a CDROM can the Save function be used in FLASH so that it outputs JPG and save it on a computer.
In short can we Save movieclip output as an JPG
/**
Screenshot and jpg output
**/
import flash.display.BitmapData;
import flash.geom.Matrix;
//Buttons handlers. Should add an extra function because delegate doesn't allow to pass parameters
shaF.onPress = mx.utils.Delegate.create(this,makeShadow);
//Helper functions to pass parameters
function makeShadow() { capture(0) }
/*
create a function that takes a snapshot of the Video object whenever it is called
and shows in different clips
*/
function capture(nr){
this["snapshot"+nr] = new BitmapData(abc._width,abc._height);
//the bitmap object with no transformations applied
this["snapshot"+nr].draw(abc,new Matrix());
var t:MovieClip = createEmptyMovieClip("bitmap_mc"+nr,nr);
//positions clip in correct place
//t._x = 350; t._y = 10+(nr*130); t._xscale = t._yscale = 50
//display the specified bitmap object inside the movie clip
t.attachBitmap(this["snapshot"+nr],1);
output(nr);
//attachMovie("print_but", "bot"+nr, 100+nr, {_x:t._x+t._width+50, _y:t._y+t._height/2})
}
//Create a new bitmapdata, resize it 50 %, pass image data to a server script
// using a LoadVars object (large packet)
function output(nr){
//Here we will copy pixels data
var pixels:Array = new Array()
//Create a new BitmapData
var snap = new BitmapData(this["snapshot"+nr].width, this["snapshot"+nr].height);
//Matrix to scale the new image
myMatrix = new Matrix();
myMatrix.scale(1, 1)
//Copy image
snap.draw(this["snapshot"+nr], myMatrix);
var w:Number = snap.width, tmp
var h:Number = snap.height
//Build pixels array
for(var a=0; a<=w; a++){
for(var b=0; b<=h; b++){
tmp = snap.getPixel32(a, b).toString(16)
//if(tmp == "-fcffff")
//{
//tmp="-ff0000";
//}
pixels.push(tmp.substr(1))
}
}
//Create the LoadVars object and pass data to PHP script
var output:LoadVars = new LoadVars()
output.img = pixels.toString()
output.height = h
output.width = w
//The page (and this movie itself) should be in a server to work
output.send("show.php", "output", "POST")
}
stop()
Since you already have the BitmapData, this is fairly easy. Modifying your output function:
//Copy image
snap.draw(this["snapshot"+nr], myMatrix);
//Now check if we want to save as JPG instead of sending data to the server
if (runningFromCdrom) {
var encoder:JPEGEncoder = new JPEGEncoder();
var bytes:ByteArray = encoder.encode(snap);
var file:FileReference = new FileReference();
file.save(bytes);
} else {
// ...the rest of your output method...
}
How to determine the runningFromCdrom value is up to you. If the SWF is being run inside an HTML document, the best way to tell if the program is being run from a CD-ROM would be to specify it in the FlashVars.
Yes, you can save JPEG's directly from Flash. There is no need for the intermediate step to PHP.
You can use one of these JPEG encoders:
https://github.com/mikechambers/as3corelib
http://www.switchonthecode.com/tutorials/flex-tutorial-an-asynchronous-jpeg-encoder
I would recommend the second as it can work asynchronously (kind of) which means your UI shouldn't lock up while encoding the JPEG.
I am running a for loop that loads swfs onto the stage. _componentData is an XMLList.
private function loadDevices():void
{
for each (var d:XML in _componentData.device)
{
var iname:String = d. # iname;
var mLoader:Loader = new Loader();
var mRequest:URLRequest = new URLRequest(d. # path);
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onDeviceLoadComplete);
mLoader.load(mRequest);
}
}
Inside onDeviceLoadComplete i want to be able to set the instance name of the loaded swf. Can i send extra parameters to the event handler function? Or is there a better approach?
Pretty sure you can't change an instance name on a dynamically-generated object. In any case, it would probably be easier to push them into an array for reference. You could use associative keys in the array and reference them as such:
var myArray:Object = new Object();
myArray.apple = "red";
for (var item in myArray) {
trace(item); // apple
trace(myArray[item]); // red
}
I found a nice link that pointed me in the right direction since i cannot set the instance name.
What i am doing is setting the name prop of the loader to iname and then using e.target.loader.name instead of the instance name. From there i am able to move forward in my development. Thanks!
is it possible to obtain the instance name of a class from the class without having to manually pass the instance name as a string parameter to the class constructor?
//Create New SizeClass
var big:SizeClass = new SizeClass();
//-------------
package
{
public class SizeClass
{
public function SizeClass()
{
trace( //-- Instance Name "big" --// );
}
}
}
No, it is not possible to know anything about the containing code block during a constructor, save what you can learn from the stack trace (though that's not available except in the debugger version of Flash). Even if you had a global access point for the containing class, it still would not allow for that access.
Think of a constructor like a method call. In a line of AS, it will be called before the assignment. Eg: var a:Foo = new Foo() the Foo is created (the constructor completes), and then a is populated with whatever just happened. After that point a will remain agnostic of its context (because of encapsulation) unless it is told about it (this is even true on a DisplayObject -- try this( var mc:MovieClip = new MovieClip(); trace( mc.root ) //this will be null ).
I'm keeping this because it is useful albeit not useful to your original answer.
You can always get the name of a class with getQualifiedClassName from the flash.utils package. You can't get a DisplayObject's until well after it has been constructed, but you can simulate this by (I believe) overriding function set name( value:String ):void. If that doesn't work, then try finding it after Event.ADDED and/or Event.ADDED_TO_SAGE.
The instance name isn't very important. You'd better store references of the instances inside an array.
var sizes:Array = new Array();
var big:SizeClass = new SizeClass();
sizes.push( big );
When you want to access them, you can loop through the array.
for (var i:uint = 0; i < list.length; ++i)
{
var size:SizeClass = list[i] as SizeClass;
trace( size );
}
BTW: Instead of an instance name it is possible to add an automatic index to your class.
package
{
public class SizeClass
{
private static var global_index:int = 0;
public const INDEX:int = global_index ++;
}
}
Which you can access like this:
var big:SizeClass = new SizeClass();
trace(big.INDEX) // 0
var small:SizeClass = new SizeClass();
trace(small.INDEX)// 1
source: http://blog.stroep.nl/2010/08/auto-increment-as3-class/
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.