AS3 error 1120 when inheriting document class - actionscript-3

I am working in Flash CS4 with AS3.
I have a TextPage.fla file that contains a dynamic text field (name: PageTitle) as an instance on the stage.
In the document class (TextPage) I set the text of PageTitle according to some XML.
This all works fine.
I have another fla file, SpecialTextPage.fla, and that also has the PageTitle dynamic text field on the stage.
I now try to have the SpecialTextPage document class inherit from Textpage:
public class SpecialTextPage extends TextPage
{
...
}
but I get a "1120: Access of undefined property PageTitle." error when trying to publish SpecialTextPage.
The error location is given as TextPage.as
As a workaround I can just copy the whole TextPage.as file and add in the extra things I need in SpecialTextPage.as but I'd obviously prefer it if I could just extend it.
I got the feeling I am not quite understanding the relationship between flash's objects on the stage and the document class.
Can someone help?

A document class is basically taken and dumped on the first frame of the timeline of a SWF then executed at run time and therefore has access to everything on the stage like they were hand written properties in your class.
Now imagine you create a variable in your SpecialTextPage that you call var i:int; No matter what you do, the parent class will never have access to i (nor should it). The same way, the stage elements linked through to your document class, the parent will never have access to them.
Similar to MovieClip extending Sprite, Sprite can never make reference to the MovieClips timeline because it has no awareness of it.
But not all is lost! A nice way to achieve your goal is to have the following:
/* TextPage */
/* Parent Class */
/* ... */
public function TextPage() {
pageTitle.text = getTitle(); // This textfield would be the one in TextPage.fla
}
protected function getTitle():String {
return "All the text you could ever need in your title";
}
/* ... */
/* SpecialTextPage */
/* Child */
/* ... */
public function SpecialTextPage() {
pageTitle.text = getTitle(); // This textfield would be in SpecialTextPage.fla
}

The way I got around the problem was to disable declaring stage instances automatically.
(Publish Settings --> Flash Tab --> Uncheck 'Automatically declare stage instances')
I then needed to declare pageTitle on my TextPage class as a public field:
public class TextPage extends MovieClip
{
public var pageTitle:TextField;
...
}

Related

Making a simple tamagoci game getting no compiler errors but receiving no output

