In AS3 calling loadBytes 2 times in sequence wont work - actionscript-3

While my code is pretty big and fragmented over many files to show here, i basically boils down to this:
I have a function which does this:
var loadedScript:Loader;
function load():void{
loadedScript = new Loader();
// loadedScript is initilized somewhere and other stuff
loadedScript.loadBytes(bytes, context);
loadedScript.contentLoaderInfo.addEventListener(Event.COMPLETE, scriptLoaded, false, 0, true);
}
when i call this function 2 times in a row in such a way that its called the 2. time before loadedScript.loadBytes(bytes, context); from the 1. time can finish, then the "scriptLoaded" method is called only for the 2. time, not the 1. time
So, is this intentional behaivor from the loadedScript.loadBytes(bytes, context); method, or a bug, cann i bypass this somehow?

you could ofcourse create a loader-queue, something like this:
var queue:Array = new Array();
function load(bytesToLoad:*):void {
queue.push(bytesToLoad);
processQueue();
}
function processQueue():void {
//check if items are on queue
if (queue.length > 0) {
//create a loader
var ldr:Loader = new Loader();
//add event for loading complete
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, scriptLoaded, false, 0, true);
//load the bytes (get first item from the queue, which contains the bytes to load)
ldr.loadBytes(queue.shift(), context);
}
}
function scriptLoaded():void {
//do stuff
//process next item in queue
processQueue();
}

Related

dispatch change value and start tween

I try to make simple scorebug. It’s feed from external XML file.
I want to start an animation when score changing, but i doesn’t found event listener or dispatcher to get change of value.
var myTimer:Timer = new Timer(100);
myTimer.addEventListener(TimerEvent.TIMER, timerListener);
myTimer.start();
function timerListener (e:TimerEvent):void
{
var myXMLLoader:URLLoader = new URLLoader();
myXMLLoader.load(new URLRequest("Xml.xml"));
myXMLLoader.addEventListener(Event.COMPLETE, processXML);
function processXML (e:Event):void
{
var myXML:XML = new XML(e.target.data);
ShotClock.text = myXML.timestamp;
GameClock.text = myXML.Clock;
HS.HomeScore.text = myXML.HomeScore;
AS.AwayScore.text = myXML.AwayScore;
Period.text = myXML.Period;
AwayTeam.text = myXML.AwayName;
HomeTeam.text = myXML.HomeName;
}
if ( (myXML.HomeScore).CHANGE )
{ var myTween:Tween = new Tween(HS.HomeScore, "alpha", Strong.easeIn, 0, 1, 1, true); }
}
You need to change the logic of loading. Instead of starting timed loadings, which won't be accurate 100 ms anyway, could lag, could fail, could arrive in order other than you issued them, etc., you need a single-thread asynchronous loop that does, simply, the following:
Start data loading.
(async pause)
Handle the loaded data.
Get XML data from loaded text.
Parse XML attributes.
Check if the score variable changed and trigger the things you want to.
Wait 100 ms.
(async pause)
Go to step №1.
Something like that:
var theLoader:URLLoader;
var theScore:Number = 0;
var theTimer:Timer;
// Start the tracking routine.
trackNext();
function trackNext():void
{
theLoader = new URLLoader;
theLoader.addEventListener(Event.COMPLETE, onXML);
// Error tracking is a must or the logic flow
// might just stop when you least expect it to.
theLoader.addEventListener(IOErrorEvent.IO_ERROR, onError, false, 0, true);
theLoader.load(new URLRequest("xml.xml"));
}
function onXML(e:Event):void
{
// Sanity check. There should be only one valid URLLoader instance.
if (e.target != theLoader)
{
return;
}
var X:XML;
try
{
X = new XML(theLoader.data);
}
catch (fail:Error)
{
// Processing the case where data is loaded successfully,
// but it is not a valid XML String.
onError(e);
return;
}
// Here's the place for your code
// that extracts data from XML.
// ...
// Get the new score value.
var aScore:Number = X.HomeScore;
// Compare it to previous value.
if (aScore != theScore)
{
// ATTENTION!!! THIS IS THE PLACE YOU ARE LOOKING FOR!!!
// Trigger things if the new score is any different.
// ...
// Keep the new score value.
theScore = aScore;
}
finishLoading();
}
// This method just ignores an error, think of it
// as of blank to process the exceptional cases.
function onError(e:Event):void
{
// Sanity check. There should be only one valid URLLoader instance.
if (e.target != theLoader)
{
return;
}
finishLoading();
}
// Call it finishLoading(true) to stop the tracking routine.
function finishLoading(thenstop:Boolean = false):void
{
// Dispose of the URLLoader instance if any.
if (theLoader)
{
var aLoader:URLLoader = theLoader;
theLoader = null;
aLoader.removeEventListener(Event.COMPLETE, onXML);
try
{
aLoader.close();
}
catch (fail:Error)
{
// Do nothing about it.
}
}
finishTimer();
if (thenstop)
{
return;
}
// Wait 100 ms to give Flash Player a breather
// before starting to load the file once more.
theTimer = new Timer(100, 1);
theTimer.addEventListener(TimerEvent.TIMER, onTime);
theTimer.start();
}
function finishTimer():void
{
// Dispose of the Timer instance if any.
if (theTimer)
{
theTimer.removeEventListener(TimerEvent.TIMER, onTime);
theTimer.stop();
theTimer = null;
}
}
function onTime(e:TimerEvent):void
{
// Now go for the next iteration.
finishTimer();
trackNext();
}

