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

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.

Related

Access of undefined property issues in AS3

I am having a bit of trouble with some AS3. First time using this language and have more experience with web development then OOP so am getting a bit confused.
I am trying to make it so that when someone clicks a 'powerbutton' which is a "movieclip" symbol within flash then another symbol should then become visible. This is all being done within the Kitchen class.
The code for the main class is which i got from a youtube tutorial video i followed;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.Event;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
public function DragFood() {
myKitchen = new Kitchen;
originalPosition = new Point (x, y);
buttonMode = true;
addEventListener (MouseEvent.MOUSE_DOWN, down);
}
protected function down (event:MouseEvent):void
{
parent.addChild(this);
startDrag();
stage.addEventListener (MouseEvent.MOUSE_UP, stageUp);
}
protected function stageUp (event:MouseEvent):void
{
stage.removeEventListener (MouseEvent.MOUSE_UP, stageUp);
stopDrag();
if (dropTarget)
{
if(dropTarget.parent.name == "bowl")
{
trace("The " + this.name + " is in the bowl");
this.visible = false;
} else {
returnToOriginalPosition();
}
} else {
returnToOriginalPosition();
}
}
protected function returnToOriginalPosition():void
{
x = originalPosition.x;
y = originalPosition.y;
}
}
}
Within it i call the other class;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
The code for the kitchen class is;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class Kitchen extends MovieClip
{
// This is a function. This particular function has the same name as our class and therefore will be executed first
public function Kitchen()
{
// This is a "call" to another function that is defined later in the program.
init();
trace("Hello world");
}
public function init():void
{
// If we want an object (on the screen or otherwise) to be notified about an event we must add a listener for that event to that object.
// We also need to specify what happens everytime the event we are listening for happens.
PowerButton.addEventListener(MouseEvent.CLICK, handleButtonClicks);
}
//This function is called when the oven on button recieves a click.
public function handleButtonClicks(event:MouseEvent):void
{
OvenOn.visible = true;
trace("the oven is being switched on");
}
}
}
The issue i keep getting is that OvenOn and PowerButton are giving me a undefined access issue and im not sure how to fix it. I have found posts on similar subjects like - Access of Undefined property? Actionscript 3
but im not quite sure how to apply it to my issue if anyone could offer any help that would be great.
When you're programming on the timeline, code is referencing the local namespace, and objects you make there (movieclips, textfields, etc.) are automatically instantiated in that namespace so that you can simply call OvenOn.visible = true. However, for each class, their local namespace is whatever is inside the class, so unless you actually created a property on your class called OvenOn, it will most definitely give you Access of Undefined Property errors.
Think of each class as its own island. For them to touch eachother, they need some sort of connection. That connection can be made once the parent instantiates the class in its own namespace. For example...
var foo:String = "Hello!";
var bar:MyClass = new MyClass();
// At this point, whatever code runs inside of MyClass has no concept of foo, or how to access it.
addChild(bar);
// Now that we've added it to the stage, the bar has some properties that have automatically been populated such as "root", "parent", or "stage".
foo.someProperty = "World";
// Since this namespace has a variable pointing to the instance, we can change properties on that class.
Now that we've instantiated MyClass on the stage, we can reference parent properties the class didn't know about. Mind you, this is not necessarily best practice.
package
public class MyClass extends MovieClip {
var someProperty:String = "cheese";
public function MyClass() {
trace(parent.foo) // this will fail
addEventListener(Event.ADDED_TO_STAGE, test);
}
public function test(e:Event):void {
trace(this["parent"].foo); // this will succeed
}
}
}
If you absolutely must change something that is not part of your Kitchen class, pass either the parent of OvenOn or that object specifically as a property of Kitchen. You could do this a couple ways.
with the Constructor...
var something:*;
public function MyClass(someObject:*) {
something = someObject;
}
public function test():void {
something.visible = false;
}
...or by Assigning the Property...
var bar:MyClass = new MyClass();
bar.something = OvenOn;
bar.test(); // will turn off the OvenOn now that 'something' is pointing to it.

Adding eventlisteners in main document class for external classes

