AS3 How to get custom class instances to access variables on timeline? - actionscript-3

Please help me understand how to get custom class instances to access variables defined on the timeline.
I am not using a document class, because this program will be a memory experiment with multiple stages, so using the timeline works best for me. (FWIW, I'm also not using xml; just trying to keep this as simple as possible.) For now I've just made a simple one-frame fla on which I'm trying to make a vocabulary test. I have made a custom class called VocabQ, which in turn contains 4 instances of a custom class called VocabButton. Right now, clicking one of those buttons just traces the button label. But I want it to also update the value of the String variable CurrentResponse, which is declared on the timeline. If I just try to reference CurrentResponse from the VocabButton class, I get an error "1120: Access of undefined property...".
I've tried a variety of approaches based on discussions I've found across the internet, but have not yet had success and am only getting more confused. Please help! (Simple solutions would be greatly appreciated, if such exist!) See code below.
thank you, ~jason
code in timeline:
import VocabQ;
import flash.display.*;
stop();
var CurrentResponse:String="NA";
var VocabQuestion = new VocabQ("VocabWord",["answerA","answerB","answerC","answerD"]);
addChild(VocabQuestion);
VocabQ.as code:
package
{
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.text.*;
import VocabButton;
public class VocabQ extends MovieClip{
private var _VocabWordText:String;
private var _VocabWord:TextField;
private var _ResponseOptions:Array;
public function VocabQ(VocabWordText:String,ResponseOptions:Array){
_VocabWordText=VocabWordText;
_ResponseOptions=ResponseOptions;
build();
}
private function build():void{
_VocabWord = new TextField();
_VocabWord.text=_VocabWordText;
_VocabWord.x=25;
_VocabWord.y=25;
_VocabWord.textColor = 0x000000;
addChild(_VocabWord);
for (var i:int; i < _ResponseOptions.length; i++){
var _VocabButton:VocabButton = new VocabButton(_ResponseOptions[i]);
_VocabButton.x = 25 + (_VocabWord.width) + 10 + ((_VocabButton.width + 2) * i);
_VocabButton.y = 25;
addChild(_VocabButton);
}
}
}
}
VocabButton.as code:
package
{
import flash.display.*;
import flash.text.*;
import flash.events.*;
public class VocabButton extends MovieClip{
private var _btnLabel:TextField;
public function VocabButton(labl:String){
_btnLabel = new TextField();
_btnLabel.textColor = 0x000000;
_btnLabel.text = labl;
_btnLabel.border=true;
_btnLabel.borderColor=0x000000;
_btnLabel.background=true;
_btnLabel.backgroundColor= 0xDAF4F0;
_btnLabel.mouseEnabled = false;
_btnLabel.selectable=false;
_btnLabel.width=100;
_btnLabel.height=18;
buttonMode=true;
useHandCursor=true;
addEventListener(MouseEvent.CLICK,onClick,false,0,true);
addChild(_btnLabel);
}
private function onClick(evt:MouseEvent):void{
trace(_btnLabel.text);
//CurrentResponse=_btnLabel.text; //causes error 1120: Access of undefined property...
}
}
}

This should work:
private function onClick(evt:MouseEvent):void {
trace(_btnLabel.text);
evt.currentTarget.root.CurrentResponse=_btnLabel.text;
}
See the discussion here: http://www.actionscript.org/forums/showthread.php3?t=161188
Personally, rather than trying to access the timeline like this, I would prefer to add a click listener
to your VocabQuestion on the timeline and allow the Event to Bubble Up, or, if you are going to have a number of buttons within your class, to extend EventDispatcher and create some custom events which can likewise be handled on your timeline, but for a simple test, accessing the root property should be good enough.

Related

AS3 doesn't recognize a variable I just declared

I'm trying to load a background image, but I'm getting an error saying "Error: Access of undefined property assetLoader." What's going on here?
import flash.display.Loader;
import flash.net.URLRequest;
class Inventory {
private var assetLoader:Loader = new Loader();
assetLoader.load(new URLRequest("image.png")); //error on this line
addChild(assetLoader);
}
If you are using addChild() method you must inherits the features of DisplayObjectContainer. And if you are using your Inventory class as document class, you must extends Sprite or MovieClip.
Document class must be defined by public access specifier.
Only globally(Class property definitions) declared variables are allowed to use private and public. You are not allowed to use it locally(within functions). And Timeline also not allow you to use access specifiers.
package
{
import flash.display.Loader;
import flash.net.URLRequest;
import flash.display.MovieClip;
public class Inventory extends MovieClip
{
private var assetLoader:Loader;
public function Inventory()
{
// constructor code
assetLoader= new Loader();
assetLoader.load(new URLRequest("image.png")); //error on this line
addChild(assetLoader);
}
}
}
You need to place these two lines in the constructor code, like this:
import flash.display.Loader;
import flash.net.URLRequest;
class Inventory {
private var assetLoader:Loader = new Loader();
public function Inventory() {
assetLoader.load(new URLRequest("image.png")); //error on this line
addChild(assetLoader);
}
}
The correct answer is that even tough you can instantiate instances at declaration time like here:
private var assetLoader:Loader = new Loader();
You are not allowed to work with those objects prior to the class instance existing. Any attempt to access assetLoader properties and methods prior to creating an instance of Inventory will fail. The constructor is the first piece of code that an instance of Inventory will run so it's the first place in the class code where you can start to use the class instance objects because at this point the Inventory instance exists. Vesper code example shows it correctly.
In theory this:
private var assetLoader:Loader = new Loader();
is equivalent to this:
private var assetLoader:Loader;
public function Inventory()
{
assetLoader = new Loader();
}
but in fact the timing of the assetLoader creation differs slightly. It is always better to create those member instance in constructor.
To Benny: All classes have an access modifier that defaults to internal. The PO class is defined as internal and so has correctly an access modifier (the internal default since none is specified). The access modifier of the member variable is correctly defined and is not related to the PO problem.

how do you reference a symbol in a different class in as3?

I have created two separate classes and I want to use a symbol I created in the main class in a function I created in the second class. I have tried importing both classes into each other, however when I do this I get Error #1023. I am fairly new at as3 and any help is appreciated as I have no idea what I have done wrong.
-Thank you!
public class SuspectSimulatorDesktop extends Sprite {
[Embed(source="/../lib/SuspectSit.png")]
private var CharacterSit:Class;
var tools:Tools = new Tools();
public var charSit:Bitmap = new CharacterSit();
public function SuspectSimulatorDesktop() {
addChild(tools);
}
}
//Tools (Second Class)
package com.powerflasher.SampleApp {
import com.powerflasher.SampleApp.SuspectSimulatorDesktop;
import flash.events.MouseEvent;
import flash.display.Sprite;
/**
* #author timcis
*/
public class Tools extends Sprite {
[Embed(source="/../lib/Fist.png")]
private var Fist:Class;
var sSim:SuspectSimulatorDesktop = new SuspectSimulatorDesktop();
private function punchChar(event:MouseEvent):void{
sSim.charSit.rotation = 90;
}
}
Error #1023 means that you have a stack overflow in your code. See this link for more explanations :
http://curtismorley.com/2007/08/19/flashflex-as3-error-1023-stack-overflow-occurred/
Regarding your code, you created a SuspectSimulatorDesktop class which instantiates a Tools object which instantiates itself a SuspectSimulatorDesktop object and so on... Each class calls the other one indefinitely and fills the stack, hence the stack overflow.
You need to break the circle by deleting either of these lines and adapt your code accordingly :
var tools:Tools = new Tools();
or
var sSim:SuspectSimulatorDesktop = new SuspectSimulatorDesktop();

actionscript 3 - error #1009 issues calling function from another class

Im having trouble calling functions from other classes. I want to call a function in one class which updates a score display in another class. The error code for this is:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at code.functions::EnemyYellow()[code\functions\EnemyYellow.as:18]
at code::Main()[\code\Main.as:27]
Would appreciate if someone could help me out, I set up 2 basic files with the code which is causing an issue. Its normally not set up like this, I just made this for testing and so I can clearly explain the problem.
Main file:
package code {
import flash.display.MovieClip;
import flash.events.*;
import code.*;
public class Main extends MovieClip {
public var _enemy:EnemyYellow;
public var playerHP:Number;
public function Main() {
playerHP = 10;
_playerHPdisplay.text = playerHP.toString();
trace(playerHP)
_enemy = new EnemyYellow;
}
public function lowerHP ():void
{
playerHP = playerHP - 1;
_playerHPdisplay.text = playerHP.toString();
trace(playerHP)
}
}
}
Second File:
package code.functions {
import flash.display.MovieClip;
import flash.events.*;
import code.Main;
public class EnemyYellow extends MovieClip {
public var _main:Main;
public function EnemyYellow() {
_main.lowerHP();
trace ("done")
}
}
}
I also tried adding _main = new Main; in the second file but the game just loads with a blackscreen and an error about invalid data.
First of all, you surely need to "instantiate" the Main class, which means basically to create it.
public var _main:Main;
This line just declares that there will be a variable of type Main. But for now, the value of _main is null. So you are right, that you need to call:
_main = new Main();
After you've done this, the first error will disappear. But then you have things in that MovieClip that are still vague. Like _playerHPdisplay. Where's that from? Is it an instance from stage or what? You just created a brand new object, and it does not have any reference to other objects, TextFields or whatever.
So this basically answers your current question and problem, but for sure you will have more :)

need help - actionscript 3 simple button class

I have very simple class, but it doesn't work? What could be wrong with this?
package {
import flash.display.Sprite;
import fl.controls.Button;
public class t_class extends Sprite {
private var b:Button;
public function t_class():void{
b = new Button();
b.width = 150;
b.label = "button label";
b.move(10, 150);
b.enabled = false;
addChild(b);
}
}
}
Assuming that you are adding an instance of your button class as a child to the stage as others have mentioned above:
package
{
//Imports
import flash.display.Sprite;
import com.wherever.is.t_class;
//Class
public class DocumentClass extends Sprite
{
//Constructor
public function DocumentClass()
{
var myButton:t_class = new t_class();
addChild(myButton);
}
}
}
The good news is that your t_class code is correct (minus a few deviations from AS3 code conventions, but i digress). However, in order to use Flash Components they must be physically imported into the library in addition to being imported with code. If they are not present in the library then Flash doesn't know they exist and you will receive the following during compile:
"ERROR 1046: Type was not found or was not a compile-time constant: Button."
Goto Window > Components, select User Interface > Button and drag the component into your library. Rebuild and you should see your button.

Trying to delete child instance in array. Lack CS training. Totally stuck

I posted yesterday about how to communicate to one class from another that I wanted to delete an instance of it, and I got the dispatcher working today. However, I think I've painted myself into a corner. Even though the dispatcher is working, I A:feel like it's running through too many functions on the way to actually deleting the object, and B: still can't manage to get it to actually delete. I don't have any formal CS training, so it's one of those situations where my mind is going in circles and I can't "see" what I'm doing wrong. I figure if I post my classes here, at the very least people can have a chuckle at my amateur code, and if I'm lucky, some kind soul will point out what I'm doing wrong. So here goes:
Background.as:
//Background class. Singleton? Sets up/maintains the application.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
public class Background extends flash.display.MovieClip {
private var slate:MovieClip;
private var slateBounds:Rectangle = new Rectangle(100,-260,0,280);
private var _toolbox:MovieClip;
private var _elementArray:Array = new Array();
public function Background() {
//attach movieclips to stage
slate = new mc_slate();
slate.x = 100;
slate.y = 20;
addChild(slate);
_toolbox = new Toolbox();
_toolbox.x = 750;
_toolbox.y = 20;
addChild(_toolbox);
//set draggables
//slate.addEventListener(MouseEvent.MOUSE_DOWN, dragSlate);
//slate.addEventListener(MouseEvent.MOUSE_UP, releaseSlate);
slate.addEventListener(MouseEvent.MOUSE_UP, dropNewElement);
}
private function dragSlate(event:MouseEvent) {
slate.startDrag(false, slateBounds);
}
private function releaseSlate(event:MouseEvent) {
slate.stopDrag();
}
private function dropNewElement(event:MouseEvent) {
var _elementType:String = _toolbox.currentTool;
var _x:Number = event.target.x;
var _y:Number = event.target.y;
var _newElement:MovieClip;
var _latestIndex:Number;
//case switch to choose element based on _elementType
//add new element to stage
_newElement = new PageElement(_elementType, event.localX, event.localY);
_latestIndex = _elementArray.push(_newElement);
_newElement.addEventListener("closeWindow", deleteElement);
slate.addChild(_newElement);
}
private function deleteElement(event:Event) {
trace("trying to remove element.");
slate.event.target.removeChild(_elementArray[0]);
}
}
}
Toolbox.as:
//Toolbox class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
public class Toolbox extends flash.display.MovieClip {
private var _toolboxback:MovieClip;
private var _tool01:MovieClip;
private var _tool02:MovieClip;
private var _tool03:MovieClip;
private var _tool04:MovieClip;
private var _tool05:MovieClip;
private var _currentTool:String = 'none';
public function Toolbox() {
_toolboxback = new ToolboxBack();
_toolboxback.x = 0;
_toolboxback.y = 0;
_toolboxback.alpha = .5;
addChild(_toolboxback);
_tool01 = new TextTool();
_tool01.x = 10;
_tool01.y = 10;
addChild(_tool01);
//_tool01.addEventListener(MouseEvent.MOUSE_DOWN, dragTool);
_tool01.addEventListener(MouseEvent.MOUSE_UP, switchTool);
_tool02 = new ImageTool();
_tool02.x = 10;
_tool02.y = 54;
addChild(_tool02);
_tool02.addEventListener(MouseEvent.MOUSE_UP, switchTool);
}
private function dragTool(event:MouseEvent) {
event.target.startDrag(false);
}
private function releaseTool(event:MouseEvent) {
event.target.stopDrag();
}
private function switchTool(event:MouseEvent) {
_currentTool = event.target.toolname;
//trace(_currentTool);
}
public function get currentTool():String{
return _currentTool;
}
}
}
Tool.as (any class with "Tool" at the end of it simply extends this class and adds a name)
//Tool class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
public class Tool extends flash.display.MovieClip {
private var _toolname:String;
public function Tool(toolname) {
_toolname = toolname;
}
public function get toolname():String{
return _toolname;
}
}
}
PageElement.as:
//Page element class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.text.*;
public class PageElement extends flash.display.MovieClip {
private var _elementname:String;
private var _elementback:MovieClip;
private var _elementmenu:MovieClip;
private var _title:TextField;
private var _formatter:TextFormat = new TextFormat();
public function PageElement(elementname, x, y) {
_elementname = elementname;
_elementback = new ElementBack();
_elementback.x = x;
_elementback.y = y;
_elementback.alpha = .5;
_elementback.addEventListener(MouseEvent.MOUSE_DOWN, dontBubble);
_elementback.addEventListener(MouseEvent.MOUSE_UP, dontBubble);
_elementmenu = new ElementMenu();
_elementmenu.x = x + _elementback.width - 5;
_elementmenu.y = y - 5;
_elementmenu.addEventListener(MouseEvent.MOUSE_OVER, showElementMenu);
_elementmenu.addEventListener(MouseEvent.MOUSE_OUT, retractElementMenu);
_elementmenu.addEventListener(MouseEvent.MOUSE_DOWN, dragElement);
_elementmenu.addEventListener(MouseEvent.MOUSE_UP, releaseElement);
_formatter.font = "Helvetica";
_formatter.size = 10;
_title = new TextField();
_title.text = elementname;
_title.x = x;
_title.y = y;
_title.textColor = 0xffffff;
_title.setTextFormat(_formatter);
addChild(_title);
addChild(_elementback);
addChild(_elementmenu);
}
public function get elementname():String{
return _elementname;
}
public function set elementTitle(newTitle) {
}
public function showElementMenu(event:MouseEvent) {
_elementmenu.expandMenu();
}
public function retractElementMenu(event:MouseEvent) {
_elementmenu.retractMenu();
}
public function hideElementMenu() {
_elementmenu.alpha = 0;
}
private function dragElement(event:MouseEvent) {
event.target.parent.parent.startDrag(false);
event.stopPropagation();
}
private function releaseElement(event:MouseEvent) {
event.target.parent.parent.stopDrag();
event.stopPropagation();
}
private function dontBubble(event:MouseEvent) {
event.stopPropagation();
}
}
}
DeleteBack.as:
//Element menu back class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
public class DeleteBack extends flash.display.MovieClip {
public function DeleteBack() {
}
public function closeElement(event:MouseEvent) {
dispatchEvent(new Event("closeWindow", true));
trace("event dispatched.");
}
}
}
ElementMenu.as:
//Element menu class.
package pc_mockup {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import caurina.transitions.Tweener;
public class ElementMenu extends flash.display.MovieClip {
private var _elementmenuback:MovieClip;
private var _deletebutton:MovieClip;
public function ElementMenu() {
_elementmenuback = new ElementMenuBack();
_elementmenuback.x = 0;
_elementmenuback.y = 0;
_elementmenuback.width = 100;
_elementmenuback.height = 5;
_elementmenuback.alpha = .5;
addChild(_elementmenuback);
_deletebutton = new DeleteBack();
_deletebutton.x = -5;
_deletebutton.y = 10;
_deletebutton.width = 10;
_deletebutton.height = 10;
_deletebutton.alpha = .2;
_deletebutton.visible = false;
addChild(_deletebutton);
_deletebutton.addEventListener(MouseEvent.MOUSE_DOWN, closeElement);
}
public function expandMenu() {
Tweener.addTween(_elementmenuback, {height:30, time:.2, transition:"easeOutBack"});
_deletebutton.visible = true;
}
public function retractMenu() {
Tweener.addTween(_elementmenuback, {height:5, time:.1, transition:"easeInBack"});
_deletebutton.visible = false;
}
public function closeElement(event:MouseEvent) {
//check that the user really wants to close the element before sending the destroy signal
//perform any closing animations
//this.parent.destroy();
_deletebutton.closeElement(event);
}
}
}
That's it for the meaningful classes. Anything else is either an empty class that's only in there to help make the library object accessible to ActionScript, or a trivial extension of something else.
The code puts a new element onto the stage, gives it a cool little dropdown menu that makes it draggable and has a delete button on it, and should link that button to a function that closes the element.
I've got everything but the closing.
General code criticism also very welcome. Like I said, I have no training, I've been figuring this stuff out for myself, and feedback of any kind from people who know what they're doing is valuable.
Thanks!
SS
PS. In response to Daniel's comment, here are the steps the code takes:
The Background class puts everything on the stage and creates the toolbox.
The toolbox creates the tools, which are like Photoshop's tools. You click on them to select an element you want to add to the stage, then you click inside the "slate" to drop a new instance of that object on top of it. The background creates the instance and saves it in an array of all instances created at runtime.
The new element makes its own dropdown menu, which is the draggable portion of the element and holds the delete button. This menu places an eventListener on the delete button.
When the delete button is clicked, the eventListener placed on it by its parent class calls an event dispatcher inside the delete button class itself.
This dispatched event is caught by the background class (I figured the best class to remove the element is the same class that made it, right?) and triggers the actual code to remove the element.
This code, "deleteElement," is where I'm stuck. I have all the instances in an array, but the event has gone through so many intermediary classes, the MouseEvent, and thus, I suspect, the MouseEvent target, has fallen by the wayside. So the only way to know which element to delete is to find its array index. I have no idea how this would work. Any ideas?
let's do this a bit at a time...
slate.event.target.removeChild(_elementArray[0]); in background.as
why are you using slate.event?
you are passing an event object to the function, but looks like you're using a different event's target, which I don't know where it's coming from or why it's not giving you an error.
it should just be event.target, which should give you the PageElement(formerly known as _newElement)
what I don't know also is why you are removing a child from it which is _elementArra[0] - which really is another PageElement and likely itself if you only have one.
so it looks to me that there are a bunch of things that should have thrown errors. What are you using to compile your code? What about debugger? are you using any?
If you look at your previous question, I added some code there about how to get the parent. So I adjusted it a bit
function deleteElement($e:MouseEvent):void{
var parentMC:MovieClip = $e.target.parent;
parentMC.removechild($e.target);
}
however the problem is that you're not passing a MouseEvent but a blank event
dispatchEvent(new Event("closeWindow", true)); in DeleteBack.as
so this will not pass anything under target, and you can't get it. (target is read only, so new Event(etc) will always have a null target. So essentially that's a bit of a lost cause.
you could set an onject in your singleton and pass which mc is to be deleted, and then the deleteElement would just grab that object. The other option is to look into the signals class which will let you do some better/more efficient event handling.
finally (sort of, there's more but for now) I'd say look into using CASAlib, in particular, use CasaMovieClip instead of MovieClip for extending, as it will delete your movie clips better. If you have a lot of event listeners and you don't clear them properly, they'll end up staying in memory even after you delete them.
of course looking into other frameworks like RobotLegs is a good idea too, it gets you into better practices.
GL
Edit ...
frameworks/micro-architectures:
http://www.robotlegs.org/
http://swizframework.org/
http://puremvc.org/
and many more
I think the important thing is to not get stuck on a framework (though I mention the word often). And the best framework is the framework that is best for you, and to me that means offering a good communication backbone for the app and staying out of the way.
My setup for writing code is this:
FlashDevelop with the Flex Compiler. FlashDevelop is for PC only so if you're on Mac you might want to consider other options like flex. FlashDevelop and the Flex compiler(the compiler only) are both free so you can't go wrong, and once you start using it you won't want to go back to coding in Flash - guaranteed!!
debugging:
Trace is the simplest form of debugging, and it can be quite difficult to understand the problem.
You can use the flash debugger by pressing Ctrl-Shift-Enter to compile and run. You will need to set the break points ahead though.
FlashDevelop has a debugger that works just like the Flash and Flex debuggers and I use it quite often.
But my favorite debug tool has to be de monster debugger
it takes a bit to more to implement, and you need to add some code, but it found issues for me that I couldn't get to using the default debugger only. Definitely worth a look.