AS3 access a text field - actionscript-3

I have 3 movieclips and each has a textbox as a child.
I set the active one with
var myroot:MovieClip = this.root as MovieClip;
var activeText:MovieClip;
This works
function keyClicked (e:MouseEvent) {
myroot.firstname_mc.getChildAt(0).text += "hello";
}
This does not
function keyClicked (e:MouseEvent) {
activeText.getChildAt(0).text += "hello";
}
How can I get this to work dynamically?

Your whole problem is that you're trying to do things that you shouldn't do. What you should do instead is write Classes that encapsulate the desired behavior, and let them handle the details. For example:
package view {
public class Label extends MovieClip {
/* This is public so the Flash Player can
populate it, not so you can "talk" to it
from outside. This is a stage instance
*/
public var tf:TextField;
protected var _text:String;
public function get text():String {
return _text;
}
public var set text(value:String):void {
if (value != _text) {
_text = value;
tf.text = _text;
}
}
}
}
Now, in your main Document Class, you type activeText as Label, and you can then set its text like this:
activeText.text += 'hello';
And you can now reuse the new Class you wrote to make all sorts of different-looking Labels, just as long as each contains a TextField called tf.

Related

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

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));

Actionscript 3 error code 1119 when dealing with this.parent?

I am fairly new to AS and am playing with a minesweeper game I found. For each cell on the game board, I have a movie clip (cell_mc) embedded with a dynamic text box (number_txt) for the numbers, a solid color square movie clip (block_mc), and a little flag to mark the bombed boxes (flag_mc). I also have a separate dynamic text box to count how many mines are left on the board (minecounter_txt).
My problem is that when I try to run the game, every where I have a "this.parent.flag_mc" or a "this.parent" with any of the mentioned elements, Flash returns an error code - "1119: Access of possibly undefined property flag_mc (or other element) through a reference with static type cell." And with minecounter_mc it returns "1119: Access of possibly undefined property flag_mc (or other element) through a reference with static type flash.display:DisplayObjectContainer"
I assumed that since it said it couldn't reference the elements with a static type, that it was unable to recognize them as dynamic. I searched for a solution, and read that it helps to declare the dynamic element before the parent, i.e. "MovieClip(this.parent.flag_mc), but it didn't help.
Can anyone help? Thanks.
package {
import flash.display.MovieClip;
import flash.display.DisplayObjectContainer;
import flash.events.MouseEvent;
public class cell extends MovieClip {
public var state:Boolean;
public var revealed:Boolean;
public var xcor:uint;
public var ycor:uint;
public var marked:Boolean;
public var cellValue:int;
public function cell(corx:uint, cory:uint) {
// constructor code
this.state = false;
this.revealed = false;
this.marked = false;
this.cellValue = 0;
this.xcor = corx;
this.ycor = cory;
this.flag_mc.visible = false;
this.addEventListener(MouseEvent.CLICK, cellClicked);
}//end of constructor
private function cellClicked(event:MouseEvent):void{
if(event.shiftKey){
if(this.marked){
this.flag_mc.visible = false;
this.marked = false;
this.parent.minecounter_txt.text = String(int(this.parent.parent.minecounter_txt.text) + 1);
} else{
this.flag_mc.visible = true;
this.marked = true;
this.parent.minecounter_txt.text = String(int(this.parent.parent.minecounter_txt.text) - 1);
}
} else{
if(!state){
openCell();
} else{
if(!this.marked){
this.parent.play_btn.visible = true;
}
}
}
}
private function openCell(){
if(!this.marked && !this.revealed){
this.block_mc.visible = false;
this.revealed = true;
this.removeEventListener(MouseEvent.CLICK, cellClicked);
}
}
}//end of class
}//end of package
this.parent will be null unless the movieclip is added to the parent's display list.
It would be better to pass the reference of the parent & store it in a class property.
var parentObj:Object = null;
// Constructor
public function cell(parentObj:Object, corx:uint, cory:uint) {
this.parentObj = parentObj;
//...
And call it as :
var cellObj = new cell(this, ...);
If you know which Class would be the parent, then you could also set the typeof parentObj to it.

How to have the variable in a class updated when its value is set in another class?

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.

Get instances by class name in AS3

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]

How to override the transform.matrix setter

