AddChild with GetChildByName - actionscript-3

Complete AS3 noob here - I've tried Googling this, but I can't seem to find what I'm looking for (I stumbled across this, http://ub4.underblob.com/as3-naming-elements-dynamically/, but it doesn't weem to work for me).
I'm trying to dynamically add a Movieclip inside another Movieclip through an external AS3 class
Something like this:
var bullet:Bullet = new Bullet(x, y, "right");
var stageBackground:MovieClip = (stage.getChildByName("back") as MovieClip);
stageBackground.addChild(bullet);
However, while this compiles correctly, at run time, I get error #1009 - Cannot access a property or method of a null object reference.
The debug panel tells me the problem is with this line:
stageBackground.addChild(bullet);
But I can't seem to figure out what's wrong with it. I've tried recasting stageBackground as a Sprite, but that didn't change anything. I know the MovieClip back exists - when I reference it through near identical code in my document class, it works perfectly.

You are accessing stage here to find your container, which is very likely the problem.
You are probably thinking that the stage property refers to "the stage" in Adobe Flash authoring environment.
That's not true.
If you placed a MovieClip on "the stage" in the Flash IDE, it ends up on the main time line, this however, is not the thing the stage property is referencing. stage is the topmost DisplayObjectContainer in the display list. It only exists at runtime. It more or less represents the FlashPlayer window, the runtime environment that executes your .swf file.
In short: you are simply looking for your back MovieClip in the wrong place.
The property of a container that represents the main timeline is root.
Do not use root either.
As you can see, your code becomes dependent on the display list
structure of your application. You are already struggling to find the
container that you are looking for. If you change the structure, your code breaks. Even changing the name of the container (for example to something like "background") will wreak havoc.
Instead, use Events.
You are in another class and you want to fire a bullet.
So you create that bullet same as you do now:
var bullet:Bullet = new Bullet(x, y, "right");
Next, dispatch an Event to notify the rest of your code that a bullet was created and it should be placed in the appropriate container:
dispatchEvent(new BulletEvent(BulletEvent.CREATED, bullet));
(Create a custom event class BulletEvent that extends Event, with the apropriate setter and getter for a Bullet object)
Register a listener on the object of your class that creates the bullets, you will catch this event and place the bullet in the container:
var object:YourClass = new YourClass();
object.addEventListener(BulletEvent.CREATED, addBulletToContainer);
function addBulletToContainer(e:BulletEvent):void
{
// adding the bullet to the container
back.addChild(e.bullet);
}
This code would be placed in the parent of your back MovieClip.
The Flash IDE automatically creates variables behind the scenes that have the same names as the instance names. That's why the variable back is available here.
Using events here allows you to literally fire the bullet into your code with somebody else taking care of it, where it's easy to figure out the container it belongs into.

Related

activating a function of a parent as3