Remove a function from the stage

I have a big issue with my code
I have a function called "delayCallFuntions":
function delayCallFuntions(delay: int, func: Function) {
var timer: Timer = new Timer(delay, 1);
timer.addEventListener(TimerEvent.TIMER, func);
timer.start();
}
And I used this function like below to make connection between 2 point in my screen:
delayCallFuntions(1, function (e: Event) {timer011(wireColor);});
And function "timer011" is making the connections:
function timer011(firstColor: int): void {
wireColor = firstColor;
//GRID is a class
//Path A to B
var PathAB: Grid;
PathAB = new Grid(4, 5, 20, 17, canvas, wireColor);
this.addChild(PathAB);
}
My problem is:
I have several of these functions like "timer012", "timer013", ... that they need to execute one after another.
When I go out of this scene and come back again, these is still some of these functions are working while I need them to start from the beginning and go one by one.
for example: when i come back, "timer011" is starting while "timer016" is also completing at the same time.
hope someone can help me as this problem made me frustrated.
Currently you are creating a whole new timer everytime you add a function. That timer will stay in memory because of the event listener, and since it's encapsulated in the function, you have no easy way to reference it again to stop them.
What would be a better approach, is to create just one timer globally referenced so you can stop it if needed.
Here is a way you could accomplish this:
//create an array that will hold all the functions you are planning on calling
var delayedFuncs:Array = [];
//this var will be used to store the current function that will be called next
var currentFuncObj:Object = null; //set it to null so it clears the value when you return to this frame
//create a single, global timer reference for everything
//don't initialize it here though
//if you revisit this frame, you don't want to create a whole new timer, but keep using the previous one
var funcTimer:Timer;
//if the timer has already been created (you've been to this frame before), stop it
if (funcTimer) {
funcTimer.stop();
}else {
//if you haven't been to this frame before, create the timer and add the listener
funcTimer = new Timer(1,1);
funcTimer.addEventListener(TimerEvent.TIMER, nextFunc, false, 0, true);
}
//this function adds items to your queue. I've added the ability to also include parameters
function delayCallFunctions(delay:int, func:Function, ... funcParams):void {
//add an object to the array that stores the function, delay, and any parameters to pass to that function
delayedFuncs.push({delay: delay, func: func, params: funcParams});
//if the timer hasn't started running yet, start it since we've added something
if(!funcTimer.running) nextFunc();
}
//this function runs when the timer completes
function nextFunc(e:Event = null):void {
//if we have an existing function to call, call it
if (currentFuncObj){
//invoke the function with the parameters
currentFuncObj.func.apply(null, currentFuncObj.params);
}
//if there are still items in the array, grab the next one
if(delayedFuncs.length > 0){
//array.shift grabs the first element in the array and removes it from the array
currentFuncObj = delayedFuncs.shift();
//reset the timer
funcTimer.reset();
//set the appropriate delay
funcTimer.delay = currentFuncObj.delay;
//start the timer again
funcTimer.start();
}
}
So now, you'd use by doing:
delayCallFunctions(3000, trace, "hello", "world", "I'll be traced 3 seconds from now");
delayCallFunctions(2000, trace, "I'll be called 2 seconds after the last one");
Or, with your specific code:
delayCallFuntions(1000, timer011, wireColor);
Now at any time (say you hit a button to go to change scenes), you can just stop the global timer.
funcTimer.stop();

as3 return value on loader complete

I am trying to get my function to return a value from a php script once it has loaded. I am having issues with the 'return' aspect. Trying to the the value of 'TheLink" from a function with a 'return' in it. It's usually null. What am I doing wrong here?
var theLink = loadAudio();
public function loadAudio():String
{
var req:URLRequest = new URLRequest("myScript.php");
var loader:URLLoader = new URLLoader(req);
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE, Finished);
function Finished(e:Event)
{
var theValue = JSON.parse(e.target.data.audioLink);
return theValue;
}
}
A lot of things are wrong in your code but more important is that you cannot escape the asynchronous nature of AS3.
First thing first, even if AS3 wasn't asynchronous your code would still not work. The "loadAudio" never returns anything so theLink can never get any data. The 'Finished' method is the one return something but there's no variable to catch that data either.
Now the real stuff, AS3 is asynchronous that means that things will happen at a later time and this is why you need to pass an event listener in order to 'catch' when things happen. In your case the Event.COMPLETE will trigger at a later time (asynchronous) so there's no way for a variable to catch the result before the result is actually available so:
var theLink:Object = loadAudio();//this is not possible
The correct way is:
private var loader:URLLoader;//avoid GC
private var theLink:Object;//this will store result when available
//I assume you are using a class
public function MyClass()
{
loadAudio();//let's start loading stuff
}
private function loadAudio():void
{
var req:URLRequest = new URLRequest("myScript.php");
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE, handleLoaded);
loader.load(req);
}
private function handleLoaded(e:Event):void
{
theLink = JSON.parse(e.target.data.audioLink);
//done loading so store results.
}

