Force to refresh movie clip? - actionscript-3

I have a really simple code piece like that;
loadingMc.visible = true;
trace("ok");
// send photo to server
loadingMc.visible = false;
Sending photo takes 3-5 seconds but movie clip becomes visible only for last second of process. I can see "ok" message in output at start of the process. So i assume problem is not redrawing movie clip. Is there any option to force redraw before process starts?
UPDATE:
Sending to server part;
upload.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void{
loadingText.visible = true;
trace("ok");
var bmd:BitmapData = new BitmapData(1024,768,true,0);
bmd.draw(imageArea);
savePicToServer(bmd);
});
function savePicToServer(bmd:BitmapData):void
{
var jpgEncoder:JPGEncoder = new JPGEncoder(85);
var jpgStream:ByteArray = jpgEncoder.encode(bmd);
var loader:URLLoader = new URLLoader();
configureListeners(loader);
var header:URLRequestHeader = new URLRequestHeader("Content-type", "application/octet-stream");
var request:URLRequest = new URLRequest("http://localhost/test/upload.php?key=prvkey");
request.requestHeaders.push(header);
request.method = URLRequestMethod.POST;
request.data = jpgStream;
loader.load(request);
}
In the COMPLETE event;
loadingText.visible = false;

EDIT
Just from reading your code, I don't see why this should not be working - but FP does strange things sometimes.
In similar cases, I used setTimeout() to force the player to delay the subsequent actions and allow the screen to refresh:
upload.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void{
loadingText.visible = true;
trace("ok");
setTimeout( doSave, 10 );
});
private function doSave() : void {
var bmd:BitmapData = new BitmapData(1024,768,true,0);
bmd.draw(imageArea);
savePicToServer(bmd);
}
If this still doesn't work, perhaps a longer timeout will do the trick - but 10ms usually should be enough to refresh the screen.
EDIT
Another way would be to add and remove an ENTER_FRAME listener to make sure the frame really was refreshed:
upload.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void{
loadingText.visible = true;
trace("ok");
addEventListener( Event.ENTER_FRAME, onNextFrame );
});
private function onNextFrame( ev:Event ) : void {
removeEventListener( Event.ENTER_FRAME, onNextFrame );
doSave();
}
private function doSave() : void {
var bmd:BitmapData = new BitmapData(1024,768,true,0);
bmd.draw(imageArea);
savePicToServer(bmd);
}

Related

How do I unload an external ".swf" file to load another?

