MovieClip(root) line seems to be crashing my game - actionscript-3

Inside my classes, I invoke this function
MovieClip(root).increaseScore();
which handles the score in the main .as file.
It all works fine during the execution of the level. However when the level is finished and the screen goes to another frame, the game crashes and gives me this error
TypeError: Error #1009: Cannot access a property or method of a null
object reference.
on the line above.
How do I fix this?
Thanks
edit:
This is were I tell it to addScore, this is in the GameController.as file
private function removeBubble(bubble, addScore:Boolean)
{
var delay:Timer = new Timer(200, 1);
delay.addEventListener(TimerEvent.TIMER_COMPLETE, function(e:TimerEvent)
{
if(bubble.parent==mcGameStage)
{
var j:int = bubbleList.indexOf(bubble);
bubbleList.splice(j,1);
if(addScore) bubble.addScore();
mcGameUI.txtScorePlayer.text = String(playerScore);
mcGameStage.removeChild(bubble);
}
e.currentTarget.removeEventListener(e.type, arguments.callee);
checkWin();
});
delay.start();
}
here is the checkWin function:
private function checkWin()
{
if (playerBlue + playerRed + playerYellow + playerOrange + playerPurple + playerGreen == 0)
{
gameWin();
}
}
private function gameWin()
{
while (bubbleList.numChildren > 0)
{
bubbleList.removeChildAt(0);
}
mcGameUI.btnMixBlue.removeEventListener(MouseEvent.CLICK, mixBlue);
mcGameUI.btnMixRed.removeEventListener(MouseEvent.CLICK, mixRed);
mcGameUI.btnMixYellow.removeEventListener(MouseEvent.CLICK, mixYellow);
mcGameUI.btnNeedle.removeEventListener(MouseEvent.CLICK, activateNeedle);
mcGameStage.removeEventListener(Event.ENTER_FRAME,update);
mcGameStage.removeEventListener(MouseEvent.CLICK, checkToHit);
removeEventListener(Event.ADDED_TO_STAGE, gameAddedToStage );
stage.removeEventListener(KeyboardEvent.KEY_DOWN,keyDownHandler);
stage.removeEventListener(KeyboardEvent.KEY_UP,keyUpHandler);
if (mouseCursor != null)
{
mouseCursor.removeEventListener(Event.ENTER_FRAME,followMouse);
mouseCursor = null;
}
gotoAndPlay("level1win");
}
And inside my classes,
public function addScore()
{
root["increaseScore"]();
}
This is what increaseScore does
public function increaseScore()
{
playerScore += 1000;
}
So where is the null object? D:
Also I am very inexperienced using the debugger so I apologize if this could be easily solved with that. I tried it and couldn't figure it out before coming here.
What is OPP method?
What is FrameScript?
Also, the class is MovieClip
thanks :)

Fixing this is done with the help of the debugger!
The error is telling you that something is null and you're trying to access it. That's not good, so let's think what could be null?
1) Your movie clip instance might be null.
2) increaseScore() method of your movie clip instance might try to access something that is null
You didn't post any code that I could analyse at the time I'm writing this answer, so I can't say for sure.
Some possible problems:
Your class is not called MovieClip, but you're just trying to cast your root object to a MovieClip. Thing is, MovieClip's don't have a increaseScore() method. You should instead call the increaseScore() method with
root["increaseScore"]();
This will call the method of your root timeline, but since we are using weak-typing, you might have problems with debugging it later. But I guess that's the price you pay when writing all the code in a frame instead of using OPP approach.