Can you add an Integer to a loaders instance name?

My project is a slideshow of sorts. I load multiple files and display them one at a time. Unlike most slideshows, each of the loaded files are combinations of SWF's, Jpegs, and Video. My code is a little much to put here, so i'll just put what relates to the problem with my "idea" thrown in.
Here's what i want to do. This code is not accurate, but it might show what i'm wanting to do. However i can't seem to find the correct method of doing it. Overall i would like to put a dynamic number into the name of my loader for each slide. That way i can load, and unload/null the loader so the content is completely removed from my project.
As of now my project runs fine, however it has some memory leaks that i cannot track down. I've checked all Eventlisteners, also even calling "kill" functions within the loaded swf's that unload/removes/null's everything. Still getting these leaks.
Thanks for any help
var i:Number;
i = 0;
function loadSlideData():void
{
curSlide[i].load(new URLRequest("Slides/"+ slideXML.image[slideInt].#file));
curSlide[i].contentLoaderInfo.addEventListener(Event.COMPLETE, DisplaySlide, false, 0, true);
}
function DisplaySlide(event:Event):void
{
curSlide.contentLoaderInfo.removeEventListener(Event.COMPLETE, DisplaySlide);
slideLength.delay = slideXML.image[slideInt].timeDelay.#seconds*1000;
slideLength.start();
addChild(curSlide);
curSlide.alpha = 1;
curSlide.x = (1150-1000)/2;
curSlide.y = (700-460)/2 - 30;
slideLength.addEventListener(TimerEvent.TIMER, zoomOut,false,0,true);
}
function zoomOut(event:TimerEvent):void
{
slideLength.removeEventListener(TimerEvent.TIMER, zoomOut);
if(slideInt == slideXML.image.length()-1)
{
slideInt=0;
}else{
slideInt++;
}
slideLength.stop();
TweenLite.to(slideArray[randomSqareNum], .75, {y:sqrOriginalY, x:sqrOriginalX, scaleX:1, scaleY:1, ease:Circ.easeInOut,onComplete:zoomIn});
TweenLite.to(curSlide, .25, {alpha:0, ease:Circ.easeInOut,onComplete:removeCurSlide});
trace(curSlide);
}
function removeCurSlide():void
{
trace(curSlide);
i++;
var curSlide[i]:Loader = new Loader();
loadSlideData();
}
This doesn't make much sense:
var curSlide[i]:Loader = new Loader();
Are you asking how to store loaders in array?
I am not sure if this will give you an idea ... this:
function removeCurSlide():void
{
trace(curSlide);
i++;
var curSlide[i]:Loader = new Loader();
loadSlideData();
}
Should be more like:
var slideArray:Array = [];
function removeCurSlide():void
{
trace(curSlide);
i++;
var curSlide:Loader = new Loader();
slideArray[i] = currSlide;
loadSlideData();
}
You would need to replace all the other 'curSlide' to 'slideArray'
Here is some nice explanation how arrays work:
http://www.republicofcode.com/tutorials/flash/as3arrays/

Refreshing every XX seconds

I am new to actionscript and flash, but i managed to make code that gets data from php file and refresh result every 30 seconds:
var timerRefreshRate:Number = 30000;
var fatherTime:Timer = new Timer(timerRefreshRate, 0);
fatherTime.addEventListener(TimerEvent.TIMER, testaa);
fatherTime.start();
function testaa(event:Event):void{
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE,varsLoaded);
loader.load(new URLRequest("data.php"));
function varsLoaded (event:Event):void {
this.opaqueBackground = loader.data.color;
title.text=loader.data.title;
banner_text.text=loader.data.text;
}
}
But now i am facing 2 problems:
1.) User must wait 30 seconds for movie to load first time
2.) Setting background color does not work any more.
What am i doing wrong?
You can call your function once to load immediately without waiting 30 seconds. Just change the parameters of the function to default to a null event:
function testaa(event:Event = null):void{
//...
}
Now you can call the function like so:
//...
fatherTime.start();
testaa();
So you start the timer but immediately run the function once.
For your second problem, the issue is most likely that you are using a nested function, so this does not refer to your class but rather the testaa function. Nested functions are bad practice in general and you should avoid them if possible. Move the function and loader reference outside and it should work. Final result should be something like this:
var loader:URLLoader;
var timerRefreshRate:Number = 30000;
var fatherTime:Timer = new Timer(timerRefreshRate, 0);
fatherTime.addEventListener(TimerEvent.TIMER, testaa);
fatherTime.start();
testaa();
function testaa(event:Event = null):void{
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
loader.addEventListener(Event.COMPLETE,varsLoaded);
loader.load(new URLRequest("data.php"));
}
function varsLoaded (event:Event):void {
this.opaqueBackground = loader.data.color;
title.text=loader.data.title;
banner_text.text=loader.data.text;
}