I have a small project I'm trying to help learn as3. It is a variation from the book Foundation Game Design with Actionscript 3.0. I am using an fla only to have a document class. All art is loaded within the as files. In the book, he just put all the code in the document class, I followed along and it worked as expected. I am trying to break out the code into separate classes to get a handle on OOP. One class makes a background - Background.as, one makes a character - Character.as, and one makes a button, which I instantiate 6 times for 6 different buttons - GameButton.as. And of course there is GameWorld.as which is the document class. Everything loads and shows up as expected. However when I try and add an eventListener for the buttons, I don't get any response. I have tried putting the eventListener in the GameButton.as and also tried it in the GameWorld.as neither of which has worked. Also I pass a reference to the stage when instantiating the various classes, because when I tried to addChild in the GameWorld.as, nothing would show up. I searched the site and found something similar, but it didn't seem to help. Thank you in advance for any advice you my have. Here is the code:
GameWorld.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.DisplayObject
import flash.events.MouseEvent;
import GameButton;
import Character;
import Background;
[SWR(width = "550", height = "400", backgroundColor = "#FFFFFF", frameRate = "60")]
public class GameWorld extends Sprite
{
//public variables
//Background
public var gameBackground:Background;
//Character
public var catCharacter:Character;
//Buttons
public var upButton:GameButton;
public var downButton:GameButton;
public var growButton:GameButton;
public var shrinkButton:GameButton;
public var vanishButton:GameButton;
public var spinButton:GameButton;
public function GameWorld ()
{
//Add the background to the stage
gameBackground = new Background("../images/background.png", stage);
//Add the character(s) to the stage
catCharacter = new Character("../images/character.png", stage);
//Set initial character position
catCharacter.CharacterPos(225, 150);
//Add the buttons to the stage
upButton = new GameButton("../images/up.png", stage, 25, 25);
downButton = new GameButton("../images/down.png", stage, 25, 85);
growButton = new GameButton("../images/grow.png", stage, 25, 145);
shrinkButton = new GameButton("../images/shrink.png", stage, 425, 25);
vanishButton = new GameButton("../images/vanish.png", stage, 425, 85);
spinButton = new GameButton("../images/spin.png", stage, 425, 145);
//Button event handlers
upButton.addEventListener(MouseEvent.CLICK, upButtonHandler);
}
public function upButtonHandler(event:MouseEvent)
{
trace("You clicked the up button!");
catCharacter.CharacterMove(15);
}
}
}
GameButton.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.MouseEvent;
public class GameButton extends Sprite
{
//public variables
public var stageRef:Stage;
public var urlRequest:URLRequest;
public var gameButtonLoader:Loader;
public var gameButtonSprite:Sprite;
//Constructor
public function GameButton (urlRequest:String, stageRef:Stage, xPos:Number, yPos:Number)
{
this.stageRef = stageRef
this.urlRequest = new URLRequest();
gameButtonLoader = new Loader();
gameButtonSprite = new Sprite();
this.urlRequest.url = urlRequest;
gameButtonLoader.load(this.urlRequest);
gameButtonSprite.addChild(gameButtonLoader);
this.stageRef.addChild(gameButtonSprite);
gameButtonSprite.buttonMode = true;
gameButtonSprite.x = xPos;
gameButtonSprite.y = yPos;
}
}
}
Character.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
public class Character
{
//private variables
private var stageRef:Stage;
private var urlRequest:URLRequest;
private var characterLoader:Loader;
private var characterSprite:Sprite;
//public variables
public var character_x_pos:Number;
public var character_y_pos:Number;
//Constructor
public function Character (urlRequest:String, stageRef:Stage)
{
this.stageRef = stageRef;
this.urlRequest = new URLRequest();
characterLoader = new Loader();
characterSprite = new Sprite();
this.urlRequest.url = urlRequest;
characterLoader.load (this.urlRequest);
characterSprite.addChild (characterLoader);
this.stageRef.addChild (characterSprite);
characterSprite.mouseEnabled = false;
}
//Set the position of the character
public function CharacterPos(xPos:Number, yPos:Number):void
{
characterSprite.x = xPos;
characterSprite.y = yPos;
}
//Move the position of the character
public function CharacterMove( yPos:Number):void
{
characterSprite.y -= yPos;
}
}
}
Background.as
package
{
import flash.net.URLRequest;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
public class Background
{
//Private variables
private var stageRef:Stage;
private var urlRequest:URLRequest;
private var backgroundLoader:Loader;
private var backgroundSprite:Sprite;
//Constructor
public function Background (urlRequest:String, stageRef:Stage)
{
this.stageRef = stageRef;
this.urlRequest = new URLRequest();
backgroundLoader = new Loader();
backgroundSprite = new Sprite();
this.urlRequest.url = urlRequest;
backgroundLoader.load (this.urlRequest);
backgroundSprite.addChild (backgroundLoader);
this.stageRef.addChild (backgroundSprite);
backgroundSprite.mouseEnabled = false;
}
}
}
All art is loaded within the as files.
This is not an approach I recommend. There's a reason God gave us the Flash IDE--and it's not to write code! Any time you're spending on layout and viduals in code is just wasted, unless you have an actual requirement to change the visuals at runtime. The fact that your paths are all hard-coded suggests that you don't have that requirement.
So let's step back and imagine that you have a Symbol that contains 6 Symbols that you've created as just Flash buttons (when you select Button as the Symbol type). These will be SimpleButtons, but in the Class below we're just going to type them as DisplayObject. The Class doesn't care what they are, but using Simplebutton gives them up, over, down and hit states that require no code.
Note that the below assumes you have "automatically declare stage instances" off, which is IMO the best way to do things.
package view {
public class NavBar extends Sprite {
//because you put these on stage in the Symbol, they will be available in the constructor
public var upButton:DisplayObject;
public var downButton:DisplayObject;
public var growButton:DisplayObject;
public var shrinkButton:DisplayObject;
public var rotateButton:DisplayObject;
public var vanishButton:DisplayObject;
//makes it easier to do the same setup on all buttons
protected var allButtons:Vector.<DisplayObject> = <DisplayObject>([upButton, downButton, growButton, shrinkButton, rotateButton, vanishButton]);
public function NavBar() {
super();
for each (var btn:DisplayObject in allButtons) {
btn.buttonMode = true;
btn.mouseChildren = false;
btn.addEventListener(MouseEvent.CLICK, onButtonClick);
}
}
protected function onButtonClick(e:MouseEvent):void {
switch (e.target) {
case upButton:
dispatchEvent(new CharacterEvent(CharacterEvent.UP));
break;
case downButton:
dispatchEvent(new CharacterEvent(CharacterEvent.DOWN));
break;
case growButton:
dispatchEvent(new CharacterEvent(CharacterEvent.GROW));
break;
case shrinkButton:
dispatchEvent(new CharacterEvent(CharacterEvent.SHRINK));
break;
case rotateButton:
dispatchEvent(new CharacterEvent(CharacterEvent.ROTATE));
break;
case vanishButton:
dispatchEvent(new CharacterEvent(CharacterEvent.VANISH));
break;
default:
break;
}
}
}
}
Note that there's zero layout code. This code is dependent on a custom Event Class. I'm going to write that Event Class so that it always bubbles. That way, it can be dispatched anywhere on the display list and received at the top level:
package control {
class CharacterEvent extends Event {
public static var UP:String = 'characterUp';
public static var DOWN:String = 'characterDown';
public static var GROW:String = 'characterGrow';
public static var SHRINK:String = 'characterShrink';
public static var ROTATE:String = 'characterRotate';
public static var VANISH:String = 'characterVanish';
public function CharacterEvent(type:String) {
super(type, true, true);//always bubbles
}
public function clone():Event {
return new CharacterEvent(type);
}
}
}
Now, if you want to manually handle instantiation of the Symbol that has view.NavBar as its base class, it will look like this:
package {
public var navBar:NavBar;
class GameWorld {
public function GameWorld() {
try {
var navClass:Class = getDefinitionByName('NavBarSymbol') as Class;
} catch (e:Error) {
trace('You need to have a Library symbol called NavBarSymbol');
}
if (navClass) {
navBar = new navClass() as NavBar;
//unnecessary layout code here
//Note that this is NOT the responsibility of the child Class!
addChild(navBar);
}
//instantiate character
...
//by listening to the whole Document, you can add other things that
//dispatch Character events on the display list, like a keyboard listener
addEventListener(CharacterEvent.UP, moveCharacterUp);
//listeners for the rest of the character events...
}
public function moveCharacterUp(e:CharacterEvent):void {
//character move logic
}
//other handlers
}
}
Personally, I'd just add the navBar to the stage, and then there's no need to manage it at all (not even reference it with a variable), simply add the event listeners for the various character events.
The root of your problem doesn't seem to be the character code. However, I'm going to give you a few "best practice" pointers about it.
The convention in AS3 is for Class members (properties and methods) to be camel case starting with a lower case letter. So, characterPos() and characterMove().
Your Class already contains character in the name, so really these should just be pos() and move() (though there's no need now to shorten position()).
The only thing your child Classes are doing with their references to the parent are adding themselves. They don't need and shouldn't have a reference to the parent for this purpose. It is the parent's responsibility to add the Children (or the responsibility of the Flash Player if you use the stage).
That said, you could give your Character a reference to the parent Class typed as IEventDispatcher and allow the Character to listen to this channel. This concept is called an event bus.
Note that the reason that so many people do what you're doing is that Adobe failed to document how to properly use OOP with the timeline. Unfortunately, by the time a few of us started documenting that around late 2009/early 2010, the damage was done and everyone assumed that if you wanted to write good code you had to pretend the timeline and stage didn't exist.
I know I've covered a lot of ground, and probably most of what I said directly contradicts what you thought you knew, so please don't hesitate to ask any questions you might have.

AS3 access a text field

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.

Unable to have Flash Component (SWC) access the library in live preview

I am building a set of Flash components with the ability to replace the skin of the component with another one in the library.
Currently, I am able to access the library after running the application, but not during live preview and I'd like to know if it is possible for the component to access the library while running in live preview mode (the mode where you can drag the component around the stage and change its properties in the Component Parameters window)
Here is a simplified code that just looks to see if there is a symbol of the name specified and than instantiates it and adds it as a child.
package
{
import fl.core.UIComponent;
import flash.display.MovieClip;
import flash.system.ApplicationDomain;
/**
* ...
* #author Roy Lazarovich
*/
public class CompTest extends UIComponent
{
private var customfile :String;
public function CompTest()
{
}
override protected function configUI():void
{
}
override protected function draw():void
{
super.draw();
}
private function setCustomFile():void
{
if (ApplicationDomain.currentDomain.hasDefinition(customfile))
{
var c:Class = Class(ApplicationDomain.currentDomain.getDefinition(customfile));
var mc:MovieClip = new c();
addChild(mc);
}
}
[Inspectable(name = "_Custom File", defaultValue = "")]
public function set _customfile(value:String):void
{
customfile = value;
setCustomFile();
drawNow();
}
}
}
Thanks!
I'm not entirely sure what you have already tried to fix this situation. But hopefully this might help.
Right click on the MovieClip in your library, select Linkage and give it a class name, ie. MyThing.
In your code,
newMyThing = new MyThing();
this.addChild(newMyThing);
trace("tada!");
Hope that helps or gets you closer to a solution.
This works for me in LivePreview, as long as I apply it in configUI, and not draw:
public class EditableBitmap extends UIComponent
{
protected var placeholder:String = "None";
protected var bitmap:Bitmap;
protected var scale:Number = 1;
[Inspectable(name = "Placeholder", type = String, defaultValue="None")]
public function set Placeholder($value:String):void
{
placeholder = $value;
configUI();
}
public function get Placeholder():String { return placeholder; }
public function EditableBitmap()
{
//Console.Debug("NEW EditableBitmap");
super();
}
override protected function configUI():void
{
//Console.Debug("EditableBitmap configUI: " + width);
if (!isNaN(width))
{
wDim = width;
hDim = height;
graphics.clear();
graphics.beginFill(0x000000, 0.1);
graphics.drawRect(0, 0, wDim, hDim);
}
if (placeholder != "None" && placeholder != "")
{
var asset:Class = getDefinitionByName(placeholder) as Class;
var data:BitmapData = new asset() as BitmapData;
bitmap = new Bitmap(data);
}
super.configUI();
}
override protected function draw():void
{
if (bitmap)
{
addChild(bitmap);
bitmap.x = off_x * scale;
bitmap.y = off_y * scale;
bitmap.scaleX = bitmap.scaleY = scale;
}
}
}
NOTE: When I'm working on the FLA where I am editing the component, the bitmap is only displayed from the library inconsistenty. Sometimes it works, sometimes it doesn't. But when I export the SWC and then import the component to another movie, it works every time, in LivePreview as well as at runtime.
UPDATE
It seems this doesn't work in CS6 unless the symbol is already embedded in the component .SWC I wanted to see if I could trick it, by embedding one image in the SWC and then replacing it with another of the same name in the destination file. This didn't work, but it did point me to how you can do this:
So, it's a bit laborious, but you could work around this by:
1) Creating a dummy asset for each property in the component SWC.
2) Overriding this using a custom class in the file where you deploy the component
... all of which may be more trouble than it's worth, but which should provide a solution to the problem.