I'm attempting to set up a saving and loading system for a flash game I am creating. At the moment I decided to attempt to get the Save File Selection part of my new game menu to work before I proceeded any further with it. I drew up the 'DifficultySelect' screen (which contains save file selection as well as a few other things) in flash, then converted it into a symbol. I made 10 save file symbols inside of this, and created base actionscript files for them. The entire 'DifficultySelect' becomes a child of my 'Main.as' once you proceed through the title screen. I am trying to have my save file data inside of my Main.as, such as levels, health, progress, etc, as well as which save file is currently in use. However, I am unable to create a way for my Main.as to access a MouseClick function for the save file, as I can't simply have a Savefile0.MouseEvent.CLICK event in my Main.as file, as I am not creating a variable to add it to the stage.
I suppose the tl;dr question version of my situation is: How can I get my Main.as to activate one of its functions when SaveFile0 has been clicked? Also, how do I refer to a child from a parent class?
You ask how to refer to a child from parent, but since the child usually "knows" its parent, I suggest following solution:
Your child (SaveFile) should listen for the event (MouseEvent). Once the event is dispatched, you have two options. Either you can dispatch a custom event on parent or directly invoke a public function of the parent.
This is assuming your SaveFile is a MovieClip added to Main:
Inside SaveFile:
var _parentMC:MovieClip = this.parent as MovieClip;
_parentMC.functionToCall();
The best practice, however is to dispatch a custom event in SaveFile, and listen for it in main:
Inside SaveFile:
var customEvent:Event = new Event("customEvent");
this.dispatchEvent(customEvent);
Inside Main:
saveFile.addEventListener("customEvent",functionToCall();
functionToCall(e:Event):void{
}

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

as3 how can i prevent that a new instance is created by entering a frame?

i am working with several nested movieclip objects in a project. but i get into trouble with the buttons i created and implemented in the nested movieclips:
to describe it in a simple way:
I have a main movieclip with five frames, including two buttons with listeners to browse between the frames. Then inside of one Frame I have another movieclip with its own buttons. i instanciated it by hand not through code and gave it a specific name like "nestedMc".
Now I dont want to build the Listeners for those buttons inside the class of the nested movieclip class but in its parent class, which works fine until i then goto another frame in the main movieclips timeline and come back.
obviously every time flash enters a frame its contents get created anew (and therefore get new instance names). I could now try solve this through filling the frames via code.
But maybe there is another way to make sure the frame contains the same instance everytime i enter?
Timeline scripting is a dirty business, and really, a carry-over compatibility layer for Actionscript 2 projects. Whenever possible, I highly recommend not doing it, and simply keeping all of your code in your document class. As you're experiencing, timeline code causes headaches.
Consider instead just creating both states of your Stage (it sounds like that's what your two buttons are jumping between) and simply hiding them offstage or setting their alpha to zero and their mouseEnabled state to false. Furthermore, if the purpose of your frames is to play animation (a tween), consider instead switching to a much more powerful suite such as TweenLite. Moving an object over a hundred pixels (smoothly) can be as easy as:
TweenLite.to(redBall, 3, {x:100});
Now, if you're manually adding these items to the stage, as long as the object is a dynamic one, you can assign an instance name to it which will be saved between frame loads. Be aware the object name is not the same as the instanced name. For example:
var redBall:Ball = new Ball();
redBall.name = "bubbles";
The object's name is Ball, but it's represented as a variable called redBall. Its actual DisplayList name will likely be ambiguous (such as "Instance71"), and I can manually define it as "bubbles". 3 different names for the same object, all very different and necessary.
Even if you give the object a displayList name, you may not be able to reference it through code unless you enable Automatically declare stage instances, which basically creates on each object a pointer to the displayList object.
That said, you can always fetch the object by other means. Obviously, your buttons are always appearing, but you're trying to find a very specific object on the stage. At this point, we can use getChildByName() or getChildAt().
Hope that helps.
-Cheers

Unable to access children in movie clip

Inside flash cs6 I have drawn a flash movieclip in which I set export settings as abc.Gameboard. Inside gameboard I have a bunch of pieces (symbol:Piece) which I export as abc.Piece - both base class set to MovieClip and with class files. The piece has frame labels like hit, over etc.. My problem is accessing the pieces in code so I can eg. gotoAndPlay("mine") - at the moment the event only fires once which is the last piece on the board.
I can set the frame action on this last piece but would like to figure out how to do same for each piece.
I add a gameboard to the stage like so
var gb:Gameboard = new Gameboard();
gb.name = "gb001";
contextView.addChild(gb);
Then
contextView.addEventListener(Event.ADDED, thingAdded);
private function thingAdded(event:Event):void
{
var type:String = event.target.toString();
switch(type)
{
// this runs only once - i want it to run for each piece that is inside the symbol
case "[object Piece]":
var p:MovieClip = event.target as Piece;
p.gotoAndPlay("mine");
break;
}
}
or if there's a better way that would be great.. this looks pretty clunky
Edit: Bit more info about how I'm trying to build the gameboard
Draw a collection of shapes in illustrator - mask it (Gameboard region). Import into Flash as Graphic. Convert graphic to several movie clip symbols (So JSFL can drill down and access masked pieces) - run JSFL script & create 00's of pieces. Then I set export settings on Piece and Gameboard and add Gameboard to the contextView.
I actually wrote an entire article about this once. The ADDED event should fire once for every DisplayObject that gets added. Are you sure that you're not using ADDED_TO_STAGE, which does not bubble? If you're using ADDED_TO_STAGE, then you need to set the useCapture flag to true to get it to fire for all children.
If you want to involve RobotLegs in the process, probably the better way is to simply create a "marker" Class for each specific button that you want to have behave in a different way, then register a mediator for each Class that will manage the behvior. Robotlegs already has the hooks built in to listen for ADDED_TO_STAGE and do this.
However, you could also consider using the Flash IDE for what it's for, which is putting stuff on stage. In that case, your GameBoard instance will be ready in the constructor of your main document Class for you to do whatever you want with it.
MPO is that logic that is outside Gameboard shouldn't know or care how it works internally, and honestly it probably shouldn't even be GameBoard's responsibility to handle simple stuff like button over states and things. That should be up to the button itself. If the buttons don't need to toggle or anything beyond what SimpleButton handles, you can just declare the button instances as Button in the library instead of MovieClip and get all that stuff for free instead of coding it yourself.
Part of being a good coder is in being able to figure out ways not to code everything.
in their Gameboard inside Piece? I want know exactly your Gameboard Structure.
If you're right. try this:
function thingAdded(e:Event):void
{
if(!e.target is Gameboard) return;
var mc:Gameboard = Gameboard(e.target);
var i:int = 0;
while(i<mc.numChildren)
{
if( mc.getChildAt(i) is Piece)
{
var piece:Piece = Piece(mc.getChildAt(i));
piece.gotoAndStop(2);
}
i++;
}
}
Here is my Sample code: Gameboard

AS3: Making a SimpleButton from library sprites: Possible?

I'm working on a small project in AS3, and I need to make some interface buttons. I had them as separate classes at first, but then realized that it was probably overkill, and on top of that, figured out a way to simplify the event calls by making them buttons and assigning the event dispatches to their parent.
ANYWAY,
I tried remaking them using the SimpleButton class, but I can't figure out how to give the buttons any sort of design. Every tutorial on the web uses SimpleButton to make only the most bare-bones Actionscript graphics by actually drawing them with the code (why anybody would want to do that is beyond me), and my attempt at assigning a library item to the upState:
_deletebutton = new SimpleButton();
_deletebutton.upState = mc_deleteButtonUp; <--- exists in my library
doesn't do anything.
The Adobe docs say that the various states take DisplayObjects, which mean they take Sprites and MovieClips, so you should be able to do this. Does anyone know how?
THANK YOU
+1 weltraumpirat
the example in the doc generates the states by code but you can assign whatever displayObject to the different states of the button.
var btn:SimpleButton = new SimpleButton();
btn.downState = new clipFromLibDown();
btn.overState = new clipFromLibOver();
btn.upState = new clipFromLibUp();
btn.hitTestState = new clipFromLibHit();
btn.useHandCursor = true;
addChild( btn );
assuming you have 4 states called : clipFromLibDown, clipFromLibOver ... in your library, this works
You have to instantiate an item from your library in order to use it with ActionScript. To do this, click "Export for ActionScript" and assign a class or base class to mc_deleteButtonUp in the properties panel. Then use the new operator with the assigned class to instantiate it. You can start your button by using the example from Adobe's documentation, then change things to fit your own program.
You're probably not setting the hitTestState property of your SimpleButton instances. This property is a DisplayObject that defines where the user has to move the mouse to get mouse events on the SimpleButton. You will never see the DisplayObject that you set this to. I would suggest just using one of the DisplayObjects you are already using for another state.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/SimpleButton.html#hitTestState
Also you will need to use the new operator as weltraumpirat and nicoptere have already said.