AS3 - e:MouseEvent = null? - actionscript-3

I was reading a tutorial on how you can make a turret follow the mouse, for a game, and I stumbled across something I've never seen before.
private function showGhostTurret(e:MouseEvent = null):void
{
var target_placeholder:Sprite = e.currentTarget as Sprite;
ghost_turret.x = target_placeholder.x;
ghost_turret.y = target_placeholder.y;
ghost_turret.visible = true;
}
I've never seen someone set the (e:Event) to null, like in the first line. Could someone please explain the purpose of doing this? Let me know if you need more information to answer.
Thanks.

That is a default parameter value. What it means is that the parameter e is optional so you can opt to not include it in a call to showGhostTurret() and e will be assigned the value null.
I'm not sure how that's useful in this particular case since, looking at the body of the function, e is most definitely required. You said this was part of a tutorial -- maybe it becomes useful later on?

Related

AS3, variables outside event handler function?

I have searched arond, but cant seem to find a proper answer to this. Lets say we have a super basic program which adds two numbers from 2 input text fields togehter and prints them out. Why cant i accses variables outside the event handler function? And what do i have to do in order to achieve this? The code is on a frame.
Why does this example not work? :
btn.addEventListener(MouseEvent.CLICK, cal);
var fnum:Number = Number(txt1.text);
var snum:Number = Number(txt2.text);
function cal(evt:MouseEvent){
txtOutput.text = String(fnum + snum);
}
And this example work?:
btn.addEventListener(MouseEvent.CLICK, cal);
function cal(evt:MouseEvent){
var fnum:Number = Number(txt1.text);
var snum:Number = Number(txt2.text);
txtOutput.text = String(fnum + snum);
}
I hate to be this guy, but I can't get it to replicate whats happening for you..
The only assumption that I can make is that the txt1.text & txt2.text aren't set yet when the button is clicked in example 1. Feel free to zip your project and dropbox it to me if you want me to investigate further :)

Dynamic Variable Name

I need to create a variable:
var numDots0:Number=0;
But when a button is clicked the variable numDots0 becomes numDots1, then numDots2 on a second click, and so on. I then need to be able to grab that new variable name and use it in a function.
That's a really, really weird request, but anyways:
You can use the key name of an Object to store the property and then change that:
var obj:Object = { numDots0: 0 };
And then when you want to change the name:
delete obj.numDots0;
obj.numDots1 = 1;
Or to easily increment you can use this:
var i:int = 0;
function increase():void
{
delete obj["numDots" + i];
obj["numDots" + (++i)] = i;
}
To access:
trace(obj.numDotsX); // where X is the most recent variable name.
I see absolutely no benefit or need for this, so I strongly suggest taking a look at what you're trying to do and making sure it makes sense and doesn't have a different application.
I am pretty sure you are going the wrong way about the problem you are trying to solve. Dynamic variable names are not something you read in the best practices book.
Anyway to answer your question in AS2 you could use the command eval which would evaluate a string as ActionScript, so you would use something like:
function onClicked(e:MouseEvent):void
{
counter++;
eval("var numDots" + counter +"+:Number=0;");
}
In AS3 that command has been removed (because it leads to bad coding practices - like the things you are trying to do), nevertheless someone implemented an evaluator in AS3:
http://eval.hurlant.com/
With this evaluator add the library to your project and add the following to the snippet above:
function eval(expression:String):void
{
var evaluator:com.hurlant.eval.Evaluator = new com.hurlant.eval.Evaluator();
var bytes:ByteArray = evaluator.eval(expression);
bytes = ByteLoader.wrapInSWF([bytes]);
var context:LoaderContext = null
var loader:Loader = new Loader();
loader.loadBytes(bytes, context);
}
the answer is to not do what you are trying to do and use an array, hash or vector instead. give us a bit more context, or the reason you want to achieve exactly what you want to and why you might believe you'd need a dynamic variable name like that. you shouldn't be using evals or anything that changes variable name at runtime because the gods of programming will strike you down where you stand. i.e., your program is going to break, and when it does, it's going to be harder to debug for sure.
if you are sure this is what you want to do, then i'm wrong, haha. good luck!

MovieClip extension

I have been trying to develop a CustomButton class that extends MovieClip, however I am having problems. I have got this error:
ArgumentError: Error #1063: Argument count mismatch on
mkh.custombutton::CustomButton(). Expected 2, got 0. at
flash.display::Sprite/constructChildren() at flash.display::Sprite()
at flash.display::MovieClip()
I've tried to debug my code, but it says
"Cannot display source code at this location."
I am not sure where is the problem, but I suppose it's in the constructor:
public function CustomButton( buttonlabel:String, animationAR:Array, active:Boolean=true, animated:Boolean = false, type:String = "free", group:int = 0 )
I would be very grateful if anyone helped me. Thank you.
EDIT2: I think I know why it's not appearing, so nevermind.
Seems like you must be instantiating CustomButton without passing it any arguments.
Like so:
var cBtn = new CustomButton();
However, you constructor has 2 arguments that must be passed - buttonLabel and animationAR (the rest are OK because they are assigned a default value).
So you should be doing something like this:
var cBtn = new CustomButton('Test', someArray);
I think I know what the problem is, now I hope I can explain to you clearly enough (English is not my first language). Did you by any chance make a graphic MovieClip in the Flash program and linked it to your CustomButton class? If so, be careful with the instances you might have on the stage, because when Flash creates the Sprites/Movieclips objects that are on the stage it calls their constructor without any parameters.
To avoid this, either:
Set default values for all parameters in your CustomButtonClass (EDIT: which would solve your problem, but is not very good practice)
Use addChild to put instances of your Button onto the stage (I recommend this one)
Hope this helps!
public function CustomButton(
buttonlabel:String,
animationAR:Array,
active:Boolean=true,
animated:Boolean = false,
type:String = "free",
group:int = 0
);
That's how you defined your constructor. This means that the first 2 arguments (buttonlabel abd animationAR) are required arguments. The rest are optional.
Now if you try to instantiate this like
var cb:CustomButton=new CustomButton();
You are not passing any arguments to the constructot, which will throw that error.
Note that this is what happens when you create the object directly in the UI.
A way to fix this would be to redefine the constructor as:
public function CustomButton(
buttonlabel:String="CustomButton",
animationAR:Array=[],
active:Boolean=true,
animated:Boolean = false,
type:String = "free",
group:int = 0
);
This makes all arguments optional and should work. Of course, you'll be best off putting the default value of the arguments as something you know will work. For example, in my example, the empty array default for animationAR could break your code, in which case you need to add this to the constructor body:
if(animationAR.length==0) {
animationAR.push(new Animation());
//YOU WILL HAVE TO CHANGE THIS LINE TO CORRESPOND TO YOUR CODE
}
OR ELSE, you could instantiate the object as
var cb:CustomButton=new CustomButton("My Crazy-ass CustomButton", animArray);

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.