The true reason here is said in the error: Cannot access a property or method of a null object reference.
What this means is that MovieClip(root) is null (it doesn't even bother to check if the function is present). The reasons for this might be three:
Your document class is not MovieClip (could be Sprite).
The class you use root in, is not DisplayObject. The property is part of the DislpayObject class and so if you use it on other kind of classes that does not extend it, it won't work.
You are using this piece of code before you have added the instance to the stage (most probable cause). The root property represents the top-most display object in the tree. If you haven't added the child to the tree, it has no roots :) Check out parent - the error should be the same.

Related

ActionScript 3 - use [brackets] instead of getChildByName

I have a MovieClip inside library, linkaged to MyObject and it contains a textField.
I don't know how I can access this textField without using the getChildByName method.
Apparently, the 3rd section works when object is on stage (without using addChild). But when using addChild I think there has to be some kind of casting; which I don't know how.
var childElement: MyObject = new MyObject();
childElement.name = "theChildElement";
container.addChild(childElement);
btn.addEventListener(MouseEvent.CLICK, changeText);
function changeText(event: MouseEvent): void
{
var targetBox:MovieClip = container.getChildByName(childElement.name) as MovieClip;
targetBox.textField.text = "hello"; // THIS WORKS
// This works too:
// MovieClip(container.getChildByName("theChildElement"))["textField"].text = "hello"; // THIS WORKS TOO.
// THIS DOESN'T WORK. why?
// container["theChildElement"]["textField"].text = "hello";
}
As confusing as it may seem, instance name, and name are not the same. From your code you should always be able to get to your MC by it's variable name. To get your last like to work you could just use this.
childElement["textField"].text = "hello";
There is a difference between Symbols created by the Flash IDE, which aggregate other DisplayObjects and programmatically created DisplayObjects.
When a DisplayObject is created in the Flash IDE, it's instance name can be used to resolve the instance as a property - which means it can be accessed via []. The [] can be used to access properties or keys of dynamic declared classes - like MovieClip. This necessary because you'll most likely down cast to MovieClip instead of using the symbol class created by Flash. That is not possible when simply using addChild, addChildAt or setChildAt from the DisplayObjectContainer API.
It is always the save way to access it via getChildByNameand check for null because otherwise your app, website or whatever is doomed for 1009 errors as soon as someone is changing the symbols.
I'd create a bunch of helper methods, like
// not tested
function getChildIn(parent:DisplayObjectContainer, names:Array):DisplayObject {
var child:DisplayObject, name:String;
while (names.length > 0) {
name = names.shift();
child = parent.getChildByName(name);
if (!child) {
// log it
return null;
}
if (names.length == 0) {
return child;
}
}
// log it
return null;
}
function getTextFieldIn(parent:DisplayObjectContainer, names:Array):TextField {
return getChildIn(parent, names) as TextField;
}
function getMovieClipIn(parent:DisplayObjectContainer, names:Array):MovieClip {
return getChildIn(parent, names) as MovieClip;
}
Your third method doesn't work because you are trying to call the ChildElement by it's name
without using getChildByName method. On the other hand, you shouldn't call your textField textField, because that's already an actionScript property.
Your should rather call it 'displayText' for example.
For a textField called 'displayText' contained in childElement :
function changeText(event:MouseEvent): void
{
childElement.displayText.text = "hello";
}

AS3 Add event listener to a movieclip within a movieclip

I currently have two movieclips one called mcInvFrame, and one called btnCloseInv (It is a movieclip, and I know the naming convention is wrong). btnCloseInv is located inside mcInvFrame. I have two files Inventory.as and my main document class. I can load the mcInvFrame just fine to the stage and everything works as expected. However when I try to access the btnCloseInv movieclip i get errors. Here is the code for Inventory.as I have commented out my most recent failed attempt
package{
import flash.display.MovieClip;
import flash.events.MouseEvent;
public class Inventory extends MovieClip
{
public var inv:MovieClip = new mcInvFrame;
public function Inventory()
{
addChild(inv);
/*var invClose:MovieClip = inv.btnCloseInv;
invClose.addEventListener(MouseEvent.CLICK, CloseInventory);
function CloseInventory($e:MouseEvent):void
{
this.parent.removeChild(inv);
}*/
}
}
}
What I need to know is can/should i create a variable within inventory.as for the button that I can access from the main document? If so how?
P.S. I have been searching the forums and trying various solutions but I either didn't understand the implementation or they were not suitable for this situation. The most common error I receive is "Error #1009: Cannot access a property or method of a null object reference." Occasionally I will receive an error stating that an object has no properties.
you cant register event on stage.movieclip.movieclip2 , i have tried to do the same thing before, but it wont work, try to create btnCloseInv outside, then use this code
btnCloseInv.x = mcInvFrame.x + numberHere;
btnCloseInv.y = mcInvFrame.y + numberHere2;
if you doesn't want to use this code, AS3 - Button inside MovieClip triggers MC's event
EDIT: if you set mcInvFrame.buttonMode = true it will not work

Problem to move a MC on foreground

i've a problem to a set a different child index of a Movieclip. This is the code:
function processMusica():void
{
var loadStatus:int=0
var lastHeight:int=0
for (var m=0; m < myXML.BLADE[sup].child("brano").length(); m++)
{
var titolobrano:TextField=new TextField
bladearray[sup].contenitore.addChild(titolobrano)
titolobrano.text=myXML.BLADE[sup].brano[loadStatus].titolo
lastHeight=titolobrano.height
titolobrano.doubleClickEnabled=true
titolobrano.addEventListener(MouseEvent.DOUBLE_CLICK, riproducibrano)
loadStatus+=1
}
if (isPlaying==false)
{
var riproduzioneDetails:MovieClip=new MovieClip
riproduzioneDetails.name="riproduzioneDetails"
var artista:TextField=new TextField
artista.name="artista"
bladearray[sup].contenitore.addChild(riproduzioneDetails)
riproduzioneDetails.x=475
riproduzioneDetails.addChild(artista)
}
setChildIndex(bladearray[sup].contenitore.riproduzioneDetails, bladearray[sup].contenitore.numChildren-1) //<------ PROBLEM HERE!
I want to move "riproduzioneDetails" MC on foreground, but when i attempt to launch application, it give me this error: TypeError: Error #2007: Parameter child must be non-null
I can see two potential problems, but it is hard to say because all the variables are not declared in your code. There may be a problem with your test condition:
if (isPlaying==false)
{
...
}
If "isPlaying" is true, your object "riproduzioneDetails" is never created, so of course it will be null. You have to create your object outside the "if" condition, before testing "isPlaying".
Another potential problem is the way you access your object, with "bladearray[sup].contenitore.riproduzioneDetails". As you used the "addChild" method to store your object, there should logically be a kind of "getChild" method. For instance,
setChildIndex(bladearray[sup].contenitore.getChildByName("riproduzioneDetails"),bladearray[sup].contenitore.numChildren-1)
... may be better.

Clearing eventListeners on a FileReference object

I have a strange issue! I am trying to remove an event listener on a FileReference object by calling a function, but it seems not to be removed, and I do not understand why.
Here is the code:
private function clearFileUploadListeners(file:FileReference, index:String):void {
var dispatchEvent:Function = function(event:Event):void {
dispatch(event.type, event, index);
};
file.removeEventListener(Event.COMPLETE, dispatchEvent);
var bool:Boolean = file.hasEventListener(Event.COMPLETE);
if (bool)
trace("ERROR");
}
When I run this code, the trace actually happens. I don't understand why this boolean returns true, when I just tried to remove the eventListener just above! I guess I am probably doing something really stupid because it seems like a strange error.
I hope someone can please help me on this issue.
EDIT:
I believe it has to do with the fact that the dispatchEvent function is defined inside another function when I add the listener:
private function upload(file:FileReference, index:String):void {
var dispatchEvent:Function = function(event:Event):void {
dispatch(event.type, event, index);
};
file.addEventListener(Event.COMPLETE, dispatchEvent);
}
The problem is that I need to access this "index" variable from the listener, and I can't set it as a global variable as each file has it's own index and it's a burden if I have to extend each event class to keep track of the index (Event, ProgressEvent, ..). I hope someone can please help me on this.
EDIT2:
I actually found a temporary solution, I am not sure if it is the best! I put my removeListener method actually inside the upload method, but made it a variable. As AS3 allows dynamic object, I attached this method to one of my object, and so I just call the reference to the method when necessary. The event is actually removed. Is this a good solution please?
Thank you very much,
Rudy
You're right, it has to do with the fact that you're defining a function inside another function, then using it to handle events.
Each time the function upload is called, it creates a new closure, and assigns a reference to it to the dispatchEvent variable, which is then passed to the addEventListener class. So each time upload is called, it is using a new, different closure in the call to addEventListener. Similarly, in the clearFileUploadListeners function, a new closure is being created on each call (which happens to have the same code each time, but isn't the same function object). The call to removeEventListener does nothing if the given callback has not been added as an event listener for the given event, which is the case here.
To solve your problem, you need to store a reference to the closure that you pass to the addEventListener function. This way, you can get a reference to the same closure that was added when you need to remove it later in clearFileUploadListeners.
You can try something along the lines of the following code (untested):
import flash.utils.Dictionary;
var callbackRegistry:* = new Dictionary();
private function upload(file:FileReference, index:String):void {
var dispatchEvent:Function = generateFileUploadCompleteCallback();
callbackRegistry[file] = dispatchEvent;
file.addEventListener(Event.COMPLETE, dispatchEvent);
}
private function clearFileUploadListeners(file:FileReference, index:String):void {
var dispatchEvent:Function = callbackRegistry[file];
callbackRegistry[file] = null;
file.removeEventListener(Event.COMPLETE, dispatchEvent);
var bool:Boolean = file.hasEventListener(Event.COMPLETE);
if (bool)
trace("ERROR");
else
trace("YAY, ALL OK!");
}
private function generateFileUploadCompleteCallback(index:String):Function {
return function(event:Event):void {
dispatch(event.type, event, index);
};
}
Two other things to note on this subject.
If you must utilize a native Event directly then you should pretty much always make sure and use these last three optional params :
myObject.addEventListener( Event.COMPLETE, myFunction, false, 0, true );
Check Grant Skinner's post on the subject here :
http://gskinner.com/blog/archives/2006/07/as3_weakly_refe.html
And the very best practice of all is to ALWAYS (seriously always) use Robert Penner's Signals (instead of custom events) and his NativeSignals (to wrap needed native Flash events).
Five times faster than Flash's native events.
Always safe with weak references.
Any number of typed payload(s) in each Signal.
Get the SWC here :
https://github.com/robertpenner/as3-signals
Signals were designed to solve the very problem you are having.
Imagine instead of creating an array and managing that to remove all listeners if you could just call :
signalBtnClicked.removeAll();
or
signalBtnClicked.addOnce( function( e : MouseEvent ) : void { /* do stuff */ } );
Knowing that the closure you just created will immediately be dereferenced once it is called and happily go night night when the GC makes its rounds.

How do I reference an object, add a tween to it and put this in an Eventlistener in AS3?

I'm having quite some trouble to try and get an app I wrote in AS2 to AS3. The reason I need to go to AS3 is something icky, so I won't go into detail about it.
I've got 90% of the application running with the new code.
Now I've come to the point where I have to convert this code from AS2,
function setAnimation(theObject,id)
{
theObject.vensterid=id;
theObject.onEnterFrame = function()
{
var myHoriTween:Tween = new Tween (this,"_x",Strong.easeOut,this._x,(130+((theObject.vensterid-frameno)*260)),1,true);
}
}
setAnimation(venster0,0);
, to AS3. My attempt of doing this ended up like
function setAnimation(anObject,id) {
var theObject = this[anObject];
theObject.vensterid=id;
function slideHorizontal(event:Event)
{
var myTween:Tween = new Tween (theObject,"x",Strong.easeOut,this.x,(130+((theObject.vensterid-frameno)*260)),1,true);
}
theObject.addEventListener(Event.ENTER_FRAME,slideHorizontal);
}
setAnimation(venster0,0);
and gives me the following non-error (it doesn't show as a compiler error, but as output):
TypeError: Error #1010: A term is undefined and has no properties.
at sliding_windows_as3_fla::SlideMenu_1/setAnimation()
at sliding_windows_as3_fla::SlideMenu_1/frame1()
I think this is very strange since it doesn't say anything about which term (and there are quite a lot) and googling didn't find me an explanation either.
I didn't get the chance to test your code, because it's difficult to set up a context for it, but my thoughts would be:
You should declare the parameter types: function setAnimation(anObject:Object,id:uint):void. It's at least good practice.
var theObject = this[anObject]; is completely unnecessary if your variable anObject is an object. I think var theObject = this[anObject]; doesn't work, theObject ends up being null and that's why you get your error. If you have declared a variable called venster0, that is the instance of a class that extends Object, then you can pass the reference to it without any other trouble.
Depending on the object you work with, theObject.vensterid=id; might not work. The class that theObject instances must have the 'vensterid' property, or you will get `1119: Access of possibly undefined property vensterid through a reference with static type ...
I think your problem here is following string:
var theObject = this[anObject];
Just replace it with
var theObject = anObject;
I hope that's what you need.
Alternatively instead of
setAnimation(venster0,0);
you could pass an instance name (i.e. String):
setAnimation("venster0",0);
That will work because by this['propertyname'] you are actually accessing Object's property by name.
Just going to throw out that using the built-in Tween classes in Flash/Flex is a pain. Look into using Tweening libraries instead: Tweener, TweenLite, etc. They are much easier to work with, and you don't have to worry about maintaining references until the Tween completes.