An ActionScript file must have at least one externally visible definition - actionscript-3

My main timeline code reads
var BlueBox:MovieClip = new bluebox();
include "bluebox.as";
My AS file reads
addChild(BlueBox);
I am getting weird errors, the documentation I read on flash's website said you can include ActionScript in an AS file as if it were on the timeline with the include function or with <mx:Script source="Box.as" /> however I am getting an error about classes or something irrelevant nonsense. I am not trying to include a class, just trying to include the AS file.
Line 1 5007: An ActionScript file must have at least one externally visible definition.

figured it out finally
just add a function definition in AS file
choosefunctionname(); // to execute function of code
function choosefunctionname(): void
{
//code here
}

Related

Actionscript 3 - Accessing a variable on the main timeline

I'm trying to make a file that is easy for a non-flash user to use/reuse to easily display content. The key here is that this file is to be a template for a novice user to just copy/paste some very minimal code to create different "flash card" type swf files.
The file I am creating has multiple buttons on the main timeline which when clicked, attaches a movie clip which will display a dynamic text area with content specific for the button that was clicked. The content for the text area will be loaded from a separate text file.
For the sake of this example, I'm just going to refer to one button...
So, on the main timeline, in frame 1, I have a variable definition:
var myFilename1:String = "mySampleFile2.txt";
When the button on the main timeline, in frame 1 is pressed, a movie clip is loaded which contains a text area. The content for the text area is located in that file: mySampleFile2.txt.
If I hard-code the file name, it works like a dream:
myTextLoader.load(new URLRequest("mySampleFile2.txt"));
But I don't want to hard code the file name. I want to refer the variable in the main timeline. In AS 2, it would have been
myTextLoader.load(new URLRequest(_root.myFilename1));
In AS3 I thought it would be either:
myTextLoader.load(new URLRequest(root.myFilename1));
OR
myTextLoader.load(new URLRequest(MovieClip(this.parent.root).myFilename1));
When I run the code I get the following error and when I run a trace I get the file name is NULL.
TypeError: Error #2007: Parameter url must be non-null.
How to I access the file name stored in the variable on the main timeline?
*************************** UPDATE! *************************
So I just discovered that the issue is related to a button on the screen. The button is one from the buttons library. If I remove the button, everything works great. But as soon as that button is on the main timeline, it makes it to I cannot access the variables using MovieClip(root).variable_name;. Unfortunately I want that button to trigger the events within the MovieClip. Any thoughts?
Not a good idea
You are not able to achieve your goals with this bad practice approach.
to use/reuse
The code you want to provide is not very reusable. It heavily relies on one variable existing in a certain place. Therefore it cannot be use twice in a project.
But I don't want to hard code the file name.
And now you are hard coding the variable and its location. If you considered hard coding the file name to be a bad things, consider this a bad thing, too.
What the problem is
Basically speaking, the problem is that your component is reaching out to grab this variable from somewhere within your project. But it is not the concern of the component to find the content it should display. It is not well encapsulated.
Learning from existing things
You want to display text. Let's take a look at the TextField class to see how it displays text.
var tf:TextField = new TextField();
tf.text = "hello";
addChild(tf);
As you can see, the text that it should display is passed to the TextField object. There is not some arbitrary variable one has to set in order to modify the text, as you are planning to do:
var tf:TextField = new TextField();
var someArbitraryVariableThatModifiesATextField = "hello";
addChild(tf);
There is no obvious connection to the TextField object and if there's a second TextField, this doesn't work at all.
Applying that to the problem at hand
Just like the TextField, your "flash card" should receive the file as a parameter. Either pass it to the constructor as seen in the example below, or create a method that takes it as a parameter.
var card:Card = new Card("mySampleFile2.txt");
addChild(card);
Additional thoughts
Create additional methods to set the values individually. There's nothing worse than some code that does exactly what one wants, but only operates on files and one doesn't have a file. The goal is again, to make it easy to reuse the code
Use the [Inspectable] meta tag to allow the user of your code to modify the properties at author time. This can be used to the extend of modifying properties belonging to a physics engine, now this is what I would call easy to use for a novice user.
Instead of writing code and thus requiring to recompile the file again, take the information (either the path to the file or it content) from the flashars that are passed to the .swf file when it is embedded into an html page. This makes the single .swf file truely reusable and easiest to use, because there's no As3 coding required whatsoever.
I haven't tested this but I believe the "root"-stage would be:
this.parent
In the child swf.
check out this question: as3 access variable from parent swf
Good luck!
Accessing variables or movieclips at main timeline is as following:
AS2:
_root.variable_name;
AS3:
MovieClip(root).variable_name;

How to link functionality from Class file to DocumentClass and to Movieclips in AS3?