Kind of new Actionscript and I'm just trying to make a simple tamagoci game. I've wrote all the code out but and receiving no compiler errors but for some reason I'm also not receiving any output messages for my mouse event listeners. Here is all my code, I really can't find the problem and any help would be greatly appreciated. Thanks.
package{
import flash.display.MovieClip;
import flash.events.MouseEvent;
public class Main extends MovieClip{
public var feedButton:MovieClip;
public var tamagoci:MovieClip;
public var disButton:MovieClip;
public var dietButton:MovieClip;
public function Main() {
this.init();
}
private function init():void {
this.feedButton.addEventListener(MouseEvent.MOUSE_DOWN, onfeedMouseDownHandler);
this.disButton.addEventListener(MouseEvent.MOUSE_DOWN, ondisMouseDownHandler);
this.dietButton.addEventListener(MouseEvent.MOUSE_DOWN, ondietMouseDownHandler);
}
private function onfeedMouseDownHandler(event:MouseEvent)void{
this.tamagoci.scaleX += 0.1;
this.tamagoci.scaleY += 0.1;
}
private function ondisMouseDownHandler(event:MouseEvent)void{
this.tamagoci.gotoAndPlay(5);
}
private function ondietMouseDownHandler(event:MouseEvent)void{
this.tamagoci.scaleX -= 0.1;
this.tamagoci.scaleY -= 0.1;
}
Are you using Flash Professional?
You're declaring your variable types in your class here;
public var feedButton:MovieClip;
public var tamagoci:MovieClip;
public var disButton:MovieClip;
public var dietButton:MovieClip;
But then in your constructor, all you are doing is running init();
public function Main() {
this.init();
}
So, this could one of a few things. The most likely is that you have declared your variables, but you haven't initialised them. You've created the variables to hold your objects, but according to your code, they're empty. More specifically, a variable or class property that doesn't assign an object to a variable of an object type will contain a default value of null.
You could prove this in your code by simply putting a condition inside your init(); method;
if(tamagoci == null){
trace("I haven't been assigned an object of type class yet!")
}
So it could be 1 of these 3 things:
1: If you have written your own classes for these class properties/variables, then you need to instantiate them with the new keyword. The general syntax is;
variable_name = new ClassName(parameter_1, parameter_2);
If you are using classes you have written yourself, you have to create an instance of the object, assign it to a variable, and then add it to the stage using addChild();. For example, lets say you've written your own Tamagoci class;
tamagoci = new Tamagoci();
tamagoci.x = 100; // set the x location
tamagoci.y = 200; // set the y location
addChild(tamagoci);
Notice the use of Tamagoci. This is just an example, but this is the class name, which shouldn't be confused with variable/property name. It could just have easily been;
tamagoci = new MovieClip();
But then, this is just an empty MovieClip. It needs a property to display on the screen. A Shape, A Bitmap, or another container class object like MovieClip or Sprite (container classes allow you to nest display objects inside them). But on a basic level, it must contain a visual component to appear on the stage.
2:
Have you made Main your document class? This is the class which will get automatically called when your Flash movie plays. To set this, click on your stage, and in the properties dialogue box on the right, under PUBLISH, type in the name of your class, which is "Main".
3:
If you have created MovieClips in your library in Flash Professional, then you need to go to your library, right click the MovieClips, and select properties. From there, you need to make sure Export for Actionscript is ticked.
Now, if you click on your MovieClips on the stage, then open the Properties tab in the top right of Flash Professional's default layout, then right at the top should be a text field, and if you hover over it, Instance name will pop up as a tool tip. This is where you name your stage objects. Once that is done, you have access to them in your timeline.
If this is how you've done this, then you don't need to declare the variables in your main class, as they are already declared on your stage by Flash Professional and instantiated automatically.

In Flash Pro, how to get the Properties panel to expose the properties you define on your ActionScript class?

I'm dabbling with game design and trying to create some characters for the game. Right now I've just created a single MovieClip that contains a rectangle. The MovieClip symbol extends a class that I've created in Flash Builder that implements the logic of a monster. I can then drag an instance of this monster symbol from the library to the stage and the code works when I run the simulation. So far, so good.
Now I want to create several monsters, all slightly different:
public class Monster extends MovieClip
{
public var isFriendly:Boolean = true;
public var strength:int = 10;
public var catchPhrase:String = "Booyah!";
public function Monster()
{
}
}
One way to do this is to write a new class for each monster that extends Monster and sets the properties I want in the constructor (I'd also have to create a unique symbol in the library for each of these variations too). However, this seems to be overkill if my monsters only differ by their property values.
Looking at the Flash Professional use interface, I see that at the very bottom of the Properties panel is a section that looks like a small table headed by 'Properties/Value'. Can I use this to somehow set the properties of my classes from within the Flash Professional UI? I can't find any info on how this is used.
Okay, I figured it out. The key is converting my symbol into a flash Component.
First I edited my ActionScript class to export the properties I wanted to set (including the Inspectable tag):
public class Monster
{
private var _catchPhrase:String;
public function Monster()
{
}
public function get catchPhrase():String
{
return _catchPhrase;
}
[Inspectable(name = "catchPhrase", type = String, defaultValue = "Booyah!")]
public function set catchPhrase(value:String):void
{
_catchPhrase = value;
}
}
Then I right clicked on the Monster symbol in my library and selected 'Component Definition...'. This brought up the Component Definition dialog. I then entered the name of my ActionScript class in the Class field and clicked the checkmark to validate it. Flash then automatically generated the properties I needed.
I also found this tutorial helpful:
http://redbjarne.wordpress.com/actionscript-3-0-custom-components-from-hell/

Removing a child of a video class called from a private function

I'm making a quiz type animation for work where on clicking an answer it plays a short animation FLV file relating to what you picked. As everything I read points towards AS3 being OOP I decided to make a MovieClip containing an FLV player and linked it to an AS3 file called FLV_Player.as. That way I can create a new instance of the FLV_Player everytime I need to play a video. Here is the code in that file which seems to work fine:
package
{
import fl.video.VideoEvent;
import flash.events.VideoEvent;
import flash.display.MovieClip;
public class FLV_Player extends MovieClip
{
public function FLV_Player(NextVideo:String)
{
animation_player.source=(NextVideo);
animation_player.addEventListener(VideoEvent.COMPLETE, vcompleted);
}
private function vcompleted(e:VideoEvent):void
{
nextFrame();
}
}
}
Now in the DocumentClass.as file I have this code:
private function NewVideo(videoname:String)
{
var nextvideo:FLV_Player = new FLV_Player(videoname);
addChild(nextvideo);
nextvideo.x = 0;
nextvideo.y = 0;
}
So when you click a button, go to the next frame or whatever the prompt is, it calls the NewVideo function and passes the name of whatever video is to be played next.
NewVideo("Introduction.flv");
Now I'm sure I'm going to run in to other issues later down the line as I really have no idea whether anything I've done is how it should be done, but the only issue I seem to be having at this point in time is removing the video and going to the next (or previous) frame to answer another question. I tried:
nextFrame();
removeChild(newVideo);
But it didn't work. Well, it may have gone to the next frame but with the video taking up the whole window it's hard to see if it did or not.
So how do I remove the video I've created? The main issue seems to be that because I had to create a new instance of the FLV_Player class in a private function the child is defined locally "var", rather than "public" or "private" var so I can't reference it again. It tells me that you can only create a "private var" from within the document class but if I make it there it will create the class on load rather than from the function when I'm ready to pass the video name parameter to it. At load I don't know what video I need it to play?
removeChild() must be called from the same object in which it was added. In this case, your DocumentClass. What you're trying to do now is telling an FLV_Player to remove itself, which won't work due to several reasons and bugs in your code.
The correct way to do things would be to have the FLV_Player object dispatch a custom event that your DocumentClass listens for. You need to create a new class which inherits from Event to create your custom event. I'd call it "PlayerEvent". In DisplayClass function you'd do this:
nextVideo.addEventListener(PlayerEvent.PLAYBACK_FINISHED, onPlaybackFinished);
addChild(nextVideo);
Then you need to create the onPlaybackFinished method:
private function onPlaybackFinished(event:PlayerEvent):void {
nextVideo.removeEventListener(PlayerEvent.PLAYBACK_FINISHED, onPlaybackFinished);
removeChild(nextVideo);
}
Inside the FLV_Player class, the vcomplete function should change to:
dispatchEvent(new Event(PlayerEvent.PLAYBACK_FINISHED));
Alternately, you could pass a pointer of the DocumentClass to the FLV_Player object, but this is very messy, can cause serious problems and not at all in the spirit of OOP. But it's a quick fix if you want to be lazy.
Events are an extremely important part of Actionscript 3 and I recommend you read up on them. Here's some good references:
http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.html
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7fca.html
http://www.blog.mpcreation.pl/actionscript-3-0-basics-custom-events-part-1/
I think you're right that your first problem is simply how to reference the new video, so to expand on my comment a bit: You can declare a variable without also assigning a value, so you don't need to have var nextvideo within your NewVideo function. With a class level variable instead, you can then reference whatever you set nextvideo to when you want to remove the video:
public class DocumentClass {
private var nextvideo:FLV_Player;
private function NewVideo(videoname:String)
{
nextvideo = new FLV_Player(videoname);
addChild(nextvideo);
}
private function removeVideo():void
{
removeChild(nextvideo);
nextvideo = null;
}
}

In AS3, can a library symbol extend another library symbol, assuming each are linked to a class?

For example:
Library symbol "Card" is linked to class "Card" which extends "MovieClip". Library symbol "Card" contains a card background image.
Library symbol "Ace" is linked to class "Ace", which extends class "Card". Library symbol "Ace" contains a TextField with a big letter "A".
So we have Ace extends Card which extends MovieClip. Ace therefore extends MovieClip, but does not DIRECTLY extend MovieClip.
When I drop an instance of Ace on the stage and compile the clip, all that shows up is the big letter A. However, I expected the background image from Card to be included, since Ace extends Card, and the Card symbol contains the background.
It seems like Flash ignores symbol content unless it belongs to the top-level class being instantiated. I think it's LAME that one symbol can't extend another. The IDE could easily draw Card as a non-editable background while I'm editing Ace which extends it, and it should instantiate Card's content and then Ace's content when an Ace is instantiated. Thoughts?
Yeah, I've tried to do that. In theory you'd expect the display list of each extended class to stack, but they don't - it works as you describe, where you only see the graphics associated with the most recent class.
It's not a deal-breaker for you, though - various architectural options are at your disposal. You could, for instance, create a CardBackground class which is exported out of your library and has the card shape etc. Then you create a Card class which has a background:CardBackground property. Then your Ace class can extend Card and it should have the desired background.
Technically you're supposed to favor composition over inheritance, but Flash really encourages the inheritance mindset. Once you get used to breaking out of that you'll realize it's possible to create much more powerful, robust classes using composition in the manner described.
Cheers, and I hope that helps!
The base class to your card_mc(Card movieclip) may be your Card class but it doesn't make your Card class synonymous with card_mc.
Try doing this instead:
1) Create a movieclip containing the card background image and call it cardSprite_mc. Give it the class name CardSprite and set its base class to flash.display.Sprite.
2) Create a movieclip containing the textfield containing the letter "A" and call it ace_mc. Give it the class name Ace and a base class of com.cards.Ace.
3) Create a class called Card with the following code:
package com.cards
{
import flash.display.Sprite;
public class Card extends Sprite
{
public function Card():void
{
addChildAt(new CardSprite(), numChildren - 1);
}// end function
}// end class
}// end package
4) Create a class called Ace with the following code:
package com.cards
{
import com.cards.Card;
public class Ace extends Card
{
public function Ace():void
{
}// end function
}// end class
}// end package
Now if you add an instance of Ace to the stage you should see the card background image too.
I hope this helped :)
You can't do this in a programmatic way.
Instead you have to use the Flash Authoring environment. Extend symbols by creating one which includes the base symbol inside it.
And, yes I agree, it's quite LAME.
Taurayi's solution is inspiring, because it establishes that missing explicit link from Class to Symbol, ensuring the Symbol's content is instantiated whether it's the top-level class or just a base class in an inheritance chain. A side effect of that approach, however, is that it adds an extra containment level in Card's content, namely the CardSprite container.
I have managed to implement a practical generic solution that actually preserves the expected stacking behavior of all inherited symbols. For example, if you check "numChildren" on an instance of Symbol2 below, it will be exactly the sum of Symbol1.numChildren and Symbol2.numChildren, so it's a true merge stacking of symbol content.
When your symbol is in an inheritance chain, simply add this "ensureLinkage" call anytime after a call to the super() method.
package
{
public class Symbol1 extends Sprite
{
public function Symbol1()
{
super();
BugFixes.ensureLinkage( this, "Symbol1" );
}
}
}
package
{
public class Symbol2 extends Symbol1
{
public function Symbol2()
{
super();
BugFixes.ensureLinkage( this, "Symbol2" );
}
}
}
Note: Don't forget to make sure your top-level symbol also explicitly defines a class with the above pattern.
When Symbol2 and Symbol1 are linked to corresponding symbols in the library, their content will now stack. Just drop an instance of Symbol2 on the stage, and test the movie. You'll see that Symbol1's content appears under Symbol2's content. (Note: does not appear in the designer, since this is a runtime fix).
The implementation of ensureLinkage is as follows:
package
{
import flash.utils.getQualifiedClassName;
import flash.utils.getDefinitionByName;
import flash.events.Event;
public class BugFixes
{
public static var linkageMonitor:Object = new Object();
private static var linkageMonitorAuthority:Array = new Array();
public function BugFixes()
{
}
public static function ensureLinkage( instance:*, className:String )
{
if (getQualifiedClassName( instance ) != className) //detect non-top-level construction
{
//prevent inevitable factorial-recursive construction
var stack:Array = linkageMonitor[instance] as Array;
if (stack == null)
{
stack = new Array();
stack["numChildren"] = instance.numChildren;
linkageMonitor[instance] = stack;
}
var barredByAuthority:Boolean = false;
if (linkageMonitorAuthority.length > 0)
barredByAuthority = (linkageMonitorAuthority[linkageMonitorAuthority.length - 1] as Array).indexOf( className ) > -1;
if (stack.indexOf( className ) == -1 && !barredByAuthority)
{
stack.push( className ); //remember construction
trace( "ensuring Linkage of inherited class " + className );
//perform top-level construction to trigger symbol linkage and child object instantiation
linkageMonitorAuthority.push( stack );
var temp:* = new (getDefinitionByName( className ) as Class)();
linkageMonitorAuthority.pop();
//Merge children
while (temp.numChildren > 0)
instance.addChild( temp.getChildAt( 0 ) );
//Merge properties
for (var prop:String in temp)
instance[prop] = temp[prop];
}
else
{
trace( "skipping redundant construction of: " + className );
}
}
else
{
var stack:Array = linkageMonitor[instance] as Array;
if (stack != null)
{
var nc:int = int(stack["numChildren"]);
trace("construction completing for " + getQualifiedClassName( instance ) );
for (var i:int = 0; i < nc; i++)
instance.setChildIndex( instance.getChildAt( 0 ), instance.numChildren - 1 );
}
delete linkageMonitor[instance]; //top-level constructor is completing, all relevant sub-objects have been constructed
}
}
}
}
Basically, it detects whether symbols are going to need manually instantiated, by seeing whether the qualified class name of the instance matches the expected class name passed to the call from the class itself. Since it's called after "super", the calls start at the deepest class and ensure its library symbol's children are instantiated by making a temporary top-level instance and claiming its children as its own. The very first call for the instance also grabs the original number of children present, since the top-level clip in the stack will have already instantiated its children before any constructor code is run at all. By storing that number, a final step can then pull those initial children to the top where they belong. The class ensures no unnecessary recursion takes place, by using an "authority" stack to ensure the main stack is always visible to child constructors.
One issue is that static strokes are not persisted, but that is only because AS3 provides no API for accessing strokes (i.e. once you draw a line in the designer, or with graphics.lineTo, there is no way to programatically access that stroke for enumeration or modification purposes, except to clear all strokes at once). So that's not a limitation of this approach, but rather Flash's API.
Perhaps Adobe was simply unable to come up with this implementation :P
Please note that if your symbols do any work that ties the symbol instance to other code, there could be an issue, since this class claims ownership of children from a temporary instance. It also claims the values of variable references from the temporary instance using a for loop, but that's the best it can do in a generic implementation such as this.