AS3.0: Acces properties of created instance from 2nd class

I'm building a small game.
On my document class i create a instances of the class Character and Level with the following code:
//add the Level
level = new TileGrid();
level.y = 100;
level.x = 400;
addChild(player);
//add our player
player = new Character();
player.y = 150;
player.x = 400;
addChild(player);
I also create a controller class which handles the user input. (for example checks if the player is able to move to the right.)
I also create eventlisteners for keyboardevents and stuff.
When a key is pressed i want to check if the movement is possible by calling the checkTile(tileNumber) function of the TileGrid class from within the controller class.
The controller class looks like this:
package {
import flash.events.KeyboardEvent;
import flash.events.Event;
public class Controller{
//Constructor code
public function Controller(){}
//Keyboard pressed -> move character
public function keyPressed(evt:KeyboardEvent):void
{
trace(level.checkTile(30));
}
}
And the TileGrid class looks something like this:
package {
import flash.events.KeyboardEvent;
import flash.events.Event;
public class TileGrid{
//Constructor code
public function TileGrid(){
//Creating all the tiles and adding them to the stage.
}
//Check if a certain tile is walkable
public function checkTile(tileNumberType){
if(tileNumberType > 15){
return false;
}else{
return true;
}
}
}
But when i test this i get the following error:
Line 81 1120: Access of undefined property level.
When i try: trace(Object(parent).level.checkTile(30)); i get: 1120: Access of undefined property parent.
How can i access methods from one class with an instance from a second class ?
I think you must do something like this:
...
// somewhere in your document class (or somewhere else)
var player:Character = new Character();
var level:TileGrid = new TileGrid();
var controller:Controller = new Controller(player, level);
...
// in your Controller class
private var level:TileGrid;
private var player:Character;
public Controller(player:Character, level:TileGrid) {
this.player = player;
this.level = level;
}
public function keyPressed(event:KeyboardEvent):void {
level.checkTile(30); // in this line "level" means "this.level"
}
In that case you must say which player and which level the controller must control. The controller is a class that has no knowledge of any other class. The variables are not global (in your example, and they shouldn't be) so you can't acces them from everywhere.
Classes in AS can't access their context. This means that if I have:
function foo():void
{
var k:MyCustomClass = new MyCustomClass();
var j:MyOtherClass = new MyOtherClass();
}
the two instances j and k don't know about each other.
In this particular case, however, because you're clearly dealing with a parent structure, it is possible to get the parent's properties. This is possible because you have a path to the variable in a public scope.
Try this instead:
trace(Object(parent).level.checkTile(30));
Also note the Object(parent). A DisplayObject's parent is a DisplayObjectContainer, which does not have a level property. By wrapping parent in Object, however, you're telling Flash, "It's OK, you should look for this property at runtime not when compiling."