A problem I've been running into lately involves trying to change a variable indirectly.
e.g.
var health:int = 100;
var currentVar = health; //I want currentVar to equal "health"
var currentVar -= 50; // I want to subtract 50 from health;
trace(health) // 100
I could set health to equal currentVar afterward, but this is not possible in some situations.
How can I make as3 understand I don't want current var to equal what health equals, but health itself?
You cannot create a reference (or pointer) to a basic type in As3.
They are always passed by value.
But you can do this with objects for example.
In your case, an object oriented approach is probably better anyway.
Who's health is it? Who's mana is is?
It's probably one thing that has both mana and health (and probably many other properties)
So whatever function is trying to modify different properties, should receive the whole object instead.
You can (should) define a class that contains all those properties with according setter and getter functions.
Than accept that class as a parameter.
Within that function you can decide what property to modify.
Here's one example how that could look like:
package
{
import flash.display.AVM1Movie;
import flash.display.Sprite;
public class FlashTest extends Sprite
{
public function FlashTest()
{
var hero:Hero = new Hero();
trace(hero.health, hero.mana);
dealDamage(hero);
trace(hero.health, hero.mana);
absorbEnergy(hero);
trace(hero.health, hero.mana);
doRandomThing(hero);
}
private function dealDamage(hero:Hero):void
{
hero.health -= 5;
}
private function absorbEnergy(hero:Hero):void
{
hero.mana -= 5;
}
private function doRandomThing(hero:Hero):void
{
if (Math.random() >= 0.5)
{
hero.health -= 15;
}
else
{
hero.mana -= 15;
}
}
}
}
internal class Hero
{
private var _health:uint = 100;
private var _mana:uint = 100;
public function set health(value:uint):void
{
_health = Math.max(0, value);
if(_health == 0)
{
trace("I'm dead");
}
}
public function get health():uint
{
return _health;
}
public function set mana(value:uint):void
{
_mana = Math.max(0, value);
if(_mana == 0)
{
trace("No more magic");
}
}
public function get mana():uint
{
return _mana;
}
}
Related
Why can't I compile this little piece of code ?
I'm overriding x and y in my class so that's why I need super.x.
public class SimpleSprite extends Sprite
{
override public function set x(value: Number): void
{
super.x -= 12;
//super.x = super.x - 12;
}
}
// or
public class SimpleSprite2 extends Sprite
{
public function get xx(): Number
{
return super.x;
}
public function set xx(value: Number): void
{
super.x = value;
}
public function SimpleSprite2()
{
xx -= 12;
super.x -= 12;
// Error: Syntax error: expecting semicolon before minusassign.
}
}
I know I can write super.x = super.x - 12; but im lazy and i dont want 2 look 4 these inconsistencies when i get hit by syntax errors im also very much accustomed to those shortcuts with -=
Looks like my IDE (FlashDevelop 4.2.3) bears the blame for this.
There's nothing syntactically wrong with what your doing. Your code works fine in my Flash Builder 4.7.
However, remember that instance.x -= value; is short for
instance.x = instance.x - value;
//ie.
instance.[set]x = instance.[get]x - value;
I mention this because of your top code:
override public function set x(value: Number): void
{
super.x -= 12;
//super.x = super.x - 12;
}
Makes me think you're trying to do something funny. Why are you decrementing in the set function?
If you need to have a seperate x value for a subclass, but can't change the interface (ie. you still want users to be able to say mySubClassInstance.x = 123.0;) do this:
public class DerivedSprite extends Sprite
{
protected var derivedX:Number;
public override function get x():Number
{
super.x = derivedX;
return derivedX;
}
public override function set x(value:Number):void
{
super.x = value;
derivedX = value;
}
public function DerivedSprite()
{
super();
derivedX = super.x;
}
}
I'm rather new with AS3, so forgive me if my question appears stupid.
Let me clarify what I want specifically: The Player goes on a mission, in which he would earn points when he catches some objects on his way, for example object A gives one point, object B gives two points, object C gives bonus points, etc... IF the Player hits some obstacle on his way, he fails the mission and he has to replay the game until he reaches the destination (end of level).
So what I want is that how I can store the points that the Player earned before he hits the obstacle and the earned points would be accumulated every time the Player has to replay the game until he reaches the end of the game level so that the total points the Player earned would be the sum of all the points, including those that he earned before he actually reaches the end of the game level.
My Class Player is the subclass of the class Box that belongs to the framework WCK. Is there a way for me to implement Dependency Injection properly to pass the variables to the other class in order to store them there?
Thank you in advance for your help!
ADDED: Here is what I implemented based on Amy's example with some adjustment to suit the setup of my game. It still doesn't work as the value of variable _totalEarnedPoints did not update the variable _earnedPoints in Class PlayerClient via EventDispatcher when I tried to retrieve it back in Class Player for accumulating all the points. I'm not sure what I did wrong or missed anything else? Can anyone please point out and tell me how to retrieve back the variable _totalEarnedPoints from Class PlayerClient?
CLASS Player:
package
{
public class Player extends Box
{
private var contacts:ContactList;
private var myLoader:SWFLoader;
private var mcTemp:MovieClip;
private var bonusPoints:int = 0;
private var onePoint:int = 0;
private var twoPoints:int = 0;
private var objectACount:int = 0;
private var objectBCount:int = 0;
private var objectCCount:int = 0;
private var replayCount:int = 0;
protected var _earnedPoints:int;
private var myPlayerClient:PlayerClient = new PlayerClient();
}
public function Player()
{
super();
}
public override function create():void
{
super.create();
listenWhileVisible(this, ContactEvent.BEGIN_CONTACT, handleContact);
contacts = new ContactList();
contacts.listenTo(this);
}
public function handleContact(e:ContactEvent):void
{
//Detect collison with other static objects
var myObjectA:objectA = e.other.m_userData as objectA;
var myObjectB:objectB = e.other.m_userData as objectB;
var myObjectC:objectC = e.other.m_userData as objectC;
var myObstacle:obstacle = e.other.m_userData as obstacle;
if(myObjectC)
{
objectCCount++;
myObjectC.remove();
if (objectCCount > 0)
{
bonusPoints = bonusPoints + 5;
}
}
else if(myObjectA)
{
objectACount++;
myObjectA.remove();
if (objectACount > 0)
{
onePoint = onePoint + 1;
}
}
else if(myObjectB)
{
objectBCount++;
myObjectB.remove();
if (objectBCount > 0)
{
twoPoints = twoPoints + 2;
}
}
else if(myObstacle)
{
var myEarnedPoints:int = myPlayerClient.totalEarnedPoints;
_earnedPoints = bonusPoints + onePoint + twoPoints + myEarnedPoints;
dispatchEvent(new Event("EarnedPointChanged"));
myLoader = new SWFLoader("myMovie.swf",{container:swfHolder,alpha:0,onComplete:completeHandler});
//adds the loader content to the display list before raw content is loaded.
addChild(myLoader.content);
stop();
myLoader.load();
function completeHandler(e:LoaderEvent):void
{
replayCount++;
if (replayCount <= 1)
{
TweenMax.to(e.target.content, 1, {alpha:1});
mcTemp = myLoader.rawContent;
mcTemp.gotoAndPlay(1);
}
else if (replayCount >= 1)
{
mcTemp = myLoader.rawContent.stop();
myLoader.unload();
mcTemp = null;
}
}
myObstacle.remove();
}
}
public function get earnedPoints():int
{
return _earnedPoints;
}
}
CLASS PlayerClient
package
{
public dynamic class PlayerClient extends MovieClip
{
private var _totalEarnedPoints:int;
protected var _player:Player;
public function get player():Player
{
return _player;
}
public function set player(value:Player):void
{
if (value != _player)
{
if (_player)
{
//[prevent memory leaks][2]
_player.removeEventListener("EarnedPointChanged", updatePlayerScore);
//don't need this if we don't have a player
removeEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
_player = value;
if (_player)
{
//listen for changes
_player.addEventListener("EarnedPointChanged", updatePlayerScore);
//we added a listener to the new player, need to make sure is removed
addEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
}
}
protected function cleanUp(e:Event):void
{
_player.removeEventListener("EarnedPointChanged", updatePlayerScore);
}
protected function updatePlayerScore(e:Event):void
{
_totalEarnedPoints = _player.earnedPoints;
}
public function get totalEarnedPoints():int
{
return _totalEarnedPoints;
}
}
}
First, what you're doing is going to cause you a world of hurt unless your project is a banner or something that you know 100% that you will work on for 2 days and never look at again. See http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/
What you need to realize is that your Points.pointsEarned looks like it will only ever change if you create a new instance of Player, and if you have several players, each new Player will have a higher number of points to start out with than the one before. That's probably not what you want. You don't really say what it is that you want, so let me throw out one thing that you could want and tell you how to code it. If that's not right, we'll go from there.
Edit:
OK, so now we know that you want to be able to just increment the earned points, so this Class now becomes much simpler (check the revision history of the post to see what parts were removed):
package{
public class Player extends EventDispatcher {
protected var _earnedPoints:int;
public function Player() {
super();//not passing a parameter, so target of events will be this
}
public function get earnedPoints():int {
return _earnedPoints;
}
public function set earnedPoints(value:int):void {
if (value != _earnedPoints) {
_earnedPoints = value;
//other Classes can listen for this to see if earnedPoints has changed:
dispatchEvent(new Event('earnedPointsChanged');
}
}
}
}
You didn't ask about this part, but I think it's a necessary part for you to understand. I'm going to explain it just in case it's not obvious. Your "objects" need to have a Class of their own:
package {
class TargetObject extends MovieClip {
public var points:int;
}
}
You can set those values when each TargetObject is created, or you can create several subclasses, where the value is hard-coded.
So now, whenever there's a collision, you can do something like this:
_player.earnedPoints += target.points;
When you set that variable, your PlayerClient will update, even when it is set from another Class.
In summary, you need to generate an event that other instances can listen for to update themselves:
package {
public class PlayerClient extends MovieClip {
public var playerScoreText:TextField;//assume this is on stage
protected var _player:Player;
//assume [dependency injection][1]
public function get player():Player {
return _player;
}
public function set player(value:Player):void {
if (value != _player) {
if (_player) {
//[prevent memory leaks][2]
_player.removeEventListener('earnedPointsChanged', updatePlayerScore);
//don't need this if we don't have a player
removeEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
_player = value;
if (_player) {
//listen for changes
_player.addEventListener('earnedPointsChanged', updatePlayerScore);
//we added a listener to the new player, need to make sure is removed
addEventListener(Event.REMOVED_FROM_STAGE, cleanUp);
}
}
}
protected function cleanUp(e:Event):void {
_player.removeEventListener('earnedPointsChanged', updatePlayerScore);
}
/* This will trigger no matter what changes the value of
earnedPoints or bonusPoints.
*/
protected function updatePlayerScore(e:Event):void {
if (playerScoreText) /*in case it wasn't put on stage */ {
playerScoreText.text = _player.earnedPoints
}
}
}
}
I got my question resolved by myself: I used SharedObject method to store the points and then retrieve the point value back into my class Player for accumulation. EventDispatcher did pass the points to the other class, but all the points value get back to initial state once Player hits the obstacle and the game replays, which makes it difficult to retrieve the points I passed to the other class back to the class Player for accumulation. SharedObject method did help me resolve this problem for my case study.
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.
I need to get all the instances in my stage according to an especific class name. I'm doing this:
var class_ref:Class = getDefinitionByName('fran.MyOwnClass') as Class;
var element;
for (var i:uint = 0; i < this.parent.numChildren; i++)
{
element = this.parent.getChildAt(i);
if (element is class_ref)
{
trace('Found element of class fran.MyOwnClass');
}
}
But I want a better way (more efficiently, without checking all the MCs). Is it possible?
If you can start tracking instances from the very beginning of you application life, I'd recommend simply add event listener:
// in document class constructor, before doing anything else
stage.addEventListener(Event.ADDED, stage_addedHandler);
stage.addEventListener(Event.REMOVED, stage_removedHandler);
private function stage_addedHandler(event:Event):void
{
var obj:DisplayObject = event.target as DisplayObject;
// do something, e.g. if (obj is MyClass) objCounter++;
}
...
If you can't track from the beginning, you can't avoid loops.. Just make them more optimized:
var n:int = container.numChildren;
while (n-- > 0)
{
...
}
Overriding everywhere addChild() and others — that's simply impossible solution in real projects.
You could keep a list of all the MC's of a certain type by extending the container class and overriding its addChild(), addChildAt(), removeChild() and removeChildAt() functions.
public class MySprite extends Sprite {
public var ownClasses:Vector.<MyOwnClass> = new Vector.<MyOwnClass>();
override public function addChild(child:DisplayObject):DisplayObject {
addOwnClass(child as MyOwnClass);
return super.addChild(child);
}
override public function addChildAt(child:DisplayObject, index:int):DisplayObject {
addOwnClass(child as MyOwnClass);
return super.addChildAt(child, index);
}
private function addOwnClass(child:MyOwnClass):void {
if (child) ownClasses.push(child);
}
override public function removeChild(child:DisplayObject):DisplayObject {
removeOwnClass(child as MyOwnClass);
return super.removeChild(child);
}
override public function removeChildAt(index:int):DisplayObject {
removeOwnClass(getChildAt(index) as MyOwnClass);
return super.removeChildAt(index);
}
private function removeOwnClass(child:MyOwnClass):void {
if (child) {
var i:int = ownClasses.indexOf(child);
if (i != -1) ownClasses.splice(i, 1);
}
}
}
Using this class, every time a child is added, you check whether it's a MyOwnClass and if it is you add it to the ownClasses list. Similar for removing children.
Now you can simply access the list when you need it without looping over the MC's.
public class Main extends MySprite
{
public function Main()
{
addChild(new Sprite());
addChild(new MyOwnClass());
trace(ownClasses);
}
}
This will output [object MyOwnClass]
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.