SWFLoader doens't work twice - actionscript-3

I have a game that dynamicaly loads another minigame.
I load it using SWFLoader:
function loadMinigame(minigameName:String):void
{
var loader:SWFLoader = new SWFLoader;
loader.load("/Content/Swf/" + minigameName + ".swf");
layerMinigames.addElement(loader);
}
The first time I load it works nice. But when I exit the minigame and try to open it again via same method it doens't apper in my application (sometimes do sometimes doens't).
Both games are Flex application.
What's going on?
Update
I did loader.loadForCompatibility = true and now it loads properly! But now I'm getting an error when I try to cast the result:
loader.addEventListener(Event.COMPLETE, function(e:Event):void
{
var sys:SystemManager = SystemManager(e.currentTarget.content);
});
gives:
TypeError: Error #1034: Type Coercion failed: cannot convert
_AppMinigame1_mx_managers_SystemManager#14c97eb9 to mx.managers.SystemManager.

You'd better cache the loaded minigame after loading, then just display it again when needed. Make sure though, that the minigame's ADDED_TO_STAGE listener resets the minigame properly.
static var loadedGames:Object={};
function loadMinigame(minigameName:String):void
{
if (loadedGames[minigameName]==null) {
var loader:SWFLoader = new SWFLoader;
loader.load("/Content/Swf/" + minigameName + ".swf");
loadedGames[minigameName]=loader;
loader.addEventListener(Event.COMPLETE,loaderFinished);
} else {
layerMinigames.addElement(loadedGames[minigameName]);
}
}
function loaderFinished(e:Event):void {
layerMinigames.addElement(e.target);
// do the rest, like setting proper start time for minigame, etc
}
UPDATE: Since you run out of memory while running such a process, this means that the minigame's instance holds onto too much memory, and needs to be freed (GC'd) in order to load another minigame. Thus, caching loading SWFs is out of question. You can release an SWF using unloadAndStop() call against it, prior to doing this, make sure that your minigame is removed from display list, and its REMOVED_FROM_STAGE listener shuts down every listener attached to stage. Also you might need to debug your minigame SWF to locate probable memory leaks, as your SWFs share the same memory space, and a memory leak in one of them nullifies your efforts to do anything in all the others.

Related

as3 air unloadAndStop() event listener or timer

my air application plays an external swf over and over until time to play the next external swf. in order to avoid memory leaks i am using unloadAndStop(). i am using two timers. the first unloadAndStops the swf. the second waits two seconds then loads it back up again.
this approach (coupled with the use of weak references) seems to keep the memory in check. however, i'd rather not use timers but event listeners. is there an event listener for when unloadAndStop completes to then load the swf again.
here is what i had in mind:
var TIMER_INTERVAL:int = int(duration);
var t:Timer = new Timer(TIMER_INTERVAL);
t.addEventListener(TimerEvent.TIMER,updateTimer,false,0,true);
t.start();
private function updateTimer(e:TimerEvent):void
{
swfLoader.unloadAndStop(true);
swfLoader.addEventListener(Event.UNLOAD,onSWFUnloadComplete,false,0,true);
}
private function updateTimer(e:TimerEvent):void
{
var swfSource2:String = File.applicationStorageDirectory.nativePath.toString();
swfLoader.load(swfSource2+'\\'+name_xml);
}
unloadAndStop is not an asynchronous method, so an unload event wouldn't really be relevant. What is likely happening behind the scenes is that it takes 1 full frame to fully dispose of the movies objects/listeners and that's why you're having issues loading it again in the same block of code.
If you wait just one frame before loading it again, you should have the results you expect.
Now, of course the best solution is go into the source file of your loaded swf and fix the memory leaks.

How do I completely remove a loaded swf and reload it? (Trying to restart a game)

