ActionScript: Adding multiple instances of the same swf file to the stage - actionscript-3

I'm creating a game in ActionScript 3.0 using the FlashPunk game engine and the FlashDevelop SDK.
I've created my own MovieClip class that takes a preloaded movie clip file.
public function MyMovieClip(file:MovieClip, posX:int, posY:int, frameRate:int)
{
movieClip = file;
movieClip.x = posX;
movieClip.y = posY;
movieClip.stop();
FP.stage.addChild(movieClip);
timer = new Timer((1 / frameRate) * 1000);
timer.start();
timer.addEventListener(TimerEvent.TIMER, onTick);
}
The update for my movie clip is as follows:
private function onTick(e:TimerEvent):void
{
if (isRepeating)
{
if (movieClip.currentFrame == movieClip.totalFrames )
{
movieClip.gotoAndStop(0);
}
}
movieClip.nextFrame();
}
The problem that I'm having is that when I have multiple instances of the MyMovieClip class using the same swf file, only the last instance is rendered and is updated for each instance of the class that I have.(e.g 3 instances of MyMovieClip, the last instance is updates at 3 times the speed.)
If anymore info is needed I'll be happy to supply it.

You can create a new instance of the same loaded swf by doing this:
// re-use a loaded swf
var bytes:ByteArray = existingLoader.content.loaderInfo.bytes;
var loader:Loader = new Loader();
loader.loadBytes(bytes);
where existingLoader is the Loader that you used to load the swf in the first place.
The Loader used with loadBytes will dispatch another COMPLETE Event, so when you make a listener for that, you can use the 'cloned' version:
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onSWFClonedComplete);

You may have multiple instances of MyMovieClip, but what's with file:MovieClip which you are adding to the stage. If this is always the same instance of a MovieClip you will have this result, regardless how often you instantiate your MyMovieClip class, because you are adding the same instance multiple times to the stage.
You may have to load the "preloaded clip" multiple times or, if you are able to (you know the class name etc.), create a new instance of the desired class with getDefinitionByName() from your loaded clip and attach this new instance.

Related

MovieClip doesn't dispatch mouse event after nesting MovieClip loaded from external SWF

Mostly, problem is described in the title... I tried to load an external SWF file that contains some named MovieClip instances (exporting and naming is done by Flash CS5 software) and to add some of externaly loaded (named) MovieClip-s in MovieClip object which is created in my code. Problem appears when i add MOUSE_CLICK listener to parent MovieClip. Simply, it does not dispatch event when i click on it at the stage...
private var loader:Loader;
public function Example(){
loader = new Loader();
var request:URLRequest = ... // URL to external SWF
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadingCompleted)
loader.load(request);
}
private function loadingCompleted(event:Event):void{
var mc:MovieClip = loader.content as MovieClip;
var myMovie:MovieClip = new MovieClip();
myMovie.addChild(mc.getChildByName("object_name"));
myMovie.addEventListener(MouseEvent.CLICK, myMovieClicked);
addChild(myMovie); // myMovie (with nested mc) appears on the stage
}
private function myMovieClicked(evt:Event):void{
//never reached
}
EDIT: I didn't mention that i'm working in Flex using FlashBuilder 4.5 where i created ActionScript project. Code above is body of Example class, which is main SWF class.
EDIT AFTER ANSWER: myMovie.mouseChildren = false solves the problem. Earlier i tried to set mouseEnabled = true, and it didn't fix the problem. But i'm confused about event flow now... Even if child is target node, why mouse listener on parent MovieClip doesn't recieve event (in capture phase) when parent is still on event flow? Moreover, when i create another movie clip in my code (whit some simple shape inside) and add it to myMovie, everything works fine. What is so special when i obtain movie clip from externaly loaded SWF?
Have you tried doing myMovie.mouseEnabled = true and myMovie.mouseChildren = false ?

remove external swf file as3

