Save & load State AS3 + Adobe Air - actionscript-3

How do I save and load using Adobe Air? This has always been something I need to know.I've asked this question before, but I've been given what to use but not how to use it.Say I save state in frame 59, I shut down and end the app.Then when I hit load, how do I get it to go back to frame 59? Please please help!

1.Always write your current frame to a sharedObject or file
ex. onEnterFrame(ev:Event){ //write current frame somewhere }
2.When App starte first check the file or shared object for last saved frame number. If not found by default it is the first frame, else gotoAndPlay(frame_saved);
Sorry but on this forums no one will write the app for you. you have to work yourself more to finish the solution.

Assuming you have just the root timeline, and you don't repeat the first frame, then on the first frame of your application use this code:
//the shared object to store the state
var stateSO:SharedObject = SharedObject.getLocal("saveState");
if (stateSO.size > 0 && stateSO.data.lastFrame && stateSO.data.lastFrame != undefined) {
//the shared object exists and has a valid value, so lets skip to that frame
gotoAndPlay(stateSO.data.lastFrame);
}
//this function runs every frame
function saveState(e:Event = null):void {
stateSO.data.lastFrame = this.currentFrame; //assign the current frame number to the shared object
stateSO.flush(); //save the cookie (shared object)
}
addEventListener(Event.ENTER_FRAME, saveState);

Related

SharedObject appears to be caching

I have a slightly unusual situation with SharedObject:
The situation: where we have a SWF (browser) based application running on a single local machine. The SWF accesses local content and is reloaded every XX number of seconds/minutes/hours.
The state of application has to be stored within a single SharedObject (this is using the '/' parameter to force it to global) in a JSON style.
The SWF loads the SO before it makes any state updates and correctly calls flush() immediately after to save state.
The Problem: Everything runs fine BUT occasionally there is a situation where by 2 instances of the same SWF are existing at the same in different instances and both are accessing the same SharedObject.
The 2nd has to take control of the SO from the 1st, by each SWF instance setting it's instance state to an incremented number (SWF Idx) stored in the SO.
Both are loading the file before any update is made, version number checked, and will disable themselves if the saved SWF Idx is above it's own. Unfortunately the 1st SWF instance somehow isn't loading the latest version of the SharedObject because it traces out the original number (e.g. 22) instead of the now updated one (e.g. 23). While the 2nd SWF is tracing out 23 and the SO contains 23.
Is there any way that the browser could possibly be caching the SO?
Testing
I'm currently testing the situation by running one browser instance locally then launching a 2nd. Making copies of the SO before and after each state.
I've also run the 1st and 2nd via IntelliJ and can see that SharedObject.getLocal is being called and checked each time.
I've included the basics of the code I'm using below where:
__so = is the public SharedObject variable
_thisSWFIdx = is the private variable inside AS3 storing the current instances SWF Index
__so.data.activeSWFIdx = the latest SWF Index
The SO 'get' I'm using is:
SharedObject.getLocal(_SO_ID, "/");
The check i'm doing is:
if (_thisSWFIdx < __so.data.activeSWFIdx) {
__amActiveSWF = false;
}
The saving of the variable to SO:
__so.data.activeSWFIdx = _thisSWFIdx;
flush();
Additonal info:
This is running on both mac & windows
FlashPlayer 10.3
Pure AS3
compiled in IntelliJ
same issue in FF, Chrome and IE
primary machine Macbook
I can't find anything within the documentation or existing threads so any help will be much appreciated.
After a little test, I remarked the behavior that you've mentioned : after updating the SharedObject by a second SWF, the first one can't get the new value only after a reload.
To avoid that, I think, as a workaround, you can use a second SWF which will just work on the SharedObject by reading / writing data.
For that, take this example :
so.swf : the SWF which will read and write data.
var shared_object_name:String = 'so';
var shared_object:SharedObject = SharedObject.getLocal(shared_object_name, '/');
// get the id
function get_id(): int {
return shared_object.data.id ? shared_object.data.id : -1;
}
// set the id
function set_id(new_id:int): void {
shared_object.data.id = new_id;
shared_object.flush();
}
app.swf : your global app which will use the "so.swf" to do SharedObject's operations :
var init:Boolean = true;
var swf_id:int = 1;
var swf_disabled:Boolean = false;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, on_SWFLoad);
loader.load(new URLRequest('so.swf'));
function on_SWFLoad(e:Event): void
{
var loaded_swf:MovieClip = MovieClip(loader.content);
var current_id:int = loaded_swf.get_id();
// if it's initial run
if(init){
init = false;
if(current_id > 0){
swf_id = current_id + 1;
}
loaded_swf.set_id(swf_id);
} else { // we are verifying if there is a new active SWF
if (swf_id <= current_id) {
swf_disabled = true;
}
}
}
function stage_onPress(e:MouseEvent){
// if this SWF is disabled, stop getting the "id"
if(swf_disabled){
stage.removeEventListener(MouseEvent.CLICK, stage_onPress);
return;
}
// otherwise, get the "id" to verify if there is another active SWF
loader.load(new URLRequest('so.swf'));
}
stage.addEventListener(MouseEvent.CLICK, stage_onPress);
I used MouseEvent.CLICK only for testing purposes, of course you have to use a Timer or something else to verify every n seconds (for example) that you have another active SWF to disable the current one...
Hope that can help.