I am a student who's working for months on a game and now I've got stuck on a problem.
I am new to actionscript 3 but i learn fast.
I can load my menu screen ("startScreen.swf") into my game (firstgame) automatically and everything works fine when I click play or storyScreen or instructionsScreen. But the problem is that I want to return after pressing ESC button. I have tried many code but nothing works exactly how I want.
Example: I click on story and the swf (storyScreen.swf) loads and I can read the story and when I am finished, I want to press ESC to reload my swf (startScreen.swf) and all the functions in it. Like play and instructions.
You can find my code and empty space in the function (esc).
I know it is maybe easy to solve but I really don't know how. :(
public class FirstGame extends MovieClip
{
public var Player:MovieClip
private var leftKeyIsDown:Boolean;
private var RightKeyIsDown:Boolean;
private var aMissileArray:Array;
private var aEnemyArray:Array;
public var scoreTxt:TextField;
public var ammoTxt:TextField;
public var MenuEnd:EndGameScreen;
public var menuAgain:EndGameScreen;
private var MenuStart:mcStartGameScreen;
private var MenuStory:mcStartGameScreen;
private var MenuInstructions:mcStartGameScreen;
private var nScore:Number;
private var nAmmo:Number;
private var tEnemyTimer:Timer;
public function FirstGame()
{
//Create a loader object
var startLoader:Loader = new Loader();
//add event listener to listen for the complete event
startLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, startLoaded);
//load our loader object
startLoader.load(new URLRequest("startScreen.swf"));
}
public function startLoaded(e:Event):void
{
MenuEnd.hideScreen();
Player.visible = false;
scoreTxt.visible = false;
ammoTxt.visible = false;
//get a reference to the loaded movieclip
MenuStart = e.target.content as mcStartGameScreen;
//listen for start game event
MenuStart.addEventListener("START_GAME", playGameAgain);
//add it to the stage
addChild(MenuStart);
//get a reference to the loaded movieclip
MenuStory = e.target.content as mcStartGameScreen;
//listen for start game event
MenuStory.addEventListener("SHOW_STORY", storyGameScreen);
//add it to the stage
addChild(MenuStory);
//get a reference to the loaded movieclip
MenuInstructions = e.target.content as mcStartGameScreen;
//listen for start game event
MenuInstructions.addEventListener("SHOW_INSTRUCTIONS", instructionsGameScreen);
//add it to the stage
addChild(MenuInstructions);
}
private function instructionsGameScreen(e:Event):void
{
var instructionsLoader:Loader = new Loader();
var url:URLRequest = new URLRequest("instructionsScreen.swf");
instructionsLoader.load(url);
addChild(instructionsLoader);
stage.addEventListener(KeyboardEvent.KEY_UP, esc);
}
private function storyGameScreen(e:Event):void
{
var storyLoader:Loader = new Loader();
var url:URLRequest = new URLRequest("storyScreen.swf");
storyLoader.load(url);
addChild(storyLoader);
stage.addEventListener(KeyboardEvent.KEY_UP, esc);
}
private function esc(e:KeyboardEvent):void
{
if (e.keyCode == 27)
{
//fscommand("quit");
/////////test//////////////////(new URLRequest(stage.loaderInfo.url), "FirstGame.swf");
}
}
private function playGameAgain(e:Event):void
{
//initialize variables
aMissileArray = new Array();
aEnemyArray = new Array();
nScore = 0;
nAmmo = 20;
Player.x = 262,95
Player.y = 323,30
Player.visible = true;
scoreTxt.visible = true;
ammoTxt.visible = true;
MenuStart.hideScreen();
MenuEnd.addEventListener("PLAY_AGAIN", playGameAgain);
MenuEnd.hideScreen();
updateScoreText();
updateAmmoText();
//trace("First Game Loaded");
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUp);
stage.addEventListener(Event.ENTER_FRAME, gameLoop)
//creat an timer object
tEnemyTimer = new Timer(1000)
//listen for the timer ticks/intervals
tEnemyTimer.addEventListener(TimerEvent.TIMER, addEnemy)
//start our timer
tEnemyTimer.start();
}
I believe you should be able to start a new request on the same loader object. Either way, you're dealing with object creation + cleanup. The crux of the solution I'm offering is that you reuse your loaders.
Your current code is somewhat repetitious, so I've modified it slightly to demonstrate how you could simplify the code. Some thoughts while reviewing your code:
You're adding the same object to the stage multiple times (ie.,
MenuStart, MenuStory, MenuInstructions); these are all pointers to
the same root swf you loaded (a.k.a., startLoader).
You've registered events the stage at multiple locations. Best
practice is to place these in your constructor as they are
persistent.
Because you want to reuse your loaders at a later point, keeping a
table with them makes it easier to reference.
Any time you find yourself programming the same code in similar ways,
it's a good indication that you can simplify with a single function
(simply change the arguments).
Give this a try:
public var Player:MovieClip
private var leftKeyIsDown:Boolean;
private var RightKeyIsDown:Boolean;
private var aMissileArray:Array;
private var aEnemyArray:Array;
public var scoreTxt:TextField;
public var ammoTxt:TextField;
public var MenuEnd:EndGameScreen;
public var menuAgain:EndGameScreen;
private var MenuStart:mcStartGameScreen;
private var MenuStory:mcStartGameScreen;
private var MenuInstructions:mcStartGameScreen;
private var nScore:Number;
private var nAmmo:Number;
private var tEnemyTimer:Timer;
private var screens:Object = {
"SHOW_INSTRUCTIONS":{
"loader":new Loader(),
"url":new URLRequest("instructionsScreen.swf")
},
"SHOW_STORY":{
"loader":new Loader(),
"url":new URLRequest("storyScreen.swf")
},
"START_GAME":{
"loader":new Loader(),
"url":new URLRequest("startScreen.swf")
}
}
public function FirstGame() {
var startLoader:Loader = loadScreen({"type":"START_GAME"});
//Register our event listeners
startLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, startLoaded);
stage.addEventListener(KeyboardEvent.KEY_UP, esc);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUp);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
stage.addEventListener(Event.ENTER_FRAME, gameLoop)
}
private function loadScreen(e:Object):Loader {
screens[e.type].loader.load(screens[e.type].url);
addChild(screens[e.type].loader);
return screens[e.type].loader;
}
public function startLoaded(e:Event):void {
//initialize variables
aMissileArray = new Array();
aEnemyArray = new Array();
nScore = 0;
nAmmo = 20;
Player.x = 262, 95
Player.y = 323, 30
Player.visible = true;
scoreTxt.visible = true;
ammoTxt.visible = true;
MenuEnd.addEventListener("PLAY_AGAIN", playGameAgain);
MenuEnd.hideScreen();
updateScoreText();
updateAmmoText();
//creat an timer object
tEnemyTimer = new Timer(1000)
tEnemyTimer.addEventListener(TimerEvent.TIMER, addEnemy)
tEnemyTimer.start();
var swfRoot = screens.START_GAME.loader["content"];
swfRoot.addEventListener("START_GAME", loadScreen);
swfRoot.addEventListener("SHOW_STORY", loadScreen);
swfRoot.addEventListener("SHOW_INSTRUCTIONS", loadScreen);
}
private function esc(e:KeyboardEvent):void {
if (e.keyCode == 27) {
//fscommand("quit");
// Loop through our loaders, and reset them all.
for each (var entry:Object in screens) {
entry.loader.unloadAndStop();
removeChild(entry.loader);
}
// Reload the start screen
var startLoader:Loader = loadScreen({"type":"START_GAME"});
startLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, startLoaded);
}
}
The last thing I want to be guilty of is telling you how to run your project, however, I think you may be taking the wrong approach, here.
Instead of setting up the story and the game as separate swf files, what you would probably need to do is set up the story as a MovieClip, and the game as a MovieClip. (Yes, MovieClips can contain other symbols, including other MovieClips.) Then, you'll want to hide/show or add/remove these MovieClips from your stage using code.
If you need to know how to do this, let me give you the age-old admonishment: RTD (Read the Documentation). These are basic tasks that are covered both in the online Adobe Flash documentation, and across numerous tutorials online.
I hope that helps!

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;
}

