How do I properly extend the AS3 Point class? - actionscript-3

I come to need a bit more info from my points, so I thought I'd add a previousX, previousY so that I can get a deltaX and deltaY. Now, this works fine if I make a simple self-contained class.
I think, however, that I'd like to extend the flash.geom.Point class to benefit from the other functions and calculations it offers.
So I extended Point and modified the x and y setters as I needed. When I compile, I get an error that these setters are not marked for override when they should be. So I override, but then I get an error that says these do not override any function.
Any clue where I screwed up?
Here is the class:
package net.jansensan.geom
{
import flash.geom.Point;
/**
* #author Mat Janson Blanchet
*/
public class UpdatablePoint extends Point
{
private var _previousX : Number = 0;
private var _previousY : Number = 0;
private var _deltaX : Number = 0;
private var _deltaY : Number = 0;
// ********************************************************************
// [ PUBLIC METHODS ]
// ********************************************************************
public function UpdatablePoint(x:Number=0, y:Number=0)
{
super(x, y);
}
override public function toString():String
{
return "(x=" + super.x + ", y=" + super.y + ", previousX=" +
_previousX + ", previousY=" + _previousY + ", deltaX=" +
_deltaX + ", deltaY=" + _deltaY + ")";
}
// ********************************************************************
// [ GETTERS / SETTERS ]
// ********************************************************************
override public function set x(x:Number):void
{
_previousX = super.x;
super.x = x;
_deltaX = super.x - _previousX;
}
override public function set y(y:Number):void
{
_previousY = super.y;
super.y = y;
_deltaY = super.y - _previousY;
}
public function get previousX():Number
{
return _previousX;
}
public function get previousY():Number
{
return _previousY;
}
public function get deltaX():Number
{
return _deltaX;
}
public function get deltaY():Number
{
return _deltaY;
}
}
}

The Point class isn't implemented using get/set-methods, rather it contains only public variables: public var x : Number and public var y : Number. See the documentation here.
Since you cannot override any public getters or setters you can't detect when anyone using your class is writing to these variables. What you can do is remove the extends Point and add a Point instance variable to your UpdatablePoint. Then change your getters and setters for x/y from super.[x/y] to myPointInstance.[x/y].
If you need to expose more functionality from the Point class, you could easily just wrap it. I.e. Say you want to use the 'equals'-method from Point, just create a copy of that method signature and make it's body contain: return myPointInstance.equals(p);

Point does not have public function set x or public function set y.
So you can't override them as they don't exist. Point is very low-level, my guess would be that Adobe didn't add these functions to avoid the increase in overload.

Related

DataNavigator and INavigatorLayout

I think my question is for advanced flex developers or experts.
My task is relatively simple: I need to build some kind of horizontal "gallery" component, and IMGO the new DataNavigator from new Apache 4.10 SDK suits best for this task.
In other words I need the same as DataNavigator with a specific INavigatorLayout - the same as CarouselLayout , but much simpler - without any perspective but with smooth transition between items. May be such layouts already exists?
Thanks for advance.
Andrew.
Well, I found the solution that satisfied me, it works for me, but the main idea:
package
{
import com.greensock.TweenLite;
import spark.layouts.HorizontalLayout;
import spark.layouts.supportClasses.INavigatorLayout;
import mx.core.IVisualElement;
/**
* #author Andrew
*/
public class LobbyNavigatorLayout3 extends HorizontalLayout implements INavigatorLayout
{
public function LobbyNavigatorLayout3()
{
}
private var _selectedElement:IVisualElement;
public function get selectedElement() : IVisualElement
{
return _selectedElement;
}
public function get selectedIndex() : int
{
return target.getElementIndex(_selectedElement);
}
public var ttt:Number = 0;
public function set selectedIndex(value : int) : void
{
if (!target) return;
var firstEl:IVisualElement = target.getElementAt(0);
if (!firstEl) return;
var shift:Number = (600 + gap) * value;
TweenLite.to(this, 1, {ttt:shift, onUpdate:function ():void {
updateDisplayList(target.width, target.height);
}} );
_selectedElement = target.getElementAt(value);
}
override public function updateDisplayList(unscaledWidth : Number, unscaledHeight : Number) : void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
for (var i : int = 0; i < target.numElements; i++)
{
var el:IVisualElement = target.getElementAt(i);
el.setLayoutBoundsPosition(el.x - ttt, 0);
}
}
}
}
need to say that this is for useVirtualLayout = false; Some extra work needed to fix this, but it suits for me for now, and if you are feel like you are able to do this - you are welcome!

