as3 error: access of possibaly undefind property through a reference with static type flash.display:DisplayObject - actionscript-3

I have this as3 project, and in frame one of the timeline I tried to load a swf movie named "menu" and in this loaded movie I have an instance of a button named "button1", and I want to add a new EventListener to this "button1". my code is here:
var theLoader:Loader = new Loader();
var address:URLRequest = new URLRequest("menu.swf");
theLoader.load(address);
theLoader.contentLoaderInfo.addEventListener(Event.COMPLETE , swfDidLoad);
function swfDidLoad(evt:Event){
if(theLoader.content){
addChild(theLoader);
var button:SimpleButton = theLoader.content.button1;
button.addEventListener(MouseEvent.CLICK, handler1);
}
}
function handler1 (event:MouseEvent):void
{
removeChild(theLoader);
gotoAndStop(10);
};
but I get this undefind property error. what should I do? Am i doing this right at all?

The reason you are getting that error is because you are trying to access button1 on theLoader.content which is a non-dynamic DisplayObject (this means that only explicitly defined properties/methods are valid). You must first cast it to a MovieClip (which is dynamic).
You should change that line to:
var button:SimpleButton = MovieClip(theLoader.content).button1;

Related

Actionscript 3: How can I change the name property of content returned by Loader.load()? Error 1074