ActionScript 3: How to remove EventListener with anon functions

I have written code as follows.
Problem is that I can't remove Event.COMPLETE event listener and when I call the loadData function twice or more, it works 2 times or more. Sorry for my bad english and worse explanation but I need to fix it today and I don't know what to do.
I think the code is pretty obvious. please help!
var ldr:URLLoader = new URLLoader();
function loadData(text_place, scrollbar, fileURL:String):void {
text_place.wordWrap = true;
var f:TextFormat = new TextFormat();
f.align = TextFormatAlign.RIGHT;
text_place.setTextFormat(f);
ldr.dataFormat = URLLoaderDataFormat.TEXT;
ldr.load(new URLRequest(fileURL));
ldr.addEventListener(Event.COMPLETE, function ldr_complete(evt:Event){
initText(text_place, ldr.data, scrollbar);
});
ldr.addEventListener(IOErrorEvent.IO_ERROR, loadError);
}
function initText(text_place:TLFTextField, fileContent, scrollbar):void {
ldr.removeEventListener(IOErrorEvent.IO_ERROR, loadError);
text_place.htmlText = "";
text_place.tlfMarkup = fileContent;
scrollbar.update();
trace("Data loaded");
}
function loadError(e:IOErrorEvent):void {
trace("Error loading an external file.");
}
just avoid writing function enclosures and extend the scope of the complete function's passed arguments so it can access them.
var ldr:URLLoader = new URLLoader();
var text_place:TextField;
var scrollbar:Object; //or whatever it is
function loadData(text_place, scrollbar, fileURL:String):void
{
var f:TextFormat = new TextFormat();
f.align = TextFormatAlign.RIGHT;
text_place.wordWrap = true;
text_place.setTextFormat(f);
scrollbar = scrollbar;
ldr.dataFormat = URLLoaderDataFormat.TEXT;
ldr.load(new URLRequest(fileURL));
ldr.addEventListener(IOErrorEvent.IO_ERROR, loadError);
ldr.addEventListener(Event.COMPLETE, loadComplete);
}
function initText(text_place:TLFTextField, fileContent, scrollbar):void
{
removeLoaderEventListeners();
text_place.htmlText = "";
text_place.tlfMarkup = fileContent;
scrollbar.update();
trace("Data loaded");
}
function loadError(e:IOErrorEvent):void
{
removeLoaderEventListeners();
trace("Error loading an external file.");
}
function loadComplete(evt:Event):void
{
removeLoaderEventListeners();
initText(text_place, ldr.data, scrollbar);
}
function removeLoaderEventListeners():void
{
ldr.removeEventListener(IOErrorEvent.IO_ERROR, loadError);
ldr.removeEventListener(Event.COMPLETE, loadComplete);
}
if you want to stop listening for an event after it triggered, you can unregister the anonymous listener in itself:
ldr.addEventListener(Event.COMPLETE, function(event:Event):void
{
event.target.removeEventListener(event.type, arguments.callee);
// ... do whatever you need to do here
});
But if you also want to stop listening for other events from the same dispatcher when it completes, such as your IOErrorEvent.IO_ERROR listener, you'd still need a reference to that listener to remove it.
There is a simpler way. Instead of removing event listeners, close the loader.
ldr.close();
Per the documentation:
Closes the load operation in progress. Any load operation in progress
is immediately terminated. If no URL is currently being streamed, an
invalid stream error is thrown.