save load error 1010 array objects with sharedObjects as3

i cant figure out this one so maby you guys can help me out.
i store some data in the form of a array filled with Objects in this example cards.
in my main class i have the following code:
deckSprite.savedData = SharedObject.getLocal("cardsdata");
deckSprite.savedData.data.savedArray = deckSprite.deckArr;
deckSprite.savedData.flush();
trace(deckSprite.savedData.data.savedArray);
the trace will output something like [object card1, object card2, object card3]
now in a static class called "deckSprite" i have this:
savedData = sharedObject.getLocal("cardsdata");
if (savedData.data.savedArray == undefined)
{
trace("no save yet");
}
{
else
{
trace("save loaded");
deckArr = savedData.data.savedArray;
trace(savedData.data.savedArray);
now my trace data turns out only ", ," (somehow the cards are gone).
now after i got saved data i restart my application and whenever he tryes to acces the deckArr it crashes giving me the error "A term is undefined and has no properties".
how is it possible that when i save the array it saves all the cards inside the array and when i restart the application its suddenly only ",,"but the cards are gone?
When serializing objects in AS3, you have to register their class using registerClassAlias() from package flash.net. So you have to call something like
registerClassAlias('com.example.deck', Deck)
in the program before any saving or loading happens.
See full reference at AS3 API Reference
NOTE: As pointed out by #BadFeelingAboutThis in comments, you have to register all referenced class in your Deck, i.e. if your deck looks like this:
class Deck {
var firstCard:Card;
var type:DeckType;
}
to be able to save Deck to SharedObject you have to call
registerClassAlias('com.example.deck', Deck);
registerClassAlias('com.example.card', Card);
registerClassAlias('com.example.decktype', DeckType);
before any saving/loading is done.
EDIT
Everything depends on content of your array
If I assume, your deckSprite is declared like this:
var deck1:Deck = new Deck();
var deck2:Deck = new Deck();
var deckArr:Array = new Array(deck1, deck2);
var deckSprite:DeckSprite = new DeckSprite()
deckSprite.setDeckArr(deckArr);
then before adding deckArray to SharedObject, you have to call registerClassAlias(). Sou your save code will look like this:
registerClassAlias('com.example.deck', Deck);
deckSprite.savedData = SharedObject.getLocal("cardsdata");
deckSprite.savedData.data.savedArray = deckSprite.deckArr;
deckSprite.savedData.flush();
trace(deckSprite.savedData.data.savedArray);
(replace the Deck with actual class you use for representing your decks)
Similarly the first line has to be called before you do any loading.
Of course it is best not to repeat yourself, so you should call registerClassAlias('com.example.deck', Deck); only once in your program, so for example in some init() method in your main class, if you have something like that.

AS3 Accessing frame without using gotoAndStop

Is there a way to access the image data (children/transformation info) of a particular frame in a MovieClip without having to use the gotoAndStop methods.
These methods are part of a rendering pipeline, all I want is access to the data, not to start a chain of asynchronous events that were developed to render things on screen, call multiple event listeners and execute frame actions.
Not only can you not do that, but gotoAndStop() doesn't even immediately make that data available. The contents of a frame aren't code accessible until the FRAME_CONSTRUCTED Event is dispatched when that frame is reached, so what you would actually have to do is more like:
var lastFrame:int = currentFrame;
function ready(e:Event):void
{
if(currentFrame !== lastFrame)
{
// In this example, frame 15 is where some image
// data we want is.
if(currentFrame === 15)
{
// Get image data.
//
}
lastFrame = currentFrame;
}
}
addEventListener(Event.FRAME_CONSTRUCTED, ready);
Needless to say; storing data you need across frames is not a viable way to structure an application.

AS3 Loading multiple external SWFs and them closing them

I am trying to load multiple external SWFs in one main SWF.
I start with the main swf. Now I need to link 4 games to it.
I have created 4 different loaders and I make it load each game into a different frame for each game. Ie when I go to frame 1 for example it will load game1. The code I am using for each game frame is:
public function LoadGame1(evt:MouseEvent):void {
trace("load game1");
this.gotoAndPlay("Game1");
var url:URLRequest = new URLRequest("game1.swf");
game1Loader.load(url);
addChild(game1Loader);
}
game 2 is the same with the exception of game1 it will be game2 etc.
Now my issue is when I close each game and go to another it does not work. I can not load another game or reload it. When I go back to the menu I use:
public function backFunc(evt:MouseEvent):void {
trace("menu");
myLoader.unload();
game1Loader.unload();
game2Loader.unload();
game3Loader.unload();
game4Loader.unload();
this.gotoAndPlay("Menu");
}
I'm assuming it has something to do with the unloading - it unloads it all and closes the loader. Ive tried removeChild(game1Loader) etc but it doesn't work? The game will close and it will go back to the menu but then I will not be able to get back into the game or load another one?
Please help :(
Flash timelines are kind of like a static state machine; moving from frame-to-frame will run all of the document code at that frame (every time). It also resets the value of the content to the state it was in during design time (so, frame = design + code). Because of the headaches this model can cause, I highly recommend you do all of your design & code in a single frame.
The way you've written your sample code, it seems to imply that you're writing custom functions for each of your game loaders. You can unify that by driving all the btn events through the same function and depending on some differentiating property (like their names), load the appropriate game.
Finally, I suspect the reason your loaders stop working is because you've unloaded and removed them from the stage. That stuff gets garbage collected. Rather than making the loader a global object, feel free to create a new one each time you make your loadGame() call.
// We'll store our buttons in an array to make it easier to register for events.
var btns:Array = [
btn_Load1,
btn_Load2,
btn_Load3
]
// Now cycle over them in bind to our listener
for each (var btn:MovieClip in btns) {
btn.addEventListener("mouseUp", btnEvents);
}
function btnEvents(e:MouseEvent):void {
// With one event listener, we can sort out each condition
switch (e.currentTarget.name) {
case "btn_Load1":
loadGame("game1.swf")
break;
case "btn_Load2":
loadGame("game2.swf")
break;
case "btn_Load3":
loadGame("game3.swf")
break;
}
}
function loadGame(url:String):void {
// Now we only have to write our loader code once (for all buttons)
trace("Loading " + url);
// Because we're unloading our swfs (and unloaded assets get garbage collected),
// we'll want to make a new loader each time we call a load op
var loader:Loader = new Loader();
loader.name = "current_game";
loader.load(new URLRequest(url));
addChild(loader);
}
function backFunc(evt:MouseEvent):void {
// Dynamically find our current game, and unload it.
trace("menu");
getChildByName("current_game").unload();
}

Removing Children in AS3

My flash game exists of a timeline with multiple frames (I know I should avoid the timeline)
The point of the game is a point and click adventure. The object that you are able to pick up get spawned and destroyed accordingly as you enter and leave the room. now my problem is when entering frame 14 (accessibel from frame 12) it creates a piece of paper which you are able to pick up if you have another item. Now my problem is when you can't or don't pick up the paper and go back to frame 12 (only exit is to frame 12), you can't click on any other object and you are basicly stuck on frame 12. When leaving and entering other rooms it works properly but for some reason it doesn't for on the paper on frame 14.
My code to remove objects works as following
In my Main.as Documentclass I have a function that called as soon as the game starts which does the following
if (lastframe == 14)
{
trace (prop.numChildren);
while (prop.numChildren )
{
prop.removeChildAt(0);
}
}
The lastframe variable is established when moving from frames
this function is found on the frame itself (each exit function on it's own respective frame)
function exitKantine(event:MouseEvent):void
{
Main.lastframe = 14;
gotoAndStop(12);
}
The function to remove the prop actually removes it but then causes all other clickable objects to be unusable.
Thanks for looking at my question and thanks in advance for your suggestions
I would say instead of removing children, add it once in the beginning, add all the listeners in the beginning, and toggle the visibility instead of trying to addChild and removeChild every time you want to hide it. Use an array so you can have a few happening at the same time.
something like this:
private function init():void
{
assignVars();
addListeners();
stage.addChild // make sure this is in document class or you are passing stage to the class using it
}
for (var i = 0; i < _thingsAry.length; i++)
{
if (_thingsAry[i] == 14)
{
_thingsAry[i].visible = false;
trace("the visibility of _thingsAry[" + i + "] is " + _thingsAry[i].visible
}
}