I have a preloader that loads a swf, the swf creates a bunch of listeners, objects, and movie clips. I want all of these to be destroyed and recreated.
Simplified version of my preloader:
var request:URLRequest = new URLRequest("myfile.swf");
var myLoader:Loader = new Loader();
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, function(event){
stage.addChild(myLoader);
myLoader.loadBytes(urlLoader.data);
});
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.load(request);
When I try to remove it, I do this:
stage.removeChild(myLoader);
var child = loader.myLoader.content as Object;
SoundMixer.stopAll();
while(stage.numChildren > 0){
stage.removeChildAt(0);
}
child.stop();
while(stage.numChildren > 0){
stage.removeChildAt(0);
}
child=null;
System.gc();
myLoader.unloadAndStop(true);
System.gc();
myLoader.unload();
System.gc();
myLoader.loadBytes(urlLoader.data);
stage.addChild(loader.myLoader);
In your loaded SWF you may create a method 'destroy' which would remove all listeners, destroy all objects and reset all data.
You can call this method either from the parent object (if the method is public) or you can call destroy when you remove the SWF from stage (Event.REMOVED_FROM_STAGE)
You can reload swf with js, this is the easy way to do this. you can check this answer.
Or you have to do a good object and listener managment and reset game in swf file. This might be complex as project get bigger. You need release methods that removes all references and listener.
first you should load your swf using the Loader Class like this :
var movie:MovieClip
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load( new URLRequest("myfile.swf"));
function loadComplete(event:Event):void
{
movie = loader.content as MovieCLip;
addChild(movie);
}
//when reloading just remove the movie object from the stage
removeChild(movie);
SoundMixer.stopAll();
//......
//.
//.
I know this is old, but I stumbled on it for reference and see some things that I think should be mentioned.
System.gc() - while it's a function you have access to, everything I've seen suggests that the garbage collector should never be called by your code unless you've used a considerable amount of memory and need it back immediately. The garbage collector usually has a better idea of when it should run than you do (Force Garbage Collection in AS3?). It can impact performance for not just your application but all flash applications running in that browser or container.
There's also a good bit of people struggling to make effective use of unloadAndStop(), as it seems to be rather unreliable in various contexts (Unloading swf using the unloadAndStop() method, but video sounds remain audible).
To be thorough, I'd strongly suggest putting the effort into simply eliminating everything as it would be in any other language from within the loaded flash object first before removing the flash object itself, not expecting the container to take care of it for you. This is why Flash has a reputation for memory leaks to begin with.
Inside the loaded swf, add code that fires on unload.
One way is to add this to the swf:
this.addEventListener(Event.REMOVED_FROM_STAGE, doUnload)
Then have the doUnload functon perform the following:
Use removeEventListener to effectively terminate the event handlers. You could either recall all the addEventListeners you created, or cook up a routine to walk through and remove them all by traversing the objects in play. The first option is more efficient since it's exclusive, but the second option would theoretically be something you could memorize and copy between applications.
Use delete() on variables to make them removable by the garbage collector. NOTE: Unlike many languages, a referenced variable will not be removable unless the reference is also removed. For example:
var a:int = 0;
var b:int = a;
delete(a); // b still exists and equals 0, a is not available to GC.
delete(b); // now both variables can be removed by GC.
There's also still confusion as to whether it's a good idea to use removeChildAt(0) or remove the child objects individually. The first has the benefit of being distinctly simple and straight-forward, but that also gives it the caveat of not being entirely understood by the coder, and possibly missing something similarly to unloadAndStop(). Not as much as walking your object tree with removeChild and eliminating things explicitly without question or uncertainty.
After this is set, adding a function to unload the flash object will trigger its self-removal, so the loader will remain simple and neat, and reload the flash cleanly.

Loading an XML in ActionScript 3 before an event listener happens

