Passing variable value from function in a class in AS3 - actionscript-3

I'm working with as3 and I don't understand how to pass from a value on a function to a global variable. I have this code (in a .as file):
package {
public class loadInfo {
import flash.events.*;
import flash.net.*;
private var teamA:String;
public var urlLoader:URLLoader = new URLLoader( );
public function loadInfo() {
urlLoader.addEventListener(Event.COMPLETE, handleComplete);
urlLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
urlLoader.load(new URLRequest("data.txt" ));
}
public function handleComplete( event:Event ):void {
this.teamA = urlLoader.data.teamA;
}
public function getTeamA():String{
return teamA;
}
}
}
What I'm doing with this code is to load several variables which are in a .txt file.
and in the .fla file I have:
import loadInfo;
var dat:loadInfo = new loadInfo();
trace(dat.getTeamA());
but the result is "null".
So, I have no clue what to do. Help is appreciated. Thanks.

The problem is that you don't wait for the loader to complete. It takes time to load that txt file and if you call getTeamA immediately, the loader isn't finished. You should do something like this:
var dat:loadInfo = new loadInfo();
dat.addEventListener(Event.COMPLETE, onDataLoaded);
function onDataLoaded(e:Event):void {
trace (dat.getTeamA());
}
And within loaderInfo:
public function handleComplete( event:Event ):void {
this.teamA = urlLoader.data.teamA;
dispatchEvent(new Event(Event.COMPLETE));
}
Should work properly. Keep in mind that loaderInfo must extend EventDispatcher (class loaderInfo extends EventDispatcher {)..

Related

Flash AS3 Loadmovie Error #1009 - External AS file

We have created a program in Flash that uses an external actionscript file.
I'm trying to load/import this swf in another flash file but I'm getting a 'Error #1009: Cannot access a property or method of a null object reference'
I know this is to a reference in my external .as file but I'm not sure how to fix this
This is my code to load (and scale) the swf
var ld:Loader = new Loader();
addChild(ld);
ld.load(new URLRequest("tatton.swf"));
ld.contentLoaderInfo.addEventListener(Event.COMPLETE,function(evt) {
var mc = evt.target.content;
mc.scaleX=0.7031;
mc.scaleY=0.7031;
});
Anyone have any ideas?
Thanks
Edit: here is the full error code: TypeError: Error #1009: Cannot access a property or method of a null object reference. at Tatton()
And below is my .as file code:
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.utils.setTimeout;
import flash.utils.clearTimeout;
import flash.utils.getDefinitionByName;
public class Tatton extends MovieClip
{
public var active_clip:MovieClip;
public var to:Number;
public function Tatton()
{
// Create empty vector of movieclips;
stage.addEventListener(MouseEvent.CLICK, reset_timeout);
init();
}
public function reset_timeout(e:Event = null)
{
clearTimeout(to);
// 3 mins
to = setTimeout(fire_timeout, 150 * 1000);
}
public function fire_timeout():void
{
// Reset the system (and run attractor) if we're not on the attractor already.
if ( !(active_clip is Attractor))
{
init();
}
}
public function init()
{
// Reset globals
Globals.skip_menu_anim = false;
Globals.skip_staff_menu = false;
load_movie("Attractor");
reset_timeout();
}
public function load_movie(name:String):void
{
if (active_clip is MovieClip)
{
kill_active_movie();
}
active_clip = createInstance(name);
addChild(active_clip);
active_clip.startup();
}
public function kill_active_movie():void
{
active_clip.shutdown();
removeChild(active_clip);
active_clip = null;
}
public function createInstance(className:String):Object
{
var myClass:Class = getDefinitionByName(className) as Class;
var instance:Object = new myClass();
return instance;
}
}
}
try this
public function Tatton() {
addEventListener(Event.ADDED_TO_STAGE, stageAvailable);
}
private function stageAvailable(e:Event = null) {
removeEventListener(Event.ADDED_TO_STAGE, stageAvailable);
// Create empty vector of movieclips;
stage.addEventListener(MouseEvent.CLICK, reset_timeout);
init();
}
refer to this article to understand why
You need to add the event listener after you call Loader.load because contentLoaderInfo is null until load is called.

synchronous image loading action script 3 flash

