So, I've been working on a project in AS3 and I've come across yet another peculiar bit of behavior.
Background: It's a turned based game. I've been optimizing it for the past week and now it runs like butter (consistently!);
Issue: However, when I try to continue playing from a saved game, it runs less consistently. Specifically, it will run the first few turns normally, then it will begin to degrade in performance dramatically until it freezes my computer. Please note, this only happens during battle, not during the menus or during any other time.
Is there something regarding Actionscript that I'm missing? I'm saving the game with cookies using the built in SharedObject class. The code I'm using to save and load the data is below (I also use the byte array class).
public static function saveGame():void
{
/// save the game using byte array
registerClassAlias("Mob", Mob);
registerClassAlias("Skill", Skill);
var ba:ByteArray = new ByteArray();
var savedData:* = Main.glblPlayer.setSaveObject();
ba.writeObject(savedData);
ba.position = 0;
so.data.game5 = ba;
so.flush();
}
public static function loadGame():Boolean
{
if (so.data.hasOwnProperty("game5"))
{
var ba:ByteArray = new ByteArray();
ba = so.data.game5;
ba.position = 0;
var loadedData:Object = ba.readObject();
glblPlayer.loadSaveObject( loadedData );
return true;
}
else
{
so.data.game = new Object();
return false;
}
}
I just double checked the above code and tested it a bit more with some variation. If it's loaded 1-3 times, it's fine, but after that the performance degrades during battle with each turn. I have no idea how the technical stuff of ActionScript works or how it saves resources aside from that it's cookies and it's in the cache.
Can anyone shed some light on this by maybe going a bit more into how saving/loading with flash games are done in AS3? Or is "use sharedObject" all there is?
Use a profiler (like Adobe scout) to see what causes the problem.
If I had to guess, it's because your (de-)serialisation routines do not work properly and have a memory leak. But again arguing about this or looking at the code wondering what might be the problem is a pointless endeavour. Use a profiler to see exactly what the problem is.
Related
How can I patch actionscript without constantly rebuilding sfw?
There is a fairly large actionscript project that I need to modify and resulting swf is used on a live site. The problem I have is that I need to make quick small updates to the swf and it's not acceptable to update the swf on live site ten time a day (I don't control that part, I need to ask another person to put the result on live site).
What options do I have to workaround that issue? I'm a complete noob when it comes to actionscript and all flash related stuff and I'm not even sure what is possible and what isn't. I'm thinking about the following approaches, which ones are possible/acceptable?
Imagine that live site is on www.livesite.com/game.html and this page loads www.livesite.com/flashgame.swf. In that flashgame.swf among many others there is a class com/livesite/Magic.as that gets instantiated and instance of that class has a member variable xxx123 of class com/livesite/MagicWork.as. I only need to modify this MagicWork class. Now, I simply modify it, build and ask to put updated flashgame.swf live. So, I want to avoid that manual step.
All my ideas can be split in two basic approaches: 1) keep flashgame.swf totally unmodified and then load flashgame.mod.swf that contains alternative implementation of that MagicWork class, then using javascript access internals of instance of that Magic class and update its xxx123 member to be an instance of MagicWork class from flashgame.mode.swf. I'd need to modify game.html to load my javascript so that my js file would load flashgame.mod.swf and patch code inside flashgame.swf. By patching I mean javascript-style overwriting of Magic.xxx123 to a new value. flashgame.mode.swf would ideally reside on my own host that I control. Is that kind of stuff possible, if not what's not possible?
2) I could make one-time change in flashgame.swf so that it would effectively load itself my own code at runtime and patch it's xxx123 member. Is that possible?
I had already written a note about loading runtime shared libraries previously. I'll put the most essential parts of the process here, and add a link to the full article at the end.
You need to tag your main application entry point in the following manner.
[Frame(factoryClass="Preloader")]
public class Main extends Sprite
{
}
Then create a class called Preloader.
public class Preloader
{
public function Preloader()
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.loader_completeHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, this.loader_ioErrorHandler);
var request:URLRequest = new URLRequest("math.swf");
var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
loader.load(request, context);
}
private function loader_completeHandler(event:Event):void
{
var mainClass:Class = getDefinitionByName("Main") as Class;
var mainInstance:Main = new mainClass();
this.addChild(mainInstance);
}
}
The full implementation of the Main class is like this.
[Frame(factoryClass="Preloader")]
public function Main()
{
var integer:IntegerArithmetic = new IntegerArithmetic(); // Type declared in math.swf
var operand1:int = 10;
var operand2:int = 10;
var result:int = integer.add(operand1, operand2);
}
Deploying Runtime Shared Libraries
The confusing bit about using a runtime shared library is realizing that the SWF has to be extracted from the SWC at the time of deploying the application. This was not immediately obvious and I ended up spending days placing a compiled SWC file in various locations and wondering why the application was unable to load it at runtime. An obscure article on the Adobe website made explicit this particular step and set things straight.
The full article along with the same example is available at http://www.notadesigner.com/runtime-shared-libraries-with-plain-actionscript/.
I was working on my flash project, which compiled the whole time just fine.
Then I did some changes, then, when testing the project, the project compiles,
the flash player comes up, showing the first frame, but no code executes.
No single trace output. Nothing. No Errors, No Warnings, very strange!!!
Can anybody help me???
You might have stumbled over a BUG in the FlashIDE/Flex Compiler.
Try this snippet and wonder:
// ------ Put in first frame of a fresh flash file
trace ("why won't i execute");
var dummy=function(a:int){
a:int = 0;
}
Instead of catching your coding mistake:
a:int = 0; should eihter be var a:int = 0; or a=0;
the compiler chokes, and dies, without even having the time to let you know.
Beware!
This mistake can be deeply hidden in some, over many intermediate classes imported, class.
So, to answer your question, look at the things you have changed, you might have changed a local var to be given as a function argument, took away the local var but forgot to remove the :type part as well.
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.
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
I've done it this way, but Adobe Air hangs for several seconds.
private function test():void
{
fileStream = new FileStream();
fileStream.addEventListener(IOErrorEvent.IO_ERROR, fileError);
fileStream.addEventListener(Event.COMPLETE, opened);
fileStream.openAsync(filePath, FileMode.READ);
}
protected function opened(event:Event):void
{
var bytes:ByteArray = new ByteArray();
fileStream.readBytes(bytes);
fileStream.close();
// MD5Stream from package com.adobe.crypto.MD5Stream https://github.com/mikechambers/as3corelib/blob/master/src/com/adobe/crypto/MD5Stream.as
var md5stream:MD5Stream = new MD5Stream;
trace(md5stream.complete(bytes)); // md5
}
How to make the process of getting md5 without hanging?
Try using Bloody's MD5 implementation. It's apparently a lot faster.
While it will speed up the hash calculation, perhaps even adequately, you're not really solving the underlying problem, which is that you want a non-blocking operation in a single threaded application model. In Flash/AIR, this is generally done by breaking the work up into smaller chunks, and doing only one chunk's worth of processing each frame, instead of all at once during one frame. There's even a cool framework to simplify this!
I noticed that the library you're currently using, MD5Stream, is built for incremental updates -- so you can easily feed it little chunks of the file each frame until the entire file is processed. This will allow the frame rate to stay relatively constant while the hash is computed.