I'm trying to fill some data from an xml feed we made into my Flash movie. The main action of this is in the constructor for MediaElementJS's .as file.
Anyway, the main problem is that I keep reading there is no way to load a URL synchronously in AS3 (which i still find hard to believe). This constructor calls both parse('url') and a function addEventListener(EVENT.ADDED_TO_STAGE, initializeAds);
Now the ads need info from the XML but the XML aint ready yet. I tried to call the ads at the end of the XML parser when I knew it would be prepped but it messes them up and the ad values never change from their defaults..
Oh wherefor art thou actionscript locking mechanism..
So is there anyway to preload data from a URL?
CODE:
public function LoadXML(e:Event):void
{
removeEventListener(Event.COMPLETE, LoadXML);
var xmlData:XML = new XML(e.target.data);
episodeData.pre.type = xmlData.episode.pre.ad.#type;
episodeData.pre.url = xmlData.episode.pre.ad.text();
episodeData.video = Number(xmlData.episode.video.#id); /*************** I CAN'T REMEMBER ***********/
episodeData.pageTitle = xmlData.episode.video.#pagetitle;
episodeData.title = xmlData.episode.video.#title;
episodeData.source = xmlData.episode.video.source.text();
episodeData.post.type=xmlData.episode.post.ad.#type;
episodeData.post.url=xmlData.episode.post.ad.text();
episodeData.nextEpisode=xmlData.episode.post.nextepisode.text(); //if not empty redirect to this
xmlLoading = false;
//THIS IS WHERE I TRIED TO CALL THE FUNCTION I NEED TO LOAD LATER
}
public function parse()
{
var xmlLoader:URLLoader = new URLLoader();
//var xmlData:XML = new XML();
xmlLoader.load(new URLRequest(rootURL + '/episode.aspx?associd=' + _episode));
//xmlLoader.addEventListener(Event.COMPLETE, processXML);
xmlLoader.addEventListener(Event.COMPLETE, LoadXML);
}
I've tried it with a static URL address and whatnot of course but no dice.
The code in the constructor works if I dynamically assign a static value but if I try chaining to events together to get the dynamic value and dynamic assignment it crunches.
In the constructor, definitely runs both by themselves:
parse();
// Google IMA EventListener
addEventListener(Event.ADDED_TO_STAGE, initialize);
Loading URLs is always asynchronous, so add the event listener in the response function for the URL loader.
Now your question sounds like you tried that but had some problem, so post that code and let us take a look.
Edit START
When I have multiple asynchronous calls that happen and I need something to happen after both of them are done I usually use booleans to store if each one has happened yet, then in a third function they both call I check both the booleans.
Here's how I'd do that:
protected function viewnavigatorapplication1_preinitializeHandler(event:FlexEvent):void
{
var loader1:Loader = new Loader();
var loader2:Loader = new Loader();
loader1.addEventListener(Event.COMPLETE, loader1_completeHandler);
loader1.load(new URLRequest("http://www.whitehouse.gov"));
loader2.addEventListener(Event.COMPLETE, loader2_completeHandler);
loader2.load(new URLRequest("http://www.nasa.gov"));
}
private function loader1_completeHandler():void
{
loader1Done = true;
//Maybe do some stuff here
moveOn();
}
private function loader2_completeHandler():void
{
loader2Done=true;
//Maybe do some stuff here
moveOn();
}
private function moveOn():void
{
if(!loader1Done||!loader2Done)
return;
//Do whatever needs to be done once both asynchronous events have completed
}
If this isn't your problem I think you need to provide more of the code in place of the comments that indicate other things happen, because it is a bit unclear.
For example I'm not sure what you mean by "The code in the constructor works if I dynamically assign a static value but if I try chaining to events together to get the dynamic value and dynamic assignment it crunches." Also since there's no example of the data or what the rootURL is there's no way to debug from here to understand what's going wrong.
Since there's no error we would need to be able to re-compile some portion of your code locally to give any better feedback.
Edit END
Blocking or synchronous calls are a horrible idea with regard to network communications due to the lack of reliability of networks and/or servers. If a front end application locked up to wait for a response before doing any other processing it would result in a horrible user experience, this is why there is no synchronous remote calls.
What happens with a synchronous call when the server bombs out, the client remains locked even though no response will result, the user can't interact with anything else in the front-end because it's waiting for said response which will never come? It's much better that remote calls of any sort are done in an asynchronous fashion, the same is true with local disk access in Java (or otherwise) where using asynchronous non-blocking calls is generally a better way to go to allow the other processes within an application to continue regardless of the state or use on the disk.
What you're doing should work just fine, you make a call to a remote service, it responds with some result and hits your "listener" or "callback" function then you want to do something with the results you can call another function, and the data is there.
It sounds to me like the only thing that's not happening is updates after the fact aren't being reflected in the UI, this is probably due to a lack of Bindable metadata/event dispatching for the properties. Have you inspected the result in the event that returns, have you put breakpoints in the method that is meant to be called after the data has returned? What you're doing is completely possible and it even looks like you have most of it right, but there's definitely something your doing wrong that's resulting in you not being able to make this work. If you can explain the behavior a bit clearer that will help, also what do you do here:
//THIS IS WHERE I TRIED TO CALL THE FUNCTION I NEED TO LOAD LATER

Flash debugger behaving differently from the player with AS3 and Events

Why this works on flash professional's debugger, but brings null on the compiled SWF?
var firstParameter:SomeObject = new SomeObject();
someLoader = new Loader();
someLoader.contentLoaderInfo.addEventListener(
Event.COMPLETE
, function(evt) {
onLoaded(evt, firstParameter);
}
, false
);
function onLoaded (evt:Event, param:SomeObject):void {
mcOnSceneForTracing.text = param; // this is used for SWF debugging
}
For the record:
To make it work without any issues this can be "solved" by creating a separate scope. However, here I'm wondering why, then, this example even works on the debugger at least.
And, please, if you have a better way other than using two anonymous functions to pass parameters, variables, values, whatever through an Event, do tell! I'm not willing to extend the Event, tho. Too 2005.
mcOnSceneForTracing is what I'm using to "trace" outside the debugger. Suggestions are also accepted here for better (and simpler) ways to do it! I've heard Vizzy is good, but haven't tried it yet.
My guess would be: When loading your resource from the debugger player, the operation finishes instantly, and thus firstParameter is available when your anonymous listener function is called, but when running the swf elsewhere, the load operation takes longer, and then the reference to firstParameter is lost, since it is a local variable.

ActionScript-3 timing issue / unwanted background multi-threading

I created a new class with private vars and public get properties.
When I create a new instance of the class, it loads text files content to the private vars - it probably takes a bit of time to load it.
After the new instance created, I try to get the value of the private var with the get property:
var item1:MyItem = new MyItem("0001");
trace(item1.ItemName);
Well, The output is blank.
The string that ItemName points to is not undefined, it contains data.
So, it's like a timing issue, and ActionScript is probably running the code using background multi-threading, so it's calling the trace command before it finished to run all methods in the MyItem c'tor (methods that load the text file data into the String var that ItemName points to).
Is there any way to force ActionScript avoid using this unwanted "background multi-threading", and run the code normally (by the order of the commands)?
I mean like "Don't run the 2nd command until you finished running the 1st one".
Thanks for any help..
Freddy
It's not multi-threading, loading a text file externally is just a completely asynchronous operation. Within your MyItem class, you need to have an Event.COMPLETE handler for that Loader. From there, there here is what I'd do:
Option 1: In MyItem's COMPLETE handler for the file load, set a flag. The class that uses the getter must check the flag via another getter to see if the data is there, and either use it (if it's there) or set up a listener to wait for it (if it's not). Once the data is loaded, you have immediate synchronous access to it (it's cached).
Option 2: An alternative to keeping the "isLoaded" flag within MyItem would be to have MyItem's Loader COMPLETE handler dispatch it's own TEXT_LOADED event. Then, the instance responsible for creating the MyItem instance would listen for that, and know not to ask for the contents of that text file until it was there.
Either approach will work. It is up to you to figure out which one makes sense. My first option potentially avoids an unnecessary listener if you don't expect to access the loaded data right away (i.e. you're loading the text at startup for use sometime later). The second approach makes sense if you do expect to make use of the text file's data the instant it loads.
Ideally what you'd want to do is implement the Observer Pattern:
Add an event handler to your MyItem class for the Loaded event of whatever component/class you're using to load your XML.
Add an event to your MyItem class that will bubble the Loaded event from Step 1 so that clients of your MyItem class can attach a handler to it. Fire this event inside the Loaded handler you created in Step 1.
Attach an event handler to the event you created in Step 2 and place your trace code in that handler.
Your code is in fact running in a single thread, however the typical data loading classes in Actionscript are asynchronous. It is likely that the data has not loaded before you call trace. To be notified of when the data is loaded you should use an event listener on the loading object.
var loader:URLLoader = new URLLoader();
loader.addEventListener( Event.COMPLETE, onLoadComplete );
loader.load( new URLRequest( 'path_to_data.xml' ) );
function onLoadComplete( event:Event ):void
{
trace( loader.data );
}
The AS3 API docs have a more complete example.