I have loaded an external swf file which plays a flv file by default as swf is loaded. Now the problem is how do i remove the swf file from memory. my code :
var myLoader:Loader = new Loader();
var url:URLRequest = new URLRequest("ExternalSWF.swf");
myLoader.load(url);
detailMovieClip.movieHolder.addChild(myLoader);
I have tried many combinations of removeChild, unload and unloadAndStop but none works. I figure its all about not referencing correctly.
Update:
I went with the answer from Jegan, but it only work when i am testing in a dummy project which has only 1 numChildren, howver in real world code example numChildren reported 22 so i am not sure if that would be an issue. here is the real world code:
var myImageLoader:Loader;
var myImageRequest:URLRequest;
var theImagePath:String;
//part from xml processor function
theImagePath = "flash/"+myXML..item_video_link[n];
loadTheMovie(theImagePath);
function loadTheMovie(theImagePath):void{
myImageLoader = new Loader();
myImageRequest= new URLRequest(theImagePath);
myImageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,showMeTheVideo);
myImageLoader.load(myImageRequest);
}
function showMeTheVideo(evt:Event):void{
detailsMovieClip_mc.details_video_holder.dynamicVideoHolder.addChild(myImageLoader);
}
stopVideo(sectionname):viod{
if(detailsMovieClip_mc.details_video_holder.dynamicVideoHolder.numChildren !=0){
trace("what is the number of children: "+numChildren);
myImageLoader.unloadAndStop();
detailsMovieClip_mc.details_video_holder.
dynamicVideoHolder.removeChild(myImageLoader);
}
}
stage.addEventListener(MouseEvent.CLICK, removeSWF);
function removeSWF (e:MouseEvent):void
{
if(detailMovieClip.movieHolder.numChildren !=0){
myLoader.unloadAndStop();
detailMovieClip.movieHolder.removeChild(myLoader);// empty the movieClip memory
}
}
OR
Name your Loader instance and then search by using getChildByName
myLoader.name = "myloader";
function removeSWF (e:MouseEvent):void
{
if(detailMovieClip.movieHolder.numChildren !=0){
Loader(detailMovieClip.movieHolder.getChildByName("myloader")).unloadAndStop();
detailMovieClip.movieHolder.removeChild(detailMovieClip.movieHolder.getChildByName("myloader"));// empty the movieClip memory
}
}
I guess this is because you are adding the loader to the scene itsef.
Either you want to keep this behavior, in this case there is a quick fix, remove the loader from the MovieClip by using removeChild(), then set the reference to null, or use the delete keyword.
Either you want to do it properly, in this case, listen for the LOADED event, adds the MovieClip contained by the loader.content to the target MovieClip. Then, when you want to unload it, remove the clip from the container using removeChild(), then loader.unload().

Looking for AS3 event when MovieClip is initialised like AS2 onclipevent(load)

In AS2 there was an event onclipevent(load) that triggered when a movie clip was initialised on the stage. I have searched a lot but the only solution I can find for the same functionality in AS3 is to make a custom class for the movie clip. This is a bit overkill in my case as I only have one line of code to run and I'm trying to avoid lots of extra AS files in my project. I should note that my MovieClips are not dynamically added so events like Event.ADDED_TO_STAGE etc will not work.
Is there really no way to do onclipevent(load) in AS3?
There are no AS2 style clip events in AS3. If you want to avoid making a custom class for your clip, you could put the initialization code in a function and call it immediately when you create the clip, like this:
import flash.display.MovieClip;
function onClipLoad(clip : MovieClip) : void
{
// do onLoadStuff here
clip.x = 100;
clip.y = 10;
}
// call onClipLoad when the clip is created, either in code or via timeline
var myClip : MovieClip = new MyClip();
onClipLoad(myClip);
Even better, you could place this code on frame 1 of your MovieClip's timeline to automatically initialize instances of it:
var initialized : Boolean;
if(!initialized) {
initialized = true;
// do init code here
x = 100;
y = 10;
}

Stage and classes