I am having a problem with my flash/as3 application. I've created most of it and at the moment I'm struggling to get my external resources work.
My application consists of a Controller class, that takes control of an application flow. At the beginning it initializes AppInitializer class, that loads / generates the whole app content (it is a simple point'n'click game).
In AppInitializer I create an array of items available in a game. Item's constructor takes path as a parameter (String) to the resource (image). Then, inside the constructor I call a static method of my AssetsLoader class which looks like that:
public static function loadImage(path:String):BitmapData
{
completed = false;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){completed = true;
trace("Loading completed");
e.target.removeEventListener(Event.COMPLETE, check);});
if (path == "")
loader.load(new URLRequest("images/default.png"));
else
loader.load(new URLRequest("images/" + path));
//while (!completed);
var image:Bitmap = new Bitmap((loader.content as BitmapData));
return image.bitmapData;
}
Where completed is a static variable of AssetsLoader.
First problem is: I create many Item objects in a row, so the method loadImage should not be static I guess (same with completed variable), since that may cause problems when loading.
Second problem is: At the moment I'm unable to return the bitmapData (or bitmap, it does not really matter), because the return statement will always return null - because the resource is loaded asynchronously and is not loaded at the time application reaches return statement. If I uncomment the wile loop, the complete event is never called.
I would like to ask more experienced ActionScript developers to point out any solutions that would require minimal changes to the rest of my app and would solve those problems. (I think first problem can be eliminated by using some sort of queueing method for the loader, but I'm stuck with the second problem for few days so far and came up with no possible solution).
I could also consider changes in my logic, so I could preload all image resources into "somewhere" and after that just make copies of these images for my purposes. If that's easier to do.
So as I suggested in the comments, a minimal change resolution could simply be to pass a function to call as part of the parameters for loadImage(). This is known as a callback function, and it would look something like this:
First create the callback function:
public function addImage( bitmapData:BitmapData ):void {
//do something with the bitmapData returned
}
Next adjust your loadImage() local function to use the callback with the bitmap data when the event has completed:
public static function loadImage(path:String, callback:Function):BitmapData {
completed = false;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){completed = true;
trace("Loading completed");
var image:Bitmap = new Bitmap((loader.content as BitmapData));
callback( image ); //call the callback with the bitmap
e.target.removeEventListener(Event.COMPLETE, check);});
if (path == "")
loader.load(new URLRequest("images/default.png"));
else
loader.load(new URLRequest("images/" + path));
}
Then just you make the call to loadImage() like so:
loadImage( myPathToImage, addImage );
That is a simply resolution and does exactly what you need it to.
Super, you commented that insane line of code with while ;)
Here for you, simple QueueLoader (It loads items one by one, when you add item to the queue, you can store id of item in queue), that will help you with your task:
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
public class StackOverflow extends Sprite {
public function StackOverflow() {
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, onAdded);
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
setup();
}
private function setup():void {
//Store somewhere reference on QueueLoader to reuse it, and use queue
var loader:QueueLoader = new QueueLoader();
loader.addEventListener(QueueLoaderEvent.COMPLETE, onCompleteItem);
//Add as many images to load as you want, and store Id's of items that
//will be loaded in future, if you want...
loader.addItem("someUrl1");
loader.addItem("someUrl2");
var importantId:int = loader.addItem("someUrl3");
loader.addItem("someUrl4");
loader.addItem("someUrl6");
}
private function onCompleteItem(e:QueueLoaderEvent):void {
trace("Item loaded");
}
}
}
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.net.URLRequest;
internal class QueueLoader extends EventDispatcher {
private var _list:Array;
private var _cursor:int;
private var _loading:Boolean;
public function QueueLoader(target:IEventDispatcher = null) {
super(target);
_list = [];
}
public function addItem(url:String):int {
var item:Object = {url: url, id: ++_cursor};
_list.push(item);
loadNext();
return item.id;
}
override public function dispatchEvent(evt:Event):Boolean {
if (hasEventListener(evt.type) || evt.bubbles) {
return super.dispatchEvent(evt);
}
return true;
}
protected function loadNext():void {
if (_list.length > 0 && !_loading) {
var loader:Loader = new Loader();
var data:Object = _list[0];
var request:URLRequest = new URLRequest(data.url);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
loader.load(request);
_loading = true;
}
}
private function onComplete(e:Event):void {
var data:Object = _list.shift();
data.content = e.currentTarget.content;
dispatchEvent(new QueueLoaderEvent(QueueLoaderEvent.COMPLETE, data.id, data));
_loading = false;
loadNext();
}
}
internal class QueueLoaderEvent extends Event {
public static const COMPLETE:String = "queueLoaderEventComplete";
private var _id:int;
private var _data:Object;
public function QueueLoaderEvent(type:String, $id:int, $data:Object, bubbles:Boolean = false, cancelable:Boolean = false) {
_id = $id;
_data = $data;
super(type, bubbles, cancelable);
}
override public function clone():Event {
return new QueueLoaderEvent(type, id, data, bubbles, cancelable);
}
public function get id():int {
return _id;
}
public function get data():Object {
return _data;
}
}
Your loadImage mehtod will look at the end:
public static function loadImage(path:String):int
{
return queueLoader.addItem(path);
}

