i've never tried to do this before, so my head a swimming a bit. i'd like to have a public boolean called enabled in myClass custom class. if it's called to be changed, how do i trigger a function from the change?
should i add an Event.CHANGE event listener to my variable? can i do that? or is there a more standard way?
We usually use properties for that.
Properties are just like public variables for the outside -- you can set instance.enabled = true; and so forth.. But you define properties as getters and/or setters functions for the class.
They are the perfect place for custom logic to be executed on value changes.
For example:
public class CustomClass {
private var _enabled:Boolean = false;
public function set enabled(value:Boolean):void {
trace('CustomClass.enabled is now', value);
this._enabled = value;
}
public function get enabled():Boolean {
trace('CustomClass.enabled was retrieved');
return this._enabled;
}
}
Note that they can't have the same name as your private variable and you don't need both of them defined. Actually, you don't even need a variable for a setter/getter. You could use them just like any function -- they just supply you with a different syntax.
For example:
var object:CustomClass = new CustomClass();
object.enabled = false;
if (object.enabled) {
...
}
They are great to expose a simple API, keeping you from rewriting outside code if the class' internals have to change.
AS3 Reference on getters and setters.
Related
I want to create a internal ctor for class in ActionScript3 to make it immutable. I want that only another builder class will be allow to create instances of this immutable class.
I try to find the answer in Adobe's ActionScrtip 3 specification but it does not explain what happen when no public namespace (accessible) is define for ctor.
Immutable object:
package {
public class Immutable {
private var _value1:int;
private var _value2:int;
private var _value3:int;
public function Immutable(value1:int, value2:int, value3:int) {
_value1 = value1;
_value2 = value2;
_value3 = value3;
}
public function get value1():int {
return _value1;
}
public function get value2():int {
return _value2;
}
public function get value3():int {
return _value3;
}
}
}
As for access modifiers, internal is default.
The internal attribute is similar to the default access control in Java, although in Java there is no explicit name for this level of access, and it can be achieved only through the omission of any other access modifier. The internal attribute is available in ActionScript 3.0 to give you the option of explicitly signifying your intent to make a property visible only to callers within its own package.
As for constructor, you can't specify internal. If you omit access modifier, by default constructor will be accessible (public)
In ActionScript3, I am trying to access the properties of the caller object from a composite.
public class Robot {
...
private var controlPanel:ControlPanel;
...
public function Robot() {
...
cPanel = new ControlPanel();
...
}
}
My ControlPanel needs to access properties from Robot instance, but I don't think I can pass this when calling the ControlPanel...
public class ControlPanel{
...
public function ControlPanel() {
//How can I refer back to robot properties ?
//
}
}
I believe I am in the case of composition as a Robot has a ControlPanel. I am thinking of using events, but there are many properties I need to access.
What would be the best way to solve this?
You can always just let ControlPanel store a reference to its own Robot object, like so:
// ControlPanel
private var robot:Robot;
public function ControlPanel(robot:Robot) {
this.robot = robot;
}
And then, when creating the control panel:
// Robot
public function Robot() {
controlPanel = new ControlPanel(this);
}
Alternatively, you could create an even system of sorts, and then let the control panel dispatch them. You could create your own ControlPanelEvent class, and then let the Robot itself handle the results. For example, let's say you change a property called foo in the control panel. You could dispatch it like this:
var event:ControlPanelEvent = new ControlPanelEvent(ControlPanelEvent.PROPERTY_CHANGE, "foo", value);
Then you'd receive it like this:
public function Robot() {
controlPanel = new ControlPanel();
controlPanel.addEventListener(ControlPanelEvent.PROPERTY_CHANGE, updateProperty);
}
public function updateProperty(event:ControlPanelEvent):void {
if (event.key == "foo") {
this.foo = event.value;
}
}
However, that's wordy and unnecessary. You could also use ActionScript's array access notation in the event handler, which would be a simple one-liner:
this[event.key] = event.value;
Still, that's not entirely secure, since you might not want the control panel to be able to update all of a robot's properties. Instead, you could maintain a simple map of configurable properties that the robot can have, and update that instead:
private var configuration:Dictionary = new Dictionary();
public function Robot() {
// ...
configuration.foo = "bar";
configuration.baz = "qux";
//...
}
public function updateProperty(event:ControlPanelEvent):void {
if (configuration.hasOwnProperty(event.key))
configuration[event.key] = event.value;
}
There you go. Of course, you could always just store the configuration map in the ControlPanel itself, and let the Robot pull from that, but if you absolutely need it as a property of the robot, here are a few solutions.
You should be able to pass 'this':
cPanel=new ControlPanel(this);
public class ControlPanel{
...
protected var _robot:Robot;
public function ControlPanel(robot:Robot){
_robot = robot;
}
}
You can't use arguments when extending display classes, but ControlPanel extends Object (by default as no extend is defined.
For display classes you can set the property after creating it:
cPanel=new ControlPanel();
cPanel.robot = this;
public class ControlPanel{
...
public var robot:Robot;
public function ControlPanel(){
}
}
I would like to extends the Event class to add some events I am using in game.
But I don't want the new Event Class to have the old public static types..
For instance I don't want to have:
NewEventClass.ENTER_FRAME
How do you go about extending the Event class without getting the old types mixed in?
Is there any way to outsmart AS3 to leave out the uneeded types?
Or should I avoid creating a new Event type altogether and just add the new strings?
Extending Event is only really necessary if you want to add some extra properties to it, for example:
public class EnemyEvent extends Event
{
// Constants used to represent event type
public static const ENEMY_KILLED:String = "killed";
// Event properties
public var score:int = 0;
/**
* Constructor
* Retain Event behaviours
*/
public function EnemyEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
So that when you dispatch this event from an enemy you can go:
var evt:EnemyEvent = new EnemyEvent(EnemyEvent.ENEMY_KILLED);
evt.score = myScoreValue;
dispatchEvent(evt);
And then make use of the score property from the listening method within the game engine:
enemy.addEventListener(EnemyEvent.ENEMY_KILLED, _countKill);
function _countKill(e:EnemyEvent):void
{
gameTotalScore += e.score;
if(gameTotalScore > 100) getAchievement();
e.target.removeEventListener(e.type, _countKill); // <-- woudn't work without extending event either
}
If you just need to store constants to use in addEventListener(x, ..), new Event(x), etc then you can just make a class that holds these and doesn't have anything to do with Event:
public class CustomEvents
{
public static const MY_CUSTOM_EVENT:String = "myCustomEvent";
}
So that you can just use these as needed:
new Event(CustomEvents.MY_CUSTOM_EVENT);
addEventListener(CustomEvents.MY_CUSTOM_EVENT, _listener);
The former method is still preferable as it's tidier and more logical.
Additionally; your note about your custom event having constants such as ENTER_FRAME isn't the case anyway, because they are static and belong to Event. You'll get this error if you try access ENTER_FRAME through the example in your answer:
1119: Access of possibly undefined property ENTER_FRAME through a
reference with static type Class.
Good day!
I noticed that in anonymous event handler this is referenced to global, not my class. I know that I can use outer referenced variables (because it creates closures), but how to get right this context?
Simple example:
_movieClipClassVariable = new MyCustomSpriteSubclass();
_movieClipClassVariable.addEventListener(MouseEvent.CLICK, function(event:Event):void {
trace(this); //gives 'global'
});
What about memory usage and garbage collection objects with anonymous handlers? Is declaring handlers as class method better?
I think most of the time declaring a method inside the class is the better choice, (because you don't have to think about the scope, to remove listeners, to not getting accidentally garbage collected, ...) however there are cases where an anonymous function might be a concise and clear approach. It depends.
For instance:
public class XYZ extends Sprite {
...
private function renderBtn() : void {
var btn : SomeButton = new SomeButton();
var ref : XYZ = this;
btn.addEventListener(MouseEvent.CLICK, function(event:Event):void {
trace("XYZ instance: " + ref);
btn.removeEventListener(MouseEvent.CLICK, arguments.callee);
removeChild(btn);
proceed();
});
addChild(btn);
}
private function proceed() : void {
...
Yes you should really declare a class to not end up in thin air.
The delegating technique you used there is mostly used in Javascript. As a flash developer I'd recommend naming the function and keeping it all together in a class.
As for the garbage collection, you'd need to removeEventListener with the exact same syntax than the addEvenListener in order to free it for garbage collection.
HTH
Instead of adding the functions declaration right inside the Event listener, declare the method in your class.
class myTestClass
{
private function listenForEvents():void
{
_movieClipClassVariable = new MyCustomSpriteSubclass();
_movieClipClassVariable.addEventListener(MouseEvent.CLICK, onClipClassClickHandler);
}
private function onClipClassClickHandler(event:MouseEvent):void
{
trace(this); // this is the instance of current "myTestClass"
}
}
More details on anonymous handlers:
http://www.ultrashock.com/forums/actionscript/anonymous-functions-remove-listener-121738.html
Is there a way to use getters and setters for Vectors?
Say, in my Main class, I would like to write
myVector.push(item);
and in another class, I have written:
public function get myVector():Vector.<int> {
return _opponentCardList;
}
public function set myVector(myVector:Vector.<int>):void {
_myVector = myVector;
}
This doesn't really work as you have to set _myVector to a Vector. But what if you just want to push(), pop() or splice?
Your getter and setter use different variables - is that intentional?
If the getter/setter myVector is in a different class, you need an instance of that class in your Main class before you can access it from there.
//in the Main class.
var obj:OtherClass = new OtherClass();
//the constructor of OtherClass should initialize _myVector
//otherwise you will get a null pointer error (1009) in the following line
obj.myVector.push(item);