and i am having problem, switching from Timeline code to OOP/Document Class. I managed to build the Fla with AS3 on timeline with no problem. But totally clueless when on OOP.
I had been told that Scenes are no good and i should stick to saving my scenes as Movieclips.
My situation is as such:
I have 8 pages of PSD files, and i imported each PSD into Flash Pro, and each PSD has a few buttons and textinput. First page is Login page, second page is register page etc.
My questions are:
1.) How should i save the PSD? Do i save them as nested Animation,(Giving each item on PSD a symbol? Buttons and textinput?) Then saving that PSD as 1 movieclip(Nested Animation?)
I already tried importing the PSD with Flash Layers onto Stage,then giving each buttons and Textinput their Properties then savin them as Nested Animation, but do i call that Movieclip from the Class document? Or do i link All Movieclips in the Document Class(Main.as)?
2.) How do i access the Movieclip from Class file, I tried var login:Login = New Login, then addChild(login). That adds the movieclip but none of the functionality works, and errors saying Access of Undefined Properties for every single button's Name.
3.) And if 1 button is clicked and that links to another page(PSD) do i do the Below?
h.addEventListener(MouseEvent.CLICK, fl_ClickToGohome);
function fl_ClickToGohome(event:MouseEvent):void
{
gotoAndStop("register.as");
Thanks for your time
Programming in .as files apart from the timeline isn't too different. If you're using the document class (found in Actionscript Settings panel), you may be feeling the abrupt change in class specific programming a bit too daunting. For a smoother transition, you can simply dump your current timeline code into a new code.as file (as-is), and simply drop the following line in your timeline:
include "code.as";
This effectively just copy/pastes the code and runs it, except now you can use a proper code editor (such as Sublime Text). Furthermore, as it's not a new class, the code you write there has the same scope as your timeline.
2). Functions only have access to the namespace their created in (this is probably why your document class was throwing "Access of Undefined Properties" errors). You can always pass a reference to an object (and by extension, its namespace) through function arguments.
1). To answer your first question, you can create your movieclips, and nest them as deeply as your want, in any order you want. The important thing is to be aware of how to navigate to that object. Take for example the following stage heiarchy:
root:MainTimeline ¬
0: PSD_one:MovieClip ¬
0: backgroundImage:Bitmap
1: button:Sprite
2: txt:TextField
1: PSD_two:MovieClip
2: PSD_three:MovieClip
The timeline has 3 objects, each with a zero-based index. The first PSD is a MovieClip DisplayObjectContainer with 3 child elements, each with name properties that we can use to address the objects. In Flash IDE we label these "instances", and they automatically become properties on the parent object due to internal magic the IDE has enabled in the Actionscript Settings panel labeled "Automatically declare stage instances".
This means, to change the text on txt, we can write PSD_one.txt.text = "foo". Without this setting, you'll need to use container methods like root.getChildByName("PSD_one").getChildAt(2). Outright calling txt.text = "foo" will never work because there is no property on the current scope called that (ie., this.txt is implied).
3). gotoAndStop is a MovieClip method that controls timeline frames. If you're really going to make a clean break from the old way of doing things, you should drop your use of frames.
There are two ways you could approach displaying these PSDs.
Method 1:
You instance your PSDs on the stage with the Flash IDE and give each one a unique name that you can then reference in your code. Assuming the layout above, you may simply move each PSD offscreen (such as with PSD_two.x = this.loaderInfo.width), and swap them to the center of the screen when you want to "go" to the next "frame".
Method 2:
You've imported your PSDs into your library as you're accustomed to, but do not instance them. Instead, you jump straight into your code and when someone clicks on button h to go to fl_ClickToGohome, your function picks the library object and manually instances it, and parents it to the stage with addChild().
function fl_ClickToGohome(e:MouseEvent):void {
var psd:PSD_one = new PSD_one();
addChild(psd);
}
This sort of approach would be preferable if you're going to start getting into dynamic loading of assets. Of course, rather than having an already created PSD_one class in your library, you'll simply use URLLoader() or Loader(), and you'll want to save out compressed images, not full-blown PSDs.
I hope this helps. I know I haven't answered your questions directly, but sometimes the issue stems from manner of implementation, not specific code.
-Cheers
To answer your questions...
1: Pathing to Objects
If you're not sure what the path is to your objects, try this function. It's a trimmed down version of the one I use in my own work.
function listLayers(obj = null, indent:String = ""):void {
// If no object was provided, start with the MainTimeline
if (obj == null) { obj = root; }
for (var i:int = 0; i < obj.numChildren; i++) {
// Make a pointer to the object at this index
var item = obj.getChildAt(i);
// Get the item type
var type:String = flash.utils.getQualifiedClassName(item);
if (type.lastIndexOf("::") != -1) {type = type.split("::")[1];}
var msg:String = indent + i + ": " + item.name + ":" + type;
if (type != "TLFTextField" && item.hasOwnProperty("numChildren") && item.numChildren > 0) {
trace(msg + " ¬");
listLayers(item, indent + " ");
} else {
trace(msg);
}
}
}
This will print out the structure of your stage in much the same format as you saw above (with the PSDs). You can then use getChildByName from the root to the child (the full path must be provided, much like navigating folders on your hard drive).
2: Public/Private Namespaces
These are only to be used in dedicated classes. If you're using the include method I mentioned, you omit these as you would in your timeline code. They denote whether a property/method is accessible from "the outside" of the class. If you're just starting out, leave everything Public. You can worry about memory optimizations later when you're ready to tackle it.
3: How does the class access the buttons?
The easiest way to think of the DisplayList is as a folder structure.
You might write
C:/Users/Me
Which in the DisplayList would look more like
C.Users.Me
Of course, we don't have a root location called C, and your objects are probably called PSD_one and myButton, so we can rewrite it as follows...
root.PSD_one.myButton
This is assuming you actually have those properties pre-defined on the objects, which does not happen when dynamically creating objects. In those cases, you'd string together your commands, as follows:
root.getChildByName("PSD_one").getChildByName("myButton")
When you write a Class, it's like creating a new computer on your network. There is no direct relationship between that Class and your MainTimeline. You need to connect them.
If you instance a DisplayListObject (such as a Sprite) and add it to the stage, the object automatically gains a few properties like stage, root, and parent which until it was parented were in fact null. These are properties that the class can reference from inside itself to connect to the MainTimeline and access objects on it.
Conversely, if you wanted to you could pass a reference to the stage to the class from the constructor arguments, as follows:
var foo:MySprite = new MySprite(stage);