How do i access a file that is loaded after?

This is in Flash Action Script 3
Here is my class, I loaded an xml in the class
and now i want to do some things after in the main page, like create things on the stage based off of the xml. Help
I understand why it doesn't work, by the time the function is called the xml hasn't be loaded yet, i'm just clueless how to solve this? add an event listener maybe?
package {
public class LoadXMLAdventure {
var adventureXML: XML;
var xmlReq:URLRequest;
var xmlLoader:URLLoader = new URLLoader();
var _currentRoom:int;
public function LoadXMLAdventure(xmlFileName:String) {
xmlReq = new URLRequest(xmlFileName);
xmlLoader.load(xmlReq);
xmlLoader.addEventListener(Event.COMPLETE, xmlLoaded);
}
function xmlLoaded(event:Event):void{
adventureXML = new XML(xmlLoader.data)
//trace( adventureXML.toXMLString() )
}
public function myFunction():void{
trace(adventureXML.toXMLString()) //this does not work
}
}
}
On my main page
var game:LoadXMLAdventure = new LoadXMLAdventure("file.xml");
game.myFunction();
Update
I want to call a function on the main page so i can do some stage manipulation
this is in the class, i get an error undefined method
function xmlLoaded(event:Event):void{
adventureXML = new XML(xmlLoader.data)
this.dispatchEvent( new Event("showRoom") );
}
this is on the stage
stage.addEventListener("showRoom",showRoom);
function showRoom(e:Event):void{
trace("showroom hello")
}
You can dispatch an event when the xml loaded complete,but in your case, you just need to call myFunction in xmlLoaded;
import flash.events.EventDispatcher;
public class LoadXMLAdventure extends EventDispatcher{
public function LoadXMLAdventure(xmlFileName:String) {
xmlReq = new URLRequest(xmlFileName);
xmlLoader.addEventListener(Event.COMPLETE, xmlLoaded);
}
public function startLoad():void {
xmlLoader.load(xmlReq);
}
function xmlLoaded(event:Event):void{
adventureXML = new XML(xmlLoader.data);
this.dispatchEvent(new Event("LoadXmlComplete"));
}
}
Here is how to use
var game:LoadXMLAdventure = new LoadXMLAdventure("file.xml");
game.addEventListener("LoadXmlComplete", onComplete);
game.startLoad();
private function onComplete(e:Event):void {
//do some thing
}

How to return a variable from an actionscript Event? Loading XML file

