Accessing a variable from an other class without the use of static or global - actionscript-3

i am trying to get an Array from a class to an other class but i can't use static or global variable for it.
in my class Jeu.as, i have 3 arrays (t_map1, t_map2 and t_map3) that represents my game map. t_map is an array that can content one of those map and a place where i can change it. I want to take the map use (form t_map) to my character (Perso.as) so it can know where it can walk or not.
The problem is that i don't know how to bring t_map from Jeu.as to Perso.as... I have tried to use a static variable (as seen in other answer) but it don't work because the map have to change...
How can i create a variable that can contain my array in my perso.as class?
in short, i want to bring t_map values form my jeu.as to an other variable in perso.as

All you really need to do is give both instances a reference to the same arrays, or give Perso a reference to Jeau. Static variables are a really bad idea, even if there's nothing inherent to this situation that would keep them from working for you.
Here's what a solution that uses Dependency Injection would look like:
package model {
public class Jeau extends EventDispatcher {
protected var _tMap1:Array = new Array();
protected var _tMap2:Array = new Array();
protected var _tMap3:Array = new Array();
//consider using more descriptive variable names
//or an array of arrays (one map in each index)
public function get tMap1():Array {
return _tMap1;
}
public function set tMap1(value:Array):void {
if (value != _tMap1) {
_tMap1 = value;
dispatchEvent(new Event('tMap1Changed'));
}
}
public function get tMap2():Array {
return _tMap2;
}
public function set tMap2(value:Array):void {
if (value != _tMap2) {
_tMap2 = value;
dispatchEvent(new Event('tMap2Changed'));
}
}
public function get tMap3():Array {
return _tMap3;
}
public function set tMap3(value:Array):void {
if (value != _tMap3) {
_tMap3 = value;
dispatchEvent(new Event('tMap3Changed'));
}
}
protected function somethingThatChangesMap1(index:int, value:String):void {
_tMap1[index] = value;
dispatchEvent(new Event('tMap1Changed'));
}
}
}
I've assumed this is a View class--you haven't given many details. You listen for events coming out of the model Class and then update the View based on whatever is in those arrays. By getting the whole instance, you have the ability to listen for these events. Otherwise, you'd have to use some other mechanism to communicate the change (such as the event bus used in Roenter link description herebotLegs).
package view {
class Perso extends MovieClip {
protected var jeau:Jeau;
public function get jeau():Jeau {
return _jeau;
}
public function set jeau(value:Jeau):void {
if (value != _jeau) {
_jeau = value;
_jeau.addEventListener('map1Changed', doMap1Stuff);
_jeau.addEventListener('map2Changed', doMap2Stuff);
_jeau.addEventListener('map3Changed', doMap3Stuff);
doMap1Stuff();
doMap2Stuff();
doMap3Stuff();
}
}
protected function doMap1Stuff(e:Event=null) {
//do actions depending on the state of map1 here
}
protected function doMap2Stuff(e:Event=null) {
//do actions depending on the state of map2 here
}
protected function doMap3Stuff(e:Event=null) {
//do actions depending on the state of map3 here
}
}
}
This is just an example of how you'd use a third Class to combine the first two. I wouldn't necessarily do it exactly like this:
package control {
public class MainGame {
protected var jeau:Jeau;
protected function perso:Perso;
public function MainGame() {
jeau = new Jeau();
//jeau setup
perso = new Perso();
perso.jeau = jeau;
}
}
}

Sounds like you need some simple accessors.
In Jeu, you'll want something to retrieve the maps like this:
function getMap(mapNumber:int):Array
{
switch(mapNumber)
{
case 1:
return t_map1;
case 2:
return t_map2;
case 3:
return t_map3;
default:
trace("Error: that's not a valid map number!")
}
}
If you saved your maps in another encompassing Array (lets call it allTheMaps), the function would look much nicer:
function getMap(mapNumber:int):Array
{
allTheMaps[mapNumber];
}
Then in Perso, you need to store a reference (or several if it needs to know about multiple maps at the same time) of an array to store the map in. You'll also need a function to set the data:
var theMap:Array;
function setMap(theMap:Array):void
{
myMap = theMap;
}
Now you can pass a map from an instance of Jeu to an instance of Perso:
var Jeu = new Jeu();
var Perso = new Perso();
...
Perso.setMap(Jeu.getMap(1));