Actionscript 3 getDefinitionByName not seeing classes

I've worked around this with a hack for now, but would rather know if there's a right way to do it. I have a function that churns out MovieClips, which are tiles for a map that I then attach to the stage. It determines which class of tile to use based on a string variable, like this:
// symbolID holds our class name, determined by logic above
var newClass:Class = getDefinitionByName(symbolID) as Class;
var newtile:MovieClip = new newClass();
This works, but only if an instance of the class already exists somewhere else in the code. It could be anywhere-- in the document class, in some buried function of a helper class, it doesn't seem to matter. If not, Flashdevelop throws error 1065, "Variable (the variable) is not defined". I mention that I'm using Flashdevelop because it seems like it might be compiler-specific, but I'm not sure.
My hack fix is to do this:
var a:baseTile;
a as anotherTile;
a as aThirdTile;
and so on, which works, but definitely isn't ideal if I'm going to have hundreds of these tile classes eventually.
Edit: I should add that these movieclips are coming from a .swc file, which comes from Flash Professional.
You have to use the 'hack'.
getDefinitionByName() can only work with classes that exist at runtime. Unfortunately, if you don't make use of a class, it won't be compiled and won't exist at runtime.
Library symbols make getting around this a little easier. If you check the box that has them available automatically at a given frame, you can just make sure your getDefinitionByName() calls are done during or after that frame.
While SWC libraries can include specific classes to include in the build path, Flash compiler will not link unreferenced classes to a SWF; therefore, requires a linkage library.
Example linkage to retain classes:
/** linkage library */
private static const classA:ClassA;
private static const classB:ClassB;
private static const classC:ClassC;
Another option would be to load the classes from a RSL (Runtime Shared Library).
Basically yes, you need to have a strict reference of that class somewhere in your code. You can even make that reference "unreferenced" elsewhere. I have a hundred of classes like this, and I had to make a single Array of these classes, located somewhere inside the project. I have placed it aside the function that calls getDefinitionByName() to make sure the classes are available in that function.
private static const dummy:Array=[Rock01, Rock02,...,Gem01,Gem02,...];
So you can use such an Array listing all your tiles that you have in your project and want to be accessible by getDefinitionByName().

Loading SWF in AS3 but flash keeps repeating constructor function, what should I do?