I have a Scaleform movie that I want to serve as the container for my game's user interface. I want it to be able to load and unload other swf files that will serve as different HUDs and menus. And when I load a swf, I need Actionscript to register the name of the DisplayObject, so the game will know which "view" (i.e., HUD, pause menu, shop menu, etc.) just loaded.
I am able to load other swfs using Loader.load(), but for some reason I can't change their names. I keep getting error 1074.
[Edit: Adding more info on the error. "Error #1074: Illegal write to read-only property." Apparently I'm trying to write to a read-only property. So how do I make that property not-read-only? name isn't read-only in any other UIComponents I'm loading.]
public function loadView(viewName:String, movieFileName:String):void
{
var loader:Loader = new Loader();
var url:URLRequest = new URLRequest(movieFileName);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
loader.name = viewName;
loader.load(url);
}
private function loaderComplete(e:Event):void
{
var loader:Loader = e.currentTarget.loader as Loader;
var content:DisplayObject = LoaderInfo(e.target).content; // This returns the content I'm looking for, but I always get error 1074 if I try changing its name
// var content:DisplayObject = loader.getChildAt(0); // This also returns the content I'm looking for, but it also gives me error 1074 if I try changing its name
// content.name = loader.name; // This line always gives me error 1074
// var newView:View = View(content); // Even if I try casting the content as a custom .as class...
// newView.setName(loader.name); // public function setName(newName:String):void { this.name = newName; } // ...I still get error 1074
addChild(content);
}
Am I just not allowed to change the name property of swf movies that get returned? Can I set the name in the document class of the swf? I tried that too, but no matter where I change the name inside the document class (their class extends scaleform.clik.core.UIComponent, and I try setting the name in the constructor and in configUI), it always seems to get overwritten when I addChild().
[And another edit. Apparently there is some confusion over the "name" property. Here's how it works...]
I start off with this code. I just put it in frame 1 of my movie.
import TestUIComponent;
var testUIComponent:TestUIComponent = new TestUIComponent();
testUIComponent.name = "Something something";
trace("This is the testUIComponent's name: " + testUIComponent.name);
addChild(testUIComponent);
This is the class TestUIComponent:
package {
import scaleform.clik.core.UIComponent;
public class TestUIComponent extends UIComponent {
public function TestUIComponent() {
}
override protected function configUI():void {
super.configUI();
enableInitCallback = true;
}
}
}
Nothing fancy there. It's just an Actionscript 3 scaleform.clik.core.UIComponent (need to specify that because I think there are at least 3 different UIComponents in different packages). enableInitCallback is a property that used to be visible in Flash's properties panel, but now in AS 3, it seems you can only change it in code.
So I run that code, and this is what I see:
This is the testUIComponent's name: Something something
CLIK Load: root.Something something
If I comment out the line
// testUIComponent.name = "Something something";
and then run the code, this is what I see:
This is the testUIComponent's name: instance1
CLIK Load: root.instance1
Going back to my original problem, the text that comes after "CLIK Load:" is the name that is getting sent from the UI to the game. I need that name to be something meaningful so the game knows what just got loaded. The swf files I am trying to load have Document Classes that are children of scaleform.clik.core.UIComponent, so I thought their name properties would work the same way as the TestUIComponent above. Apparently it doesn't. And as you can see all the way back up at the top, I even cast the loader.content as a View (which is a child of UIComponent), and I still can't change the name.
This is what I meant in the comments. Try something like this:
//... where you declare your variables, make them public to use in other functions...
public var myString = "";
//... later where you declare functions...
public function loadView(viewName:String, movieFileName:String):void
{
var loader:Loader = new Loader();
var url:URLRequest = new URLRequest(movieFileName);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
//loader.name = viewName;
myString = viewName; //# 1) update String here to re-access in next function...
loader.load(url);
}
private function loaderComplete(e:Event):void
{
var content:DisplayObject = LoaderInfo(e.target).content; // This returns the content I'm looking for, but I always get error 1074 if I try changing its name
// content.name = loader.name; // This line always gives me error 1074
content.name = myString; //# 2) set content name to whatever myString holds (ie the viewName)...
addChild(content);
}

addEventListener of random MovieClips

I'm making a game in AS3.
I've got in my code :
public var _classes:Array = new Array(poubelle1, poubelle2, poubelle3);
public var _movieClips:Array = new Array();
public function apparitionDechet(event : TimerEvent):void{
_movieClips.push(new _classes[Math.floor(Math.random() * _classes.length)]());
stageRef.addChild(_movieClips[_movieClips.length-1]);
I'm trying to put an addEventListener on the MovieClips.
The player should be able to click on a MovieClip when it's appearing or he can wait. Few will appears, and he can click on them at any moments.
Each clicks will make the MoviClip disapear..
So I've put :
_movieClips[1].addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
public function removePoubelle(e:MouseEvent):void{
if(e.target=="_movieClips[0]"){
trace("ok1");
}
if(e.target=="_movieClips[1]"){
trace("ok2");
}
if(e.target=="_movieClips[2]"){
trace("ok3");
}
but it's not that...
Do you know how I can do that ?
It's my first time that I'm using the randomly apparition of MovieClips...
Thank you very much,
EDIT
So I've followed your tips and did this :
public function apparitionDechet(event : TimerEvent):void{
var mc:DisplayObject = new _classes[Math.floor(Math.random() * _classes.length)]();
_movieClips.push(mc);
stageRef.addChild(mc);
mc.addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
public function removePoubelle(e:MouseEvent):void{
var mc:DisplayObject = e.target;
var i:int=_movieClips.indexOf(mc);
if (i>=0){
_movieClips.splice(i,1);
mc.parent.removeChild(mc);
}
}
But I've got the error 1118 Implicit coercion of a value with static type Object to a possibly unrelated type flash.display:DisplayObject
EDIT 2
Quick question though, is it possible to do :
if(stageRef.contains(poubelle1)) {
trace("poubelle1détécté");
}
if(stageRef.contains(poubelle2)) {
trace("poubelle2 détécté");
}
?
movieClips poubelle1 and poubelle 2 are defined like this
public var _classes:Array = new Array(poubelle1, poubelle2, poubelle3);
public var _movieClips:Array = new Array();
it doesn't seem to work if I do that.(error 1027 Contrainte implicite d'une valeur du type Class vers un type sans rapport flash.display:DisplayObject) Any idea why ?
Do you want me to create a new post ?
Thank you
If you are to remove the movieclip that was clicked, you already have it as the event's target. So you get its parent and call removeChild(). Don't forget to remove the event listener off the target.
public function removePoubelle(e:MouseEvent):void {
var mc:DisplayObject = e.target as DisplayObject;
if (!mc) return; // typecast failed
mc.parent.removeChild(mc);
// mc.removeEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
// the line above might not be needed as the listener weakly references the mc
}
And you put the listener as soon as you create your new movie clip.
public function apparitionDechet(event : TimerEvent):void {
var mc:DisplayObject = new _classes[Math.floor(Math.random() * _classes.length)]();
_movieClips.push(mc);
stageRef.addChild(mc);
mc.addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
See, how you can avoid referencing the newly created movie clip without ugly _movieClips[_movieClips.length-1] construction? You just make a local variable which is then instantiated for your random MC out of _classes, and you then use the variable to do everything else that's needed at the time of creation.
But, this is still not enough - your "poubelle" is still inside your _movieClips array, so it'll grow. You need to clean up the array too. So, add this code to removePoubelle:
var i:int=_movieClips.indexOf(mc);
if (i>=0) _movieClips.splice(i,1);
This gets the position of the clicked movie clip inside your array, and if it's a valid one (zero or more) the array is told to remove that element.
You shouldn't be using just _movieClips[1]. That refers specifically to the SECOND object in your _movieClips Array.
You should add your eventListener as soon as the MovieClip is added to the _movieClips Array. You can add it to the most recently '.push'ed MovieClip, like this:
_movieClips[_movieClips.length-1].addEventListener(MouseEvent.CLICK, removePoubelle);
Do that on the next line after the line where MovieClip is pushed into the _movieClips Array.
Your event handler (the removePoubelle function) will be passed a MouseEvent and you can refer to the .target of this event to isolate WHICH MovieClip has been clicked:
private function removePoubelle(e:MouseEvent):void {
var mcToRemove:DisplayObject = e.target;
removeChild(mcToRemove); // note there is no need to refer to .parent as the MovieClip was added in this Class
// more code to come - see below
}
Also note: Because each MovieClip has an eventListener added WHEN IT IS CREATED, e.target will ALWAYS refer to whichever MovieClip was clicked.
The only other thing you may want to implement is removing the MovieClip from the _movieClips Array. This can be done in the removePoubelle function too:
var removalIndex:int = _movieClips.indexOf(MovieClip(e.target)); // here I am 'casting' the e.target to the type MovieClip. That basically just means I'm changing it's type from DisplayObject to MovieClip (which is a subclass of DisplayObject)
if (removalIndex>-1) {
_movieClips.splice(removalIndex, 1); // this line removes one item at the index returned from the _movieClips.indexOf... line above.
}
Let me know if any of this doesn't make sense.
}

AS3 Retrieving a file from FZip

I am currently coding a game and I am storing all external files in a zip file to save space.
I am trying to use Fzip to load an image out of the Zip file and display it on stage but I am having no luck.
FZip Site - http://codeazur.com.br/lab/fzip/
Example 1 Site - http://www.tuicool.com/articles/7VfQr2
I have got the zip file to load and return me the number of files inside it but I cannot figure out how to retrieve an image called "test.png".
import deng.fzip.FZip;
import deng.fzip.FZipFile;
import deng.fzip.FZipLibrary;
import flash.display.*;
var zip:FZip = new FZip();
var loaderzippy:Loader = new Loader();
function unzipObb():void {
zip.load(new URLRequest("file://"+File.applicationStorageDirectory.nativePath+"/cake2.zip"​));
zip.addEventListener(Event.OPEN, onOpen);
zip.addEventListener(Event.COMPLETE, dosomething); }
function dosomething(evt:Event):void {
trace("dosomething");
var img_file:FZipFile = zip.getFileByName("test.png");
var loaderzip:Loader = new Loader();
loaderzip.loadBytes(img_file.content);
var image:Bitmap = Bitmap(loaderzip.content);
addChild(image); }
I just get returned #2007: Parameter child must be non-null.
Any help would be great. Thanks.
When you load the PNG bytes using loaderzip.loadBytes(), you need an event listener to check when it's done loading. The documentation itself states:
The loadBytes() method is asynchronous. You must wait for the "init" event before accessing the properties of a loaded object.
Because you're trying to access the code directly after you called loadBytes(), at which point it hasn't actually loaded anything, you're getting a null bitmap.
Although you can access the data once the "init" event has been dispatched, it's best to wait for the "complete" event, since you want your image to be done loading (rather than halfway done).
I've tweaked your code to something that should work:
function dosomething(evt:Event):void {
trace("dosomething");
var img_file:FZipFile = zip.getFileByName("test.png");
var loaderzip:Loader = new Loader();
loaderzip.contentLoaderInfo.addEventListener(Event.COMPLETE, getImage);
loaderzip.loadBytes(img_file.content);
}
function getImage(e:Event):void {
var loaderInfo:LoaderInfo = e.currentTarget as LoaderInfo;
var image:Bitmap = Bitmap(loaderInfo.content);
addChild(image);
}
(I haven't tested this myself but it's just to give you the general idea anyway.)
Note that you should add the event listener to loaderzip.contentLoaderInfo, not just loaderzip. That's what actually dispatches the event and it also contains the loaded content.

AS3 objects (buttons) from library not accessible from within certain functions

I'm working on a simple image processor in AS3 and as usual I almost had it finished when an annoying little problem appeared. I want to open an image using a dialog box, and once the image is open I want the "selectBtn" (which is in my Library) to disappear. However when I try the code below I get a: "Error #1009: Cannot access a property or method of a null object reference". I'm sure it's something simple but I just can't figure it out!
// declare variables
var image:Bitmap;
var loader:Loader = new Loader();
var fileRef:FileReference= new FileReference();
selectBtn.addEventListener(MouseEvent.CLICK, openImage);
function openImage(event:MouseEvent):void {
fileRef.browse([new FileFilter("Images", "*.jpg;*.gif;*.png")]);
fileRef.addEventListener(Event.SELECT, onFileSelected);
}
function onFileSelected(e:Event):void {
fileRef.addEventListener(Event.COMPLETE, onFileLoaded);
fileRef.load();
}
function onFileLoaded(e:Event):void {
loader.loadBytes(e.target.data);
image = Bitmap(loader.content);
selectBtn.visible = false;
}
What Todd says is accurate: where is "selectBtn" defined? Is it on a stage, or nested within some movieclip instance?
Assuming the button exists on the stage, and the code runs in the same frame as the button exists, you could try this.stage.selectBtn.visible = false;
Otherwise, trace that you have selectBtn as a reference. Or you could declare a variable to reference it. i.e.:
var selectBtnRef:Button = this.stage.selectBtn;

How Do I Reference a Dynamically Instantiated Object in AS3? (Added a Moviclip to the Stage)

This is something that has been bugging me since I took up AS3 a year ago.
Example. I make a class that extends a Movie Clip and call it "LoaderBar" all it is is some text that says "Loading" and below it another movie clip that is a plain rectangle that I call "lBar"
When I call a function to load my image I add the Loader to the stage as such..
function imageLoader(URL:String):void
{
var loader:Loader = new Loader(new URLRequest(URL));
loader.contentLoaderInfo.addEventListner(ProgressEvent.PROGRESS, progressHandler);
var loadBar:Loader Bar = new LoaderBar();
addChild(loadBar);
}
function progressHandler(e:Event):void
{
var pcent:Number = e.getBytesLoaded / e.getBytesTotal;
// HERE IS WHERE I'D LIKE TO MAKE DIRECT REFERENCE TO MY LOADBAR;
loadBar.lBar.width = pcent*100;
}
Essentially I just want to tell the lBar in the loadBar Movie Clip to be the width of the percent *100. (so that when the clip is loaded the loader bar is 100 pixels wide).
My problem is this. When I add the loadBar to the stage inside of a function, I cannot make reference to it inside of another function without doing some hack making a global variable outside of my function like...
var loadBarClip:MovieClip;
and inside the load function assigning the loadBar to the loadBarclip as such
loadBarClip = loadBar.
I feel like this is redundant. Does anyone know of anyway of accessing my loadBar without making a reference variable?
If it's just for that handler, you could make the handler anonymous and keep in inside the current scope.
var loadBar = new LoaderBar();
var loader:Loader = new Loader(new URLRequest(URL));
loader.contentLoaderInfo.addEventListner(
ProgressEvent.PROGRESS, function (e:Event):void {
var pcent:Number = e.getBytesLoaded / e.getBytesTotal;
loadBar.lBar.width = pcent*100; //Here you are making a direct reference.
}
);
If you really want to encapsulate your scopes you could use closures:
returnFromEncapulatingClosure = function(){
var loadBar = new LoaderBar();
var loader:Loader = new Loader(new URLRequest(URL));
return {
loadBar: loadBar,
loader: loader
};
}();
That allows you to bundle together some references so they won't clobber other parts of code, and you could refer to it with:
returnFromEncapulatingClosure.loader.contentLoaderInfo.addEventListner(ProgressEvent.PROGRESS, progressHandler);
function progressHandler(e:Event):void {
var pcent:Number = e.getBytesLoaded / e.getBytesTotal;
returnFromEncapulatingClosure.loadBar.lBar.width = pcent*100;
}
As a footnote, when you extend the Movie Clip, add a method that sets the lBar.width. Something like:
loadbar.setLBarWidth = function (w:number) {
this.lBar.width = w;
}
I don't see a major problem in having the variable declared outside of the imageLoader function. If you were writing this in a class instead of the timeline then it would just be a class member variable and there is nothing wrong with that. They exist for this very reason.
If your deadset wanting to keep the loadBar variable local then you could always do this:
in the imageLoader function:
var loadBar:Loader Bar = new LoaderBar();
loadBar.name = "loadBar";
addChild(loadBar);
in the progressHandler function:
getChildByName("loadBar");
Since lBar (or loadBar for that matter) is an element that you need to manage at a class level, you should indeed make it a class member. There is nothing wrong with it ;)