How do I access the Document Class' properties from the time line in AS3?

I am building your standard slideshow flash header for a web page.
There are three main parts:
The Slideshow class
A controller class that is used as the projects Document Class
Some linking timeline code.
The slideshow class has all the functionality, so I used the Document class to create a new instance of the slideshow and keep a property variable called slideshow that keeps a reference the Slideshow instance.
import flash.display.MovieClip;
import flash.events.Event;
public class Header extends MovieClip
{
public var slideshow:Slideshow;
public function CSYC_Header()
{
var picturesURL:String = "images/pictures.xml";
var picturesURLFVar:String = root.loaderInfo.parameters.pictures;
picturesURL = picturesURLFVar ? picturesURLFVar : picturesURL;
slideshow = new Slideshow(picturesURL, Slideshow.FADE);
slideshow.init();
addChild(slideshow);
}
public function hello():void{trace("Hello");}
}
My next step now is to use Adobe Flash Professional to draw some play and stop buttons, and then link thier click events to call slidshow.play()/.pause(). This code is just place in the timeline:
import flash.events.MouseEvent;
pause_control_btn.addEventListener(MouseEvent.CLICK, pauseClicked);
play_control_btn.addEventListener(MouseEvent.CLICK, playClicked);
addChild(pause_control_btn);
addChild(play_control_btn);
function pauseClicked(e:MouseEvent):void
{
//the play and pause buttons are on the stage and have the following names as
// thier instance names: pause_control_btn, play_control_btn
pause_control_btn.alpha = 0;
play_control_btn.alpha = 0.37;
slideshow.pause();
}
function playClicked(e:MouseEvent):void
{
pause_control_btn.alpha = 0.37;
play_control_btn.alpha = 0;
slideshow.play();
}
Despite me being able to call regular methods that are in the Doc Class from the timeline, I can not call properties without the following error, for example when i say slideshow.play():
1061: Call to a possibly undefined method play through a reference with static type com.example.test:Slideshow.
So am I missing something obvious, or will I have to make a method on my document class every time I want to wire an event to call an object in my Document Class?
There's no need to put that code for the buttons on the timeline; you can reference those objects by their instance names from within the Document Class. That'd be the easiest solution, avoiding the timeline altogether, I think.
Otherwise possibly a call to parent.slideshow, or root.slideshow (though I think root is AS2, I don't quite remember) would give you access to that instance from the timeline. The former option is probably still the better option, and keeps your code in one place.
Hope that helps.