I am importing several external files using the Loader-class, and one of them is an swf-file. When doing so (I had done it successfully before, so did not expect any issues), I ran into all sorts of errors, and finally Flash crashed.
I put down a trace in the constructor function, and it didn't trace just once, but kept on tracing, suggesting that the constructor was stuck on loop. I guess the loading of too many of the same swf is what causes flash to eventually crash.
Here is my code (the swf im loading is now a simple test-file which contains an image and no code):
private var slides:Loader = new Loader();
public function DocumentClass()
{
trace(1)
slides.load(new URLRequest("Resources/Slides.swf"));
slides.contentLoaderInfo.addEventListener(Event.COMPLETE, SlidesComplete);
}
public function SlidesComplete(evt:Event):void
{
slides.contentLoaderInfo.removeEventListener(Event.COMPLETE, SlidesComplete);
addChild(slides);
}
This traces "11111111111..." and everything dies in the end.
HELP!
Try putting a stop() action at the top of the swf you load in (either in actionscript, or on the timeline). It's possible that the swf is being loaded in and is running a play and running on loop in the mean time (hence your code running over and over).
I would do a progress watch until the swf is fully loaded, then jump to your display frame:
Create a section for loading (your choice if you want to use a preloader)
Create another section (set of keyframes) for loaded content. I use keyframes because it's easy, but you could also wait to instantiate classes until loading is complete.
Below is a snippet I occasional build from:
// stop the playhead from moving ahead
stop(); // you can also use gotoAndStop("loading"); if you want
function loaderProgressHandler(event:Event):void {
// switch the framehead to main which will show your content
if(event.bytesLoaded >= event.bytesTotal) {
event.target.removeEventListener(Event.PROGRESS, this.loaderProgressHandler);
this.gotoAndStop("main");
}
}
this.loaderInfo.addEventListener(Event.PROGRESS, this.loaderProgressHandler);
Hope that helps!
I was just stuck on this same problem.
In my case it turned out that the problem was down to the swf having the same document class name as the swf that was loading it.
eg. Main.as was loading another swf that also had its document class called Main.as - Changing this to anything else solved the infinite loop.

What is the correct way to add components to the library of a Swf?

I've been having a problem that's plagued me many times in the course of developing a Flash project. It looks something like this:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at fl.containers::BaseScrollPane/fl.containers:BaseScrollPane::drawBackground()
at fl.controls::TileList/fl.controls:TileList::draw()
at fl.core::UIComponent/::callLaterDispatcher()
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/addChildAt()
at fl.controls::BaseButton/fl.controls:BaseButton::drawBackground()
at fl.controls::BaseButton/fl.controls:BaseButton::draw()
at fl.core::UIComponent/drawNow()
at fl.controls::ScrollBar/fl.controls:ScrollBar::draw()
at fl.core::UIComponent/::callLaterDispatcher()
Now, in my case, this error stems from initializing components in code when they have not been explicitly added to the fla's component library in CS4. In the past, I have run into this issue when trying to dynamically create ScrollPanes in code. I have solved it by adding ScrollPane components to my Main.fla's library. This seemed to work for a while.
Now, I am trying to use an AstraFlash AutoComplete box. I have imported the proper fla files into CS4, and placed an AutoComplete box into my Swf. Everything builds fine, but the above error occurs when the Swf is loaded. My thought is that the AutoComplete box is trying to create a ScrollPane as part of its functionality. Ok, I understand this, so I add the ScrollPane component to the library as well with the same results.
Usually I would just mess with the library components/settings until I get rid of the error, but I'm sick of running into this, and I want to know the correct way to solve the problem. So, here are a few questions I have:
When are you required to add a component to a Fla's library rather than just creating the component in code?
Which Flas do you need to add the component to? Just the one using it? Or all of parents of that Fla as well?
Let's say the Autocomplete component requires a ScrollPane component. Why isn't this dependency recognized when I add the one component? Why must I explicitly add it?
What is the difference between adding a component to the library, and adding it to the library's 'Component Assets' folder? What is this folder's purpose?
I really need this AutoComplete component to work. Assuming the AS3 code is correct, and I am still getting the above error, what settings do you think are probably incorrect? Out of frustration, I have tried adding every possible component to the library, as well as to the library's component assets folder just to have a starting point, but I still get the error.
Any help is appreciated.
I'm not sure I understand you're setup. What I'm assuming is you have a child fla that contains some components and you need to create instances of those components in a parent(loader) fla.
It should work if you compile the classes from the child fla into the parent fla, but that would duplicate things.
The issue is when you load a swf, the classes sit in a different ApplicationDomain.
Simply put, that's the thing that manages classes in a swf so you don't have class collisions between a loader and loaded swf and some other security stuff.
I made a simple fla that holds a Button component(fl.controls.Button) and loaded that from a loader fla. I didn't add the Button component to the loader fla, but did create a button instance, using the loaded swf's application domain. Here's how:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
loader.load(new URLRequest('componentContainer.swf'));
function loaded(event:Event):void {
addChild(loader.content);
var SWFButton:Class = loader.contentLoaderInfo.applicationDomain.getDefinition('fl.controls.Button') as Class;
var button = new SWFButton();
button.label = 'Test';
addChild(button);
}
There is a page in Programming Actionscript 3.0 about thise kind of issues and how to use ApplicationDomain, but I didn't fully understand it to be honest.
HTH,
George