AS3 - Error #1180: Call to a possibly undefined method kill.

This one is driving me crazy for a couple hours. I try to call a method kill(); (in function takeDamage()) which is in the same class, yet it won't find it.
package classes.ship
{
imports ...
public class Ship extends MovieClip
{
var speed:Number;
var shootLimiter:Number;
public static var health:Number;
public static var maxHealth:Number;
public function initialize()
{
var stageReff:Stage = this.stage as Stage;
stage.addEventListener(KeyboardEvent.KEY_DOWN, reportKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, reportKeyUp);
stage.addEventListener("enterFrame", move);
}
//code
public static function takeDamage(d):void
{
health -= d;
if(health <= 0)
{
health = 0;
kill();
}
Main.healthMeter.bar.scaleX = health/maxHealth;
}
public function kill():void
{
var boom = new Explosion();
stage.addChild(boom);
boom.x = this.x;
boom.y = this.y;
this.visible = false;
//Main.gameOver();
}
//code
}
}
Has it to do with var stageReff:Stage = this.stage as Stage; ?
Thanks in advance.
kill() is an instance method, but takeDamage is a static class method. You can't call instance methods from a static class method. You can only call instance methods when you have an instance reference to call it on.
nice simple one for early in the year!
You have declared the function 'takeDamage' as a static method - this means that it does not belong to a particular instance of the class Ship, instead it belongs to the Class itself. Static methods and properties can be a bit confusing if you are new to OOP, but are easily explained through a quick example:
Class Member Property
In this example we declare a new Class definition for a Ship where we can define the speed of the ship instance via setSpeed().
public class Ship {
private var speed : Number;
public function setSpeed(value : Number) : void {
this.speed = value;
}
public function getSpeed() : Number {
return this.speed;
}
}
Now we will create a couple of ships and set their speed:
var scoutShip : Ship = new Ship();
scoutShip.setSpeed(500); // Scout's are fast!
var cargoShip : Ship = new Ship();
cargoShip.setSpeed(10); // Cargo ships are sloooow.
trace("Scout Ship Speed: " + scoutShip.getSpeed()); // 500
trace("Cargo Ship Speed: " + cargoShip.getSpeed()); // 10
As you can see from the above, each new instance of Ship that we create can have its own Speed - this is a fundamental of Object Orientated Programming (where the Ship is the Object and it's speed is the data).
Static Property
Now we will create another class, this time called StaticShip which uses a static property instead, note the use of the static keyword:
public class StaticShip {
private static var speed : Number;
public function setSpeed(value : Number) : void {
this.speed = value;
}
public function getSpeed() : Number {
return this.speed;
}
}
Because the speed property is static it is shared across all instances of StaticShip; for example:
var scoutShip : StaticShip = new StaticShip();
scoutShip.setSpeed(500); // So the scout should move at 500
var cargoShip : StaticShip = new StaticShip();
cargoShip.setSpeed(10); // ... and the Cargo move at 10, as before
trace("Scout Ship Speed: " + scoutShip.getSpeed()); // 10
trace("Cargo Ship Speed: " + cargoShip.getSpeed()); // 10
Notice how both StaticShips move at 10 - this is because we set the Speed of the 'cargoShip' instance last - as the 'speed' property in StaticShip is declared static it is shared across all instances of that Class.
Now, just as you can have static properties in Classes, you can also have static functions. Usually, when you call a Class' method (ie: setSpeed()) you need to invoke that method on an instance (ie: scoutShip.setSpeed(500);), however, Static Methods allow you to interact with other static members of a given class, here's another example:
Static Method Example
public class StaticMethodShip {
private static var speed : Number;
// Note that setSpeed is now declared as static
public static function setSpeed(value : Number) : void {
this.speed = value;
}
public function getSpeed() : Number {
return this.speed;
}
}
Now, we can still create new instances of StaticMethodShip as before, but because we have now declared 'setSpeed' as static, we can't invoke setSpeed on an instance:
var scoutShip : StaticMethodShip = new StaticMethodShip();
// This call will trigger Error #1180 - Call to a possibly undefined Method because
// setSpeed was declared as static.
scoutShip.setSpeed(500);
Instead, we can now only invoke the setSpeed() method on the StaticMethodShip Class, ie:
// Set the speed of all StaticMethodShip instances.
StaticMethodShip.setSpeed(250); // all StaticMethodShips travel at 250.
// Proof!
var shipOne : StaticMethodShip = new StaticMethodShip();
var shipTwo : StaticMethodShip = new StaticMethodShip();
trace("ShipOne Speed: " + shipOne.getSpeed()); // 250
trace("ShipTwo Speed: " + shipTwo.getSpeed()); // 250
Static methods are useful when you want to define behaviour for all instances of a given Class (ie: all StaticMethodShips move at the specified speed, all fade out Tweens last for 0.25 seconds, etc); but they are also used in common design Patterns such as the Static Factory Method
Now, to the reason you are seeing your error - member level methods are able to invoke static methods, ie:
public class StaticExampleOne {
public static function getName() : String {
return "Robbe";
}
public function traceName() : void {
// traces 'Robbe'.
trace(getName());
}
}
In usage (new StaticExampleOne().traceName()) this works just fine - member methods can access static methods without problem, however this doesn't work the other way around:
public class StaticExampleTwo {
private var name : String = "Robbe";
public function getName() : void {
return this.name;
}
public static function traceName() : void {
// Throws Error #1180.
trace(getName());
}
}
This is because static methods have no scope (ie: They do not know which instance of the Class they are referring too because they can only reference other static members) and therefore can not access class level members (methods and properties).
To solve your problem you could introduce a new static property to Ship called 'STAGE' (typically static properties are written in ALL CAPS to differentiate them from member properties) and then make your kill() method static.
Hope this helps and good luck!
Jonny.

How to pass a reference to class into another class

The question is a bit silly. I am trying to implement a skill updating system. So to explain.
There is a class
class AppInfo
{
public static var power:int = 10;
public static var speed:int = 20;
}
and class SmartButton which should take a reference to one of the static variables e.g. power in a constructor and increment it on the given value.
e.g.
class SmartButton
{
public function onClick(skillReference:int = <AppInfo.power>, incrementVAlue:int = 10)
{
skillReference += incrementVAlue
}
}
I want this code to update the value of the power in AppInfo class. But this doesn't happen... I assume because the skill was passed as value not as reference...
Can you suggest a way of solving the task?
Thanks
Your assumption is correct, ints are passed by value rather than reference. One direct approach would be to encapsulate power into a reference type (a class) rather than a value type:
class Skill {
public var value:int;
public function Skill(val:int) {
this.value = val;
}
}
class AppInfo
{
public static var power:Skill = new Skill(10);
public static var speed:Skill = new Skill(20);
}
Then passing power should pass it as a reference to the instance. Though you would have to change your implemenation a bit to use skillReference.value instead.
Aside from that, I think there are a couple of ways to abstract what you want out. One way would be use an interface and leverage some dependency injection.
interface ISkills
{
function get power():int;
function set power(val:int):void;
}
class AppInfo implements ISkills
{
private static _power:int = 0;
public function get power():int { return _power; }
public function set power(val:int):void { _power = val; }
}
class SmartButton
{
public function onClick(skills:int = ISkills, skill:String = "power", incrementVAlue:int = 10)
{
skills[skill] += incrementVAlue
}
}
The idea here that you want to decouple your usage from your implementation. In this case SmartButton doesn't need to know how Skills work just how to operate on them. It loses its reference to the static class AppInfo in favor of an injectable instance. There are some advantages to this approach, it makes it easier to test and easier to swap implementations later if you decide that a static class isn't the best implementation idea without having to update a bunch of classes/code. Also, rather than injecting ISkills into the method, you could inject it into the constructor of SmartButton, and keep a private reference to the skill container.
Another approach would be to use a functional approach.
class SmartButton
{
public var defaultWorker:Function = function(val:int):void {
AppInfo.power += val;
}
public function onClick(worker:Function = undefined, incrementValue:int = 10):void
{
if(worker == undefined) worker = defaultWorker;
worker.call(this, incrementValue);
}
}
Again, in this case, rather than tightly coupling your implementation to use the AppInfo class directly, you inject a "worker" for it do the work for you (if the worker is undefined then use the default worker. You can then swap out which property gets changed by changing the closure that gets passed in. For instance if you wanted to change speed instead then you would call:
var smartButton:SmartButton;
smartButton.onClick(function(val:int):void { AppInfo.speed += val});
Not quite as succinct as it could be, but it gets the job done.
The obligatory "elegantly sophisticated" approach using the command pattern:
Interface Command {
function execute():void;
}
Class UpdatePower implements Command {
private var appInfo:AppInfo;
private var delta:int;
public function UpdatePower(appInfo:AppInfo, delta:int) {
this.appInfo = appInfo;
this.delta = delta;
}
public function execute():void {
appInfo.delta += delta;
}
}
Class SmartButton {
var command:Command;
public function SmartButton(command:Command) {
this.command = command;
}
public function onClick(event:Event):void {
command.execute();
}
}
I would probably implement this in a slightly different way.
Maybe something like;
class Properties {
private var _properties:Dictionary = new Dictionary();
public function setValue(key:String, value:int) {
_properties[key] = value;
}
public function getValue(key:String):int {
if( !_properties[key] ) return 0;
else return _properties[key];
}
public function modifyValue(key:String, value:int) {
setValue(key, getValue(key) + value);
}
}
class SmartButton
{
public function onClick(target:Properties, key:String, incrementValue:int = 10) {
target.modifyValue(key, incrementValue);
}
}
Or something along those lines.

AS3 1120 Error on instantiating

I am trying to create a basic functionality in AS3.
I have a class like this:
package tax
{
public class Calculator
{
public function Calculator()
{
}
private var grwage:Number;
private var taxes:Number;
private var superannuation:Number;
private var insurance:Number;
private var net:Number;
public function getGrwage():Number {
return grwage;
}
public function setGrwage(grwage:Number):void {
this.grwage = grwage;
}
public function getTaxes():Number {
return grwage;
}
public function setTaxes(taxes:Number):void {
this.taxes = grwage * 0.2;
}
public function getSup():Number {
return superannuation;
}
public function setSup(superannuation:Number):void {
this.superannuation = superannuation * 0.05;
}
public function getIns():Number {
return insurance;
}
public function setIns(insurance:Number):void {
this.insurance = insurance * 0.1;
}
public function getNet():Number {
return net;
}
public function setNet(net:Number):void {
this.net = grwage - taxes - superannuation - insurance;
}
}
}
Just some getters and setters.
And in my mxml file:
import tax.Calculator;
public var calculate:Calculator = new Calculator();
calculate.setGrwage(1000);
trace(calculate.getTaxes());
trace(calculate.getSup());
trace(calculate.getIns());
trace(calculate.getNet());
I get an error 1120 Access to undefinded property when I try to access calculate. Do you have any ideas?
Thanks.
I guess you're mixing up things a bit between getters and setters. If a variable depends on another, you have to write the calculation in its getter. This variable shouldn't exist as a private member and the accesses to it must always be done through the appropriate getter.
for example:
public function getNet():Number {
return getGrwage() - getTaxes() - getSup() - getIns();
}
So here, you only need one setter (setGrwage), you can remove the others ones and put the calculation in the getters.
Note that there is a more appropriate syntax for setters and getters in AS3.
class GetSet
{
private var privateProperty:String;
public function get publicAccess():String
{
return privateProperty;
}
public function set publicAccess(setValue:String):void
{
privateProperty = setValue;
}
}
There are multiple issues with your class. So lets redesign it
The main issue you had was you were not initiating the vars before you accessed them as they had a NULL value the flash player was throwing you that error.
As you can see in the constructor I set a default value of 0 so that they will never be NULL.
package tax {
public class Calculator{
private var _grwage:Number;
private var _taxes:Number;
private var _superannuation:Number;
private var _insurance:Number;
public function Calculator() {
this._grwage = 0;
this._taxes = 0;
this._superannuation = 0;
this._insurance = 0;
}
public function get grwage():Number {
return this._grwage;
}
public function set grwage(val:Number):void {
this._grwage = val;
}
public function get taxes():Number {
return this._taxes;
}
public function set taxes(val:Number):void {
this._taxes = val * 0.2;
}
public function get superannuation():Number {
return this._superannuation;
}
public function set superannuation(val:Number):void {
this._superannuation = val * 0.05;
}
public function get insurance():Number {
return this._insurance;
}
public function set insurance(val:Number):void {
this._insurance = val* 0.1;
}
public function get net():Number {
return this._grwage - this._taxes - this._superannuation - this._insurance;
}
}
}
import tax.Calculator;
public var calculate:Calculator = new Calculator();
//Now due to the fact we are using true setter/getter methods.
calculate.grwage = 1000;
trace( calculate.taxes); // should be 0 as we are defaulting to the initial value
trace( calculate.superannuation );// should be 0 as we are defaulting to the initial value
trace( calculate.insurance );// should be 0 as we are defaulting to the initial value
trace( calculate.net );// should be 1000 as we are defaulting to the initial values of everything but grwage
The answers here are all good, but they all miss the primary point:
You're trying to get your variables before you set them.
import tax.Calculator;
public var calculate:Calculator = new Calculator();
calculate.setGrwage(1000);
trace(calculate.getTaxes());
trace(calculate.getSup());
trace(calculate.getIns());
trace(calculate.getNet());
This will throw an error (due to the way you've set up your class), because you never called setSup(), setIns(), and setNet() before trying to use them.
While your class may not be written in the best way, it is syntactically sound.
Try calling each setter before your getter. I think you'll solve your issue.

Does AS3 provide any way to stop a returned object being modified?

If an AS3 method returns a reference to a complex type, is there any way to make that 'readonly', like how you can have const member functions in C++? An architecture I want to use calls for a class building itself from a passed template object... and really the template object should not be modifiable. I'm currently forced to add call-back enumerators and/or lots of extra accessor methods.
Flex has an ObjectUtil.clone() method that will make a deep copy. The copy will still by modifiable, but since it's a copy, the changes won't propagate back to the original.
The method is no complicated so if you're not using Flex, just add this to a util class:
public static function copy(value:Object):Object
{
var buffer:ByteArray = new ByteArray();
buffer.writeObject(value);
buffer.position = 0;
var result:Object = buffer.readObject();
return result;
}
There is no way to do that in AS3, there is Sam's way of doing it, but it still requires copying that object before you return it, depending on the complexity of that object, it can impact the performance.
Immutable interfaces are a near-equivillant to const-correctness. Here's an example:
interface CPoint {
function get x():Number;
function get y():Number;
}
class Point implements CPoint {
private var _x:Number;
private var _y:Number;
public function get x():Number { return _x; }
public function get y():Number { return _y; }
public function set x(val:Number) { _x = val; }
public function set y(val:Number) { _y = val; }
public function normalize():void {
var length:Number = Math.sqrt(_x*_x + _y*_y);
_x /= length;
_y /= length;
}
public function Point(x:Number, y:Number) {
_x = x; _y = y;
}
}
If you return a Point as a CPoint reference, then its fields cannot be altered. You can do an explicit cast to a Point from a CPoint to force access, but you can do the same thing with const casting in C++.
Unfortunately, AS3 doesn't support covariance like it should, so things get unnecessarily difficult for const sub-objects. For example, if you had a Line class that was made up of two points, you might want to say line.start.x = 47; if you have full access to the line, but allow reading of line.start.x through an immutable interface. You could do this if there was covariance, but instead you'll need to add separate get properties for mutable and immutable properties. So, you'd end up instead with line.cstart.x for reads from a CLine. Something like this:
interface CLine {
function get cstart():CPoint;
function get cend():CPoint;
}
class Line implements CLine {
private var _end:Point;
private var _start:Point;
public function get cend():CPoint { return _end; }
public function get cstart():CPoint { return _start; }
public function get end():Point { return _end; }
public function get start():Point { return _start; }
public function Line(x1:Number, y1:Number, x2:Number, y2:Number) {
_start = new Point(x1, y1);
_end = new Point(x2, y2);
}
}
I would create a flash.utils.proxy object. You could create a proxy object that has read only implementation of a child that is passed in.
Here is the documentation for creating a proxy object. http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/utils/Proxy.html
Note: Proxy is pretty damn slow, since you'll be bypassing native object checking, and replacing it with a function call -- which when using a lot will be slow. I would do some simple performance testing first.
note: This is pseudo-code.
use namespace flash_proxy;
dynamic class ReadOnly extends flash.utils.Proxy {
private var target:Object;
public function ReadOnly(target:Object) {
this.target = target;
}
flash_proxy function getProperty(name:*):*
return target[name];
}
flash_proxy function setProperty(name:*, value:*):void
// throw an error or do nothing
}
}
You could then do:
var readOnly:ReadOnly = new ReadOnly(stage.loaderInfo.parameters);
readOnly.someparameter = 'newvalue';
trace(readOnly.someparameter); // should be old value