I have a class which extends the Sprite object in as3. I need to be able to override the transform.matrix setter in this class but haven't been successful in doing so.
I've tried many things, along with creating my own separate class which extends the Transform class and then overrides its set matrix function, and set my transform = new CustomTransform(). Sadly this didn't work.
In code this is what i tried:
public class MyClass extends Sprite
{
public function MyClass()
{
super(); transform = new MyTransform(this);
}
}
class MyTransform extends Transform
{
public function MyTransform(dp:DisplayObject)
{
super();
}
override public function set matrix(value:Matrix)
{
super.matrix = value;
customcode();
}
}
All help is greatly appreciated!
This seems to work:
public class MyClass extends Sprite
{
public function MyClass()
{
super();
transform = new MyTransform(this,super.transform);
// i'm drawing a rect just to see the results of scaling
graphics.beginFill(0xff0000);
graphics.drawRect(0,0,100,100);
graphics.endFill();
}
override public function get transform():Transform {
var tmp:Transform;
if(super.transform is MyTransform) {
tmp = super.transform;
} else {
tmp = new MyTransform(this,super.transform);
}
return tmp;
}
override public function set transform(value:Transform):void {
var tmp:Transform;
if(value is MyTransform) {
tmp = value;
} else {
tmp = new MyTransform(this,value);
}
super.transform = tmp;
}
}
public class MyTransform extends Transform
{
public function MyTransform(dp:DisplayObject,transf:Transform = null)
{
super(dp);
if(transf) {
for(var prop:String in transf) {
this[prop] = transf[prop];
}
}
}
override public function set matrix(value:Matrix):void
{
super.matrix = value;
// customcode();
}
}
Use:
var sp:MyClass = new MyClass();
var mat:Matrix = sp.transform.matrix;
mat.scale(3,3);
trace(sp.transform);
sp.transform.matrix = mat;
addChild(sp);
The problem is that, even if you create and assign your tranform to be of type MyTransform, the getter returns a regular Transform object. There's something weird about how transform objects work in Flash (this is also true for SoundTransform, for instance). There's some kind of cache mechanism implemented in a rather lame way that forces you to reassign the instance if you want to commit your changes.
I mean this pattern:
var t:Transform = mc.transform;
// do something with t
mc.transform = t;
So I think this is related to why your code doesn't work as expected.
To get around this, I'm checking both in the setter and the getter if the trasnform object passed is of type MyTransform. If it is, I use it as is. If it's not, I create a MyTransform object and copy all of the properties from the original Transform. It'd be nice if the Transform class had a clone method, but it doesn't, so I implemented this simple copy mechanism. Not sure if this doesn't mess up with some internal state in Transform (could be the case). I haven't tested it apart from applying a scale, once. You might want to do it, as there could be other side effects I'm not considering. Also, this is probably not the most performant. But I can't think of another way to have your matrix setter called.
Edit
Using a static/global dispatcher is not a good idea except you really need it to be global. Implementing IEventDispatcher, since you can't directly extend EventDispatcher, is what you want.
The code needed for that is a bit verbose, but it's a no-brainer anyway. All you need is having an internal instance of event dispatcher and implement the methods of the interface. In said methods, you forward the parameteres to the actual dispatcher.
public class MyTransform extends Transform implements IEventDispatcher
{
private var _dispatcher:EventDispatcher;
public function MyTransform(dp:DisplayObject,transf:Transform = null)
{
super(dp);
_dispatcher = new EventDispatcher(this);
if(transf) {
for(var prop:String in transf) {
this[prop] = transf[prop];
}
}
}
override public function set matrix(value:Matrix):void
{
super.matrix = value;
// customcode();
}
public function dispatchEvent(event:Event):Boolean {
return _dispatcher.dispatchEvent(event);
}
public function addEventListener(type:String,listener:Function,useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void {
_dispatcher.addEventListener(type,listener,useCapture,priority,useWeakReference);
}
public function removeEventListener(type:String,listener:Function,useCapture:Boolean = false):void {
_dispatcher.removeEventListener(type,listener,useCapture);
}
public function hasEventListener(type:String):Boolean {
return _dispatcher.hasEventListener(type);
}
public function willTrigger(type:String):Boolean {
return _dispatcher.willTrigger(type);
}
}