I am new to AS3 and am trying to lean its OOP ways. What I am having problems with is understanding how to access the stage with separate classes.
Here is an example of what I am trying to do:
package game{
import flash.display.*;
public class Main extends MovieClip{
function Main(){
var player = new Player();
var playerBullets = new playerBullet();
addChild(player.players);
}
}
package game{
import flash.display.*;
public class Bullet extends Main // also tried with MovieClip and Sprite{
function Bullet(){
// empty
}
function blah(){
var someSprite = new someSprite();
Main.addChild(someSprite);
stage.addChild(someSprite);
root.addChild(someSprite);
}
}
}
I have Omitted another class which calls the blah method as I feel it is not relevant.
Basically what I want to know is how to add things to the stage in classes as it lookes like I am missing something crucial.
*EDIT TO INCLUDE ERROR*
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at game::Bullet/blah()
at game::Player/fire()
You shouldn't necessarily be extending main to create something like a bullet class, this can be it's own class that extends Sprite or MovieClip. The stage object is considered a global object, as it is a singleton (except in the case of Adobe AIR where you can have one stage per NativeWindow that you spawn). So any object that extends DisplayObject or has DisplayObject in it's inheritance chain will by default have a reference to the stage via a getter, which is populated automatically when a displayObject is added to the display list. This can happen by either adding a clip directly to the root stage object or by adding a clip as a child of another clip, that eventually connects to the stage. For example:
var clip1:MovieClip = new MovieClip();
stage.addChild(clip1); //Clip 1 can now access the stage reference internally.
ver clip2:MovieClip = new MovieClip(); //Right now, clip2 cannot access the stage reference interally.
clip1.addChild(clip2); //Now clip2 can access the internal stage reference because it has been connected to the display list through clip1.
The other mistake people make is accessing stage within a DisplayObject typed class (such as your Main class) without first ensuring that the object itself has been added to the stage. You do this by listening for the Event.ADDED_TO_STAGE event within the constructor of the class, like so:
public class Main extends MovieClip{
function Main(){
if(stage){
//The stage reference is present, so we're already added to the stage
init();
}else{
addEventListener(Event.ADDED_TO_STAGE, init);
}
var player = new Player();
var playerBullets = new playerBullet();
addChild(player.players);
}
private function init(e:Event = null)
{
trace("Added to stage, the stage reference is now populated and stage can be accessed");
}
}
This could be the problem you're having, but it's hard to say since you have not specified any errors. However, this is likely an issue or will be for you, since it's quite common. Inside the init() method you can then set a flag so that when external classes call your Main.blah() method, you can ensure that the stage reference exists before attempting to add something to the stage. Take note however that within your Main class when you simply say:
addChild(someChild);
or
this.addChild(someChild);
you're not adding that child to the stage, but rather to the Main object, which is a MovieClip or Sprite based object that is itself attached to the stage automatically when you set it as the Document class. Hope this info helps.
Update
To explain the display list a little more:
Think of all your movieclips as dishes, and the stage as the table. You can only access the table from the dish, if the dish is placed directly on the table, or if a dish is stacked on top of another dish that touches the table. If you have 10 plates stacked on top of each other, they all touch the table eventually, via their connection to each other. This is essentially a visualization of the flash display list. The way you put dishes on the table is by using addChild(dish). If you have not placed an object somewhere on the table, and try to access the table from that object, you're going to fail. You're getting the "access to undefined" error because you're calling the "blah()" method, which accesses the stage (table) before the bullet (dish) has been added to the stage (table). So you must first either directly add the bullet to the stage, or add it to another object that has already been added to the stage. Change your code like so:
var myBullet:Bullet = new Bullet();
stage.addChild(myBullet);
//Or, if this class, assuming it's the player class, has already been added to the stage, just do this:
this.addChild(myBullet);
myBullet.blah();
Even so, you should still have some error checking within your "blah" method to ensure that the stage is available:
function blah(){
var someSprite = new someSprite();
if(stage){
Main.addChild(someSprite);
stage.addChild(someSprite);
root.addChild(someSprite);
}else{
trace("error, stage not present");
}
}
However you should also note that by adding this child to Main, then stage, then root all in sequence, this does not duplicate the someSprite object. When you add a display object to a new parent object, the object is automatically pulled from it's current parent and moved to the new one. So all this code will do is eventually add someSprite to root, which I believe will fail because root is not a display object, but rather a global reference mainly used to access global objects such as the stage and the Loader object used to load the SWF.
You shouldn't ever be calling stage.addChild. There should be only one child of the Stage, and that's the document class.
You make a MovieClip display on the screen by adding it to the stage's display list.
Stage
+ Main Timeline
+Anything
+Else
+You
+Want
So assuming that Main is your document class for the main timeline...
// inside of Main's constructor...
public function Main(){
var anything:MovieClip = new MovieClip();
var Else:TextField = new TextField();
var you:SimpleButton = new SimpleButton();
var want:Sprite = new Sprite();
this.addChild(anything);
this.addChild(Else);
this.addChild(you);
this.addChild(want);
}
Then in order to add children even lower, for example if you want something to be a child of "Anything" such that you have....
Stage
+ Main Timeline
+Anything
+And
+Everything
+Else
+You
+Want
public function Main(){
var anything:MovieClip = new MovieClip();
var Else:TextField = new TextField();
var you:SimpleButton = new SimpleButton();
var want:Sprite = new Sprite();
this.addChild(anything);
this.addChild(Else);
this.addChild(you);
this.addChild(want);
var And:Sprite = new Sprite();
var everything:Sprite = new Sprite();
anything.addChild(And);
anything.addChild(everything);
}
EDIT: Ascension Systems asks why you should never add any display object directly as a child of the stage. The simplest answer is that you can't ever guarantee that what you believe you're creating as a document class, or as a main timeline in fact actually is going to be used as such. Your use of the stage may later preclude your swf from being loaded as a child of a larger application depending on what it is you've done, exactly. Relying directly on the stage can mean that you're making some assumptions about the nature of the display list that may not hold in the future. That's the way in which it breaks modularity (which is not the same as breaking oop).
Why add to the stage when you could just create your entire application as a MovieClip that is completely self-contained with no reliance on the concept of a "stage" beyond that which is required for learning world coordinates? That way you can be much more modular in your design and you sacrifice nothing.
In some people's work this may be considered an edge case. In my work this has happened both to me when I've created applications that I thought at the time were purely stand-alone that ended up being repurposed later to be a module, and also to swfs that other people created that were intended to be strictly stand-alone, but that I was then to integrate as a module into a larger application. In all cases there were some nasty side effects to contend with. That's where I learned not to rely too closely on the stage for much beyond world coordinates.
Every display object has a property called stage, which is null until that object is added to the display tree.
When you are unsure if an object has been added to the stage, there is a listener you can employ for that purpose:
public class Main extends MovieClip
{
import flash.events.Event;
public function Main():void
{
if(stage) {
init();
} else {
this.addEventListener(Event.ADDED_TO_STAGE,init);
}
}
private function init(evt:Event = null):void
{
this.removeEventListener(Event.ADDED_TO_STAGE,init);
//object is now definitely on the display tree
}
}
I'm gonna take a wild stab in the dark here.
stage is a property implemented something like so:
public function get stage():Stage {
var s:DisplayObject = this;
while(s.parent) s = s.parent;
return s as Stage;
}
root is very similar but stops a level below stage (root is a child of stage).
These properties only work when the object you're calling them on is on the stage somewhere. Doesn't matter where, because the while loop will walk up the hierarchy to get to the stage node at the top. But if it's not on the stage, then parent will be null.
So if your movieclip is not on the stage, then its reference to stage will be null. Same goes for root.
I'm guessing that you're calling blah before the bullets are added to the stage? In which case your call stage.addChild(someSprite) will be a Null Reference error (stage is null).
So you either need to add the bullets to stage first, or you need to pass stage in as a parameter:
function blah(s:Stage){
var someSprite = new someSprite();
s.addChild(someSprite);
}

ActionScript 3 stage question HELP!

I have a 1.swf loads up a another 2.swf, while i doing things inside 2.swf like
1.swf is bigger
2.swf is smaller inside
stage.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
it keep refer to the parent's stage instead the 2.swf's stage.
Please help out. thanks
There is only one stage object, and it is always inherited from the root. Anytime you can access the property "stage" inside a DisplayObject (Sprite,MovieClip) it's actually just a reference to the root.stage that gets populated whenever that DisplayObject is added to the root stage, or a child of some object that eventually connects to the stage (the display list). The display list is just an object tree of various display objects that are connected to the root stage. Anyway so, about your question, if you just want to listen for events on your loaded swf do something like this:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load(new URLRequest("swf2.swf"));
private function loadComplete(e:Event):void
{
var swf2Clip:MovieClip = loader.content as MovieClip;
swfClip.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
}
private function onMouseOut(e:MouseEvent):void
{
//Do something when swf2 is moused out.
}