How can I use addEventListener and return something that's changed from it in Actionscript?

private function getTitle(src:String):String{
var urlLoader:URLLoader = new URLLoader();
var rssURLRequest:URLRequest = new URLRequest(src);
var rss:XML = new XML;
var t:String = src;
urlLoader.addEventListener(Event.COMPLETE,
function(event:Event):void{
rss = XML(urlLoader.data);
t = rss.channel.title.toString();
});
return t;
}
I'm aware that this code doesn't work because the anonymous function doesn't work until after t is returned. How would I make it so that it works?
You won't be able to return the loaded data from this method. The reason for this is because the loading is asynchronous and doesn't not block the execution of subsequent code. Your best option is to move the vars out of the scope of the function and to write a second function to handle the COMPLETE event.
Something like the following should work:
var rss:XML;
var t:String;
var path:String = "some path";
var urlLoader:URLLoader = new URLLoader();
private function getTitle(src:String):String
{
urlLoader.load( new URLRequest( src ) );
urlLoader.addEventListener(Event.COMPLETE, onComplete );
}
private function onComplete(event:Event):void
{
rss = XML(urlLoader.data);
t = path + rss.channel.title.toString();
}
I realize that this doesn't really answer the question directly, though it is the best practice for handling data loading. If you really want to stop any code from executing before the data is loaded, it may be possible use a while loop after the addEventListener line to halt the player from until the data is loaded. This should probably be considered a not so elegant hack though.
private function getTitle(src:String):String
{
var urlLoader:URLLoader = new URLLoader();
var rssURLRequest:URLRequest = new URLRequest(src);
var rss:XML = new XML;
var t:String = src;
var complete:Boolean;
urlLoader.addEventListener(Event.COMPLETE,
function(event:Event):void
{
rss = XML(urlLoader.data);
t = rss.channel.title.toString();
complete = true;
});
while( !complete ) { /* sleep hack */ }
return t;
}
I haven't tested this, but it seems like it could work. The first example is recommended.

AS3 - Error - The supplied DisplayObject must be a child

I'm using the following code to display an advertisement in a flash game. It works great except for one small issue. If the startButton function gets called before the ad is displayed I get an error saying:
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
at flash.display::DisplayObjectContainer/removeChild()
at VirusDefender/clickStart()[VirusDefender::frame1:17]
There are no errors when the advert is on the stage. I tried wrapping the removeChild in a try catch but that did not work.
Could someone please tell me how to prevent the removeChild line from being called if the ad has not been displayed yet. Sometimes it takes up to 3 seconds for the ad to show so people may click before it gets on the stage.
stop();
//Start Button
startScreen.startButton.addEventListener(MouseEvent.CLICK,clickStart);
function clickStart(event:MouseEvent)
{
sndFire=new fire_sound();
sndFireChannel=sndFire.play();
removeChild(l);
gotoAndStop("play");
}
// Help Button
startScreen.helpButton.addEventListener(MouseEvent.CLICK,clickHelp);
function clickHelp(event:MouseEvent)
{
sndFire=new fire_sound();
sndFireChannel=sndFire.play();
removeChild(l);
gotoAndStop("help");
}
// SMAATO Advertising Code for Start Page
var request:URLRequest = new URLRequest("http://soma.smaato.com/oapi/reqAd.jsp");
var variables:URLVariables = new URLVariables();
variables.adspace = "0";
variables.pub = "0";
variables.devip = "127.0.0.1";
variables.format = "IMG";
variables.adcount = "1";
variables.response = "XML";
request.data = variables;
var loader:URLLoader = new URLLoader();
var l:Loader = new Loader();
loader.addEventListener(Event.COMPLETE, onComplete);
loader.load(request);
function onComplete(e:Event):void
{
var data:XML = new XML(loader.data as String);
var status:String = data.*::status.toString();
if(status == "success")
{
var ad:XMLList = data.*::ads.*::ad;
var link:String = ad.*::link.toString();
l.load(new URLRequest(link));
addChild(l);
l.x = 135;
l.y = 265;
var clickurl:String = ad.*::action.#target.toString();
l.addEventListener(MouseEvent.CLICK, onAdClick);
}
function onAdClick(e:MouseEvent):void
{
var request:URLRequest = new URLRequest(clickurl);
navigateToURL(request);
}
}
Thanks! Rich
Wrap the line into:
if (l != null && contains(l)) {
removeChild(l);
}
This will check if l is not null and a child, and only then removes it.
Alternatively, as Loader is a DisplayObject, you can simply add it to the stage before anything else (it need not have finished loading before you can add it to the stage) and remove it when start is pressed, that way you wont have it lingering or popping in later if you do in fact want it gone on press.