Related

Access caller object when using composition in AS3

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(){
}
}

AS3: Making a wrapper for the Dictionary class with a counter for the number of key/value pairs

So according to this discussion there is no constant time method for getting the size of a Dictionary object.
I'm trying to implement a wrapper which adds this functionality. Nothing fancy - just a 'numPairs' property and overridden methods to keep it updated. The problem lies in that the [] operator is used to add key/value pairs, rather than a named method, so I don't know how to override this to keep my counter updated. I could just do something like...
public function addPair(key:*, val:*):void {
this[key] = val;
numPairs++;
}
...but it'd be really nice if I could keep the bracket notation. Does anyone know of a solution?
If you want to keep the bracket notation you can still use the Proxy class for that, wrapping a real dictionary.
Here an implementation using the Proxy class, but here i didn't use a weak dictionary because it can be tricky as the 'key' can be garbaged collected and you will not be aware of that. Of course performance operation (adding, removing, ...) will also be lower than the real dictionary.
here the live test : http://wonderfl.net/c/dstz
import flash.utils.Dictionary;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public class MyDict extends Proxy {
private var _size:int = 0;
private var _dict:Dictionary = new Dictionary();
public function get size():int {
return _size;
}
flash_proxy override function getProperty(name:*):* {
return _dict[name];
}
flash_proxy override function setProperty(name:*, value:*):void {
if (!_dict.hasOwnProperty(name))
_size ++;
_dict[name] = value;
}
flash_proxy override function deleteProperty(name:*):Boolean {
if (_dict.hasOwnProperty(name)) {
_size --;
delete _dict[name];
return true;
}
return false;
}
}
var dict:MyDict = new MyDict();
dict[1] = 2;
dict["foo"] = "bar";
trace(dict.size, dict[1], dict["foo"]);
delete dict[1];
trace(dict.size, dict[1], dict["foo"]);
I understand you like that you want to keep actual pair count, and you should also check for undefined/null value passed that would indicate a removal of pair. So, you first check if there is a key in "this", then assign value.
public function addPair(key:String, val:*):void {
if (this[key]) {
// pair exists, updating
this[key]=val;
if (!val) numPairs--;
} else {
// pair does not exist, adding
if (val) {
this[key]=val;
numPairs++;
}
}
}

Overriding function from another class

I am defining this function in one of my classes:
public function onUse():void {};
Then in another of my classes (let's call it "class2"), I create a object of this class, and then want to override this function with another one. After some Google-Fu, I have found this, and used it...
button.onUse {
variable = value;
}
...but it executes instantly, and not when onUse() is called - which seems to be an empty function, always.
I didn't find anything more than that - I tried a few things myself, like specifying a function inside class2 and using button.onUse = function();, but it always throws errors.
Can anyone tell me whether what I am trying to do is actually possible, and if it is, how can I do it?
You can only override functions when you are extending the class:
public class A {
public function foo():void {
doStuff();
}
}
public class B extends A {
override public function foo():void {
doOtherStuff();
}
}
var n:A = new A();
n.foo(); // => calls doStuff();
var o:B = new B();
o.foo(); // => calls doOtherStuff();
Hence, assigning a different function to a class method of an instance is not possible at runtime.
You can, however, let your original class contain a field of type Function, and then simply assign a different closure to it.
public class A {
public var foo:Function;
}
var n:A = new A();
n.foo = function ():void {
doStuff();
};
n.foo(); // => calls doStuff();
var o:A = new A();
o.foo = function ():void {
doOtherStuff();
}
o.foo(); // => calls doOtherStuff();
check the syntax of
button.onUse {
variable = value;
}
a function would be defined as
public function onUse():void {};
and overwritten with
override public function onUse():void {
}
in a different class
the way you're trying to do it, does not constitute overriding a function.
What I've done in similar circumstances is create a onClickFunction function in the class
public var onClickFunction:Function = null;
and then in the CLICK event listener function add
if(onClickFunction != null){
onClickFunction();
}
then you can assign your on-click functionality by doing something like this
button.onClickFunction = function():void{
variable = value;
// dostuff
}
this is not the best way of doing it, but probably the easiest way of implementing the functionality. And ideally you'd use inheritance the way the spacepirate suggested.

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.

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