i'm trying to load an xml file from my assets folder.
I wrote this function :
public static function loadXML(i_fileURL:String):XML // i want to return the actual loaded xml here
{
var xml:XML;
var ldr:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest(i_fileURL);
ldr.addEventListener(Event.COMPLETE, onXMLLoad);
ldr.load(request);
//how can i return the loaded xml?
}
public static function onXMLLoad(e:Event):void
{
var ldr:URLLoader = URLLoader(e.target);
var myxml:XML = new XML(ldr.data);
trace(myxml.toXMLString());
//how can i return myxml to loadXML function?
}
Is there a different way to do this?
Thank you!
You can do something like a promise or future, where you return empty XML and then populate it with the actual XML when the call returns. Since you are using Flex, you have access to data binding, which should allow this approach to work just fine.
Note that you really shouldn't be using static methods for this, and your onXMLLoad member has no reason to be exposed. Here's what the updated code might look like:
package service {
public class XMLLoader {
//note that the existence of this variable means that you need
//to create a new instance of the Class each time you make a call.
protected var future:XML;
protected var _url:String;
public function loadXML(url:String):XML {
_url = url;
var request:URLRequest = new URLRequest(url);
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.Complete, onLoad);
loader.addEventListener(IoErrorEvent.IO_Error, onFail);
loader.addEventListener(SecurityErrorEvent.Security_Error, onFail);
future = ;
return future;
}
protected function onLoad(e:Event):void {
var loader:URLLoader = e.currentTarget as URLLoader;
var data:XML = loader.data as XML;
if (data) {
//you lose your original root node, because you want data
//binding to fire on the future object you returned.
future.setChildren(data.children());
}
}
protected function onFail(e:Event):void {
//both Security and IOerrors have a text property, but they
//can't both be cast to the same thing.
trace('failed to load', _url, e[text]);
}
}
}
One thing to be aware of with this method is that you need to keep a reference to the instance around until the data has updated, or else it could be garbage collected before it populates the future. Because of that, you're probably better off following the conventional approach of having your instance dispatch a custom event that carries the data that it was retrieving. If you want an example of that, post back and I can provide you an example.
you can't. because xml load request is asynchronous. when you called loadXML, onXMLLoad not yet arrived. so such work impossible asynchronous return. surely you can waiting the function process while loop using, but this method not good. because to full use a cpu resource, overkill. you must next process in onXMLLoad function. It's the most appropriate. or xml variable declared as global, and using a ENTER_FRAME or TimerEvent as a way to continue to operate if the xml is not null.
Since the URLLoader is asynchronous, it's not safe to create a static loader function as it would be quiet easy to mix up returned data during multiple calls. Even if you attempted to accomplish what you want with the use of Event.OPEN and a vector of URLs to keep track of the which completed data should belong to each URL, asynchronousity works on a first-come, first-served basis so it wouldn't be possible to have persistent alignment of the file URL and the returned data.
I suggest that you create instances of an XMLLoader class that uses a custom XMLLoaderEvent, which will return both the xml data and the associated file URL. The following code is untested, but with possible typos aside, it should work as intended.
Use Case
var xmlLoader:XMLLoader = new XMLLoader();
xmlLoader.addEventListener(XMLLoaderEvent.COMPLETE, xmlLoadCompleteEventHandler);
xmlLoader.load("myXMLFile.xml");
function xmlLoadCompleteEventHandler(event:XMLLoaderEvent):void
{
xmlLoader.removeEventListener(XMLLoaderEvent.COMPLETE, xmlLoadCompleteEventHandler);
trace(event.type, event.fileURL, event.xml);
}
XMLLoader.as
package
{
//Imports
import flash.events.EventDispatcher;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
//Class
public class XMLLoader extends EventDispatcher
{
//Properties
private var loader:URLLoader;
private var fileURL:String;
//Constructor
public function XMLLoader():void
{
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, loadCompleteEventHandler);
}
//Load
public function load(fileURL:String):void
{
this.fileURL = fileURL;
loader.load(new URLRequest(fileURL));
}
//Load Complete Event Hanlder
private function loadCompleteEventHandler(event:Event):void
{
loader.removeEventListener(Event.COMPLETE, loadCompleteEventHandler);
dispatchEvent(new XMLLoaderEvent(XMLLoaderEvent.COMPLETE, fileURL, XML(loader.data)));
}
}
}
XMLLoaderEvent.as
package
{
//Imports
import flash.events.Event;
//Class
public class XMLLoaderEvent extends Event
{
//Constants
public static const COMPLETE:String = "complete";
//Properties
public var xml:XML;
public var fileURL:String;
//Constructor
public function XMLLoaderEvent(type:String, fileURL:String = null, xml:XML = null)
{
super(type);
this.xml = xml;
this.fileURL = fileURL;
}
//Clone
public override function clone():Event
{
return new XMLLoaderEvent(type, fileURL, xml);
}
//To String
public override function toString():String
{
return formatToString("XMLLoaderEvent", "type", "fileURL", "xml");
}
}
}

Initialize Assets after Preload

Whenever I export the .swf file of my Flash game, I am receiving "TypeError: Error #1009: Cannot access a property or method of a null object reference.", along with a Runtime Shared Library Preloading Warning for my preloader. I have my timeline organized so that the first and third frames are both empty along with a stop(); command in the Actions layer. The second frame contains a single MovieClip that contains all of my exported assets, which are going to be initialized in the third frame of the timeline. None of my assets, except for the preloader, are exported in the first frame. What changes should I make to my Document Class for it to initialize the assets in the third frame?
Document Class:
package com.gameEngine.documentClass
{
import flash.events.*;
import flash.display.*;
import flash.geom.Point;
import com.gameEngine.assetHolders.*;
import com.gameEngine.assetHolders.Levels.*;
public class Document extends MovieClip
{
private static var _document:Document;
private var preloader:Preloader;
public var mcMain:Player;
public var restartButton:RestartButton;
public var spawnArea:SpawnArea;
public var level_1:Level_1;
public var level_2:Level_2;
public var level_3:Level_3;
public function Document()
{
addEventListener(Event.ADDED_TO_STAGE, init);
_document = this;
preloader = new Preloader(390, this.loaderInfo);
this.addChild(preloader);
preloader.addEventListener("loadComplete", loadAssets);
preloader.addEventListener("preloaderFinished", showLogo);
mcMain = new Player(this);
restartButton = new RestartButton(this);
spawnArea = new SpawnArea();
level_1 = new Level_1(this);
level_2 = new Level_2(this);
level_3 = new Level_3(this);
this.addChild(restartButton);
this.addChild(spawnArea);
this.preloader.x = 400;
this.preloader.y = 250;
restartButton.x = 822.95;
restartButton.y = 19;
spawnArea.x = 400;
spawnArea.y = 250;
trace ("Document Class Initialized");
// constructor code
}
public static function getInstance():Document
{
return _document;
}
private function loadAssets(event:Event):void
{
this.play();
}
private function showLogo(event:Event):void
{
this.removeChild(preloader);
}
public function init(event:Event)
{
if (stage.contains(spawnArea))
{
addChild(mcMain);
}
mcMain.x = spawnArea.x;
mcMain.y = spawnArea.y;
}
}
}
Preloader Class:
package com.gameEngine.assetHolders
{
import com.gameEngine.documentClass.*;
import flash.display.*;
import flash.events.*;
public class Preloader extends MovieClip
{
private var fullWidth:Number;
public var loaderInfo:LoaderInfo;
public function Preloader(fullWidth:Number = 0, loaderInfo:LoaderInfo = null)
{
this.fullWidth = fullWidth;
this.loaderInfo = loaderInfo;
addEventListener(Event.ENTER_FRAME, checkLoad);
}
private function checkLoad (event:Event):void
{
if (loaderInfo.bytesLoaded == loaderInfo.bytesTotal && loaderInfo.bytesTotal != 0)
{
dispatchEvent(new Event("loadComplete"));
phaseOut();
}
updateLoader(loaderInfo.bytesLoaded / loaderInfo.bytesTotal);
}
private function updateLoader(num:Number):void
{
progressBar.width = num * fullWidth;
}
private function phaseOut():void
{
removeEventListener(Event.ENTER_FRAME, checkLoad);
progressBar.gotoAndPlay(2);
if (progressBar.currentFrame == progressBar.totalFrames)
{
phaseComplete();
}
}
private function phaseComplete() : void
{
dispatchEvent(new Event("preloaderFinished"));
}
}
}
You have a lot of race conditions going on here. Many of these events could occur at relatively random times in relation to one another . . . you have to think asynchronously. That is, there can be no assumption that any object exists. E.g., in Document.init(), you check is if the spawnArea exists, but it is almost guaranteed not to at that point, and you never check for it again.
Without making any specific changes, I can recommend a generic solution. For any object (objB) you want loaded after another object (objA) is loaded, have objB created in the objA's ADDED_TO_STAGE handler. A simple example would be:
var objA:Whatever;
var objB:WhateverElse;
[...]
objA = new Whatever();
objA.addEventListener(Event.ADDED_TO_STAGE, objAAddedHnd);
[...]
public function objAAddedHnd(event:Event)
{
// remove the event, if no longer needed:
objA.removeEventListener(Event.ADDED_TO_STAGE, objAAddedHnd);
objB = new WhateverElse();
objB.addEventListener(Event.ADDED_TO_STAGE, objBAddedHnd);
}
[...]
public function objBAddedHnd(event:Event)
{
// remove the event, if no longer needed:
objB.removeEventListener(Event.ADDED_TO_STAGE, objBAddedHnd);
// and so on . . .
}
At this point, it shows that you would need to plan the timeline of object creation.