How to get the width of a MovieClip for a different frame instantly? - actionscript-3

Is there a way to get the width of a MovieClip (that does have a name) on a different frame? I have tried to using .width and .getBounds(null).width, however, both of them will give me only the width of the current frame. I have tried to do gotoAndStop(frameiwant), but the information doesn't seem to be correct until at least the next frame
I would like to get the width of the frame instantly so I don't have to wait until the next frame for the width.

The only way I could think of doing this was to have an initial phase in your project which will:
Run through all of the frames in your timeline. Create an object which will hold information about the children in that frame. It can be called Frame.
Iterate over all the children that are added to the stage in that frame and add a definition object that describes that child. The description can be as basic or vast as you need. We can call this class an ObjectDefintion.
The downside of this process is that you need to wait for the FRAME_CONSTRUCTED event like #Larusso pointed out in his answer. This means that the frame actually has to finish rendering before you are able to get information about its children, which of course means you have to go through and render every single frame in your timeline during this phase. All you can really do to mitigate this problem is set the frameRate to something high and then set it back when you're done assessing all the frames.
I have set this up and it works well - I'll paste each class and try explain what they do.
So for your document class (or whichever MovieClip holds the frames you want to look at), I have this:
public class Main extends MovieClip
{
private var _userFrameRate:int;
private var _frames:Vector.<Frame> = new <Frame>[];
public function Main()
{
_userFrameRate = stage.frameRate;
stage.frameRate = 120;
addEventListener(Event.FRAME_CONSTRUCTED, _assess);
}
public function getFrame(index:int):Frame
{
return _frames[index - 1];
}
private function _assess(e:Event):void
{
var frame:Frame = new Frame(this);
_frames.push(frame);
if(currentFrame === totalFrames)
{
removeEventListener(Event.FRAME_CONSTRUCTED, _assess);
gotoAndStop(1);
stage.frameRate = _userFrameRate;
ready();
}
else play();
}
public function ready():void
{
// Start here.
// There is a MovieClip on frame 10 with the instance name 'test'.
// We can get the width of it like this.
trace( getFrame(10).define("test").property("width") );
}
}
This basically initializes the phase in which we will run over each frame in the MovieClip and assess its children. The ready() method is used as the entry point for your code post-assessment.
Next we have the Frame class, which serves to hold information about children related to a frame:
public class Frame
{
private var _main:Main;
private var _content:Object = {};
public function Frame(main:Main)
{
_main = main;
update();
}
public function update():void
{
_content = {};
for(var i:int = 0; i < _main.numChildren; i++)
{
var target:DisplayObject = _main.getChildAt(i);
// This will be explained below.
var definition:ObjectDefinition = new ObjectDefinition(target, "x", "y", "width", "height");
_content[target.name] = definition;
}
}
public function define(name:String):ObjectDefinition
{
return _content[name];
}
}
It's pretty straightforward - you give it a reference to Main so that it can check children that are existent within it each frame.
The ObjectDefinition class is also pretty straightforward, acting purely as a repository for data that you want to keep track of on each child of the frame:
public class ObjectDefinition
{
private var _definition:Object = {};
public function ObjectDefinition(target:DisplayObject, ...properties)
{
for each(var i:String in properties)
{
_definition[i] = target[i];
}
}
public function property(property:String):*
{
return _definition[property];
}
}
You'll notice that the constructor accepts the target DisplayObject that will be defined, as well as any amount of properties you want to keep track of as strings (see above within Frame for implementation).
Once complete, you can chain the methods Main.getFrame(), Frame.define() and ObjectDefinition.property() to get properties of children that will exist throughout the timeline. For example, if you have a MovieClip with the instance name square on frame 15 and you want to get its width and height, you can do this within .ready() like so:
var square:ObjectDefinition = getFrame(15).define("square");
trace(square.property("width"), square.property("height"));
Of course this process is not ideal - but unfortunately it is the only way I can see that what you want to achieve is possible.

You have to listen to a specific event before you can ask for the information.
clip.addEventListener(Event.FRAME_CONSTRUCTED, frameReadyHandler);
clip.gotoAndStop(frame);
function frameReadyHandler(event:Event):void
{
clip.removeEventListener(Event.FRAME_CONSTRUCTED, frameReadyHandler);
var width = clip.width;
}
The Frame constructed event is the first of several events that gets dispatched. It gets dispatches right before the frame script gets executed. You could also wait for the on enter frame event.

You could add an event listener for 1 millisecond and test if the previousWidth you had stored is different. If it is, there you go. If not, its probably listening to the same frame.
A 1 millisecond timer is not such a big deal, stop it if you don't need it, resume it if you do, else, keep it running constantly. When it changes, dispatch an event or whatever needs to happen.

If you know the maximum size of the MovieClip, you may try this:
// Create movie clip
var movie :MovieClip = new MovieClipWith3Frames();
// Move to second frame
movie.gotoAndStop(2);
// Create bitmap witch magenta background
var bd :BitmapData = new BitmapData(200, 200, false, 0xFF00FF);
// Draw second frame
bd.draw(movie);
// Found the bounds of shape
var movieBounds:Rectangle = bd.getColorBoundsRect(0xFFFFFF, 0xFF00FF, false);
trace(movieBounds); // (x=42, y=15, w=32, h=33)

Related

Best way to read a property from a MovieClip?

I got a .fla file, where inside I have some movieclip instances placed in the scene. I need to iterate through them and gather some data, like position, name, and custom properties.
These custom properties, I don't know how to pass them, I know one way that works so far is to use the accessibility properties panel (Flash Pro CC), and then in the code I can just read them. However there should be a better way I assume.
If I have understood correctly your question and what you have said in your comments about the answer of #Aaron, you have an swf file, which you load dynamically, and you want to get/set some of its MovieClips properties, if it's the case, take this example :
MyMC.as :
public class MyMC extends MovieClip
{
private var timer:Timer;
private var rotation_speed:int = 1;
public function MyMC() {
}
public function set_Rotation_Speed(_rotation_speed:int): void {
this.rotation_speed = _rotation_speed;
}
public function get_Rotation_Speed(): int {
return this.rotation_speed;
}
public function start_Rotation(): void {
this.timer = new Timer(500, 10);
this.timer.addEventListener(TimerEvent.TIMER, on_Timer);
this.timer.start();
}
private function on_Timer(e:TimerEvent): void {
this.rotation += this.rotation_speed;
}
}
Then, in my swf.swf I have an instance of that MovieClip.
I loaded the swf.swf using this code :
var loader:Loader = new Loader()
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, on_SWFLoad);
loader.load(new URLRequest('swf.swf'));
And to set/get some of my MovieClip properties, I did :
function on_SWFLoad(e:Event): void
{
var swf:DisplayObjectContainer = DisplayObjectContainer(loader.content);
var num_children:int = swf.numChildren;
for(var i:int = 0; i < num_children; i++)
{
var child:MovieClip = MovieClip(swf.getChildAt(i));
// get the name
trace('name : ' + child.name);
// set the position
child.x = child.y = 100;
// get the class name, in my case it's MyMC
var class_name:String = getQualifiedClassName(child);
// get all the details of the child
trace(describeType(child));
child.set_Rotation_Speed(45);
child.start_Rotation();
trace(child.get_Rotation_Speed()); // gives : 45
}
addChild(loader);
}
You can use the describeType() function To get all the properties of your instance.
Hope that can help.
First of all, you can set properties on timeline instances from code. There's nothing special about this. For example:
Place an instance of a library symbol on a keyframe
Give it an instance name in the Properties panel, for example "myInstance"
On the same keyframe put some code that refers to it, such as myInstance.color = "red"
You can also create and assign custom properties by making the symbol a component:
Right-click on the symbol in the library and choose "Component Definition"
Add custom properties in the Parameters table. It's now a component symbol.
On the timeline, place an instance of the symbol and use the Properties panel to set its parameters.
You can do a lot more with components if you want, such as live preview and compiled components. More info can be found here here: http://www.adobe.com/devnet/flash/learning_guide/components/part03.html

addEventListener of random MovieClips

I'm making a game in AS3.
I've got in my code :
public var _classes:Array = new Array(poubelle1, poubelle2, poubelle3);
public var _movieClips:Array = new Array();
public function apparitionDechet(event : TimerEvent):void{
_movieClips.push(new _classes[Math.floor(Math.random() * _classes.length)]());
stageRef.addChild(_movieClips[_movieClips.length-1]);
I'm trying to put an addEventListener on the MovieClips.
The player should be able to click on a MovieClip when it's appearing or he can wait. Few will appears, and he can click on them at any moments.
Each clicks will make the MoviClip disapear..
So I've put :
_movieClips[1].addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
public function removePoubelle(e:MouseEvent):void{
if(e.target=="_movieClips[0]"){
trace("ok1");
}
if(e.target=="_movieClips[1]"){
trace("ok2");
}
if(e.target=="_movieClips[2]"){
trace("ok3");
}
but it's not that...
Do you know how I can do that ?
It's my first time that I'm using the randomly apparition of MovieClips...
Thank you very much,
EDIT
So I've followed your tips and did this :
public function apparitionDechet(event : TimerEvent):void{
var mc:DisplayObject = new _classes[Math.floor(Math.random() * _classes.length)]();
_movieClips.push(mc);
stageRef.addChild(mc);
mc.addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
public function removePoubelle(e:MouseEvent):void{
var mc:DisplayObject = e.target;
var i:int=_movieClips.indexOf(mc);
if (i>=0){
_movieClips.splice(i,1);
mc.parent.removeChild(mc);
}
}
But I've got the error 1118 Implicit coercion of a value with static type Object to a possibly unrelated type flash.display:DisplayObject
EDIT 2
Quick question though, is it possible to do :
if(stageRef.contains(poubelle1)) {
trace("poubelle1détécté");
}
if(stageRef.contains(poubelle2)) {
trace("poubelle2 détécté");
}
?
movieClips poubelle1 and poubelle 2 are defined like this
public var _classes:Array = new Array(poubelle1, poubelle2, poubelle3);
public var _movieClips:Array = new Array();
it doesn't seem to work if I do that.(error 1027 Contrainte implicite d'une valeur du type Class vers un type sans rapport flash.display:DisplayObject) Any idea why ?
Do you want me to create a new post ?
Thank you
If you are to remove the movieclip that was clicked, you already have it as the event's target. So you get its parent and call removeChild(). Don't forget to remove the event listener off the target.
public function removePoubelle(e:MouseEvent):void {
var mc:DisplayObject = e.target as DisplayObject;
if (!mc) return; // typecast failed
mc.parent.removeChild(mc);
// mc.removeEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
// the line above might not be needed as the listener weakly references the mc
}
And you put the listener as soon as you create your new movie clip.
public function apparitionDechet(event : TimerEvent):void {
var mc:DisplayObject = new _classes[Math.floor(Math.random() * _classes.length)]();
_movieClips.push(mc);
stageRef.addChild(mc);
mc.addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
See, how you can avoid referencing the newly created movie clip without ugly _movieClips[_movieClips.length-1] construction? You just make a local variable which is then instantiated for your random MC out of _classes, and you then use the variable to do everything else that's needed at the time of creation.
But, this is still not enough - your "poubelle" is still inside your _movieClips array, so it'll grow. You need to clean up the array too. So, add this code to removePoubelle:
var i:int=_movieClips.indexOf(mc);
if (i>=0) _movieClips.splice(i,1);
This gets the position of the clicked movie clip inside your array, and if it's a valid one (zero or more) the array is told to remove that element.
You shouldn't be using just _movieClips[1]. That refers specifically to the SECOND object in your _movieClips Array.
You should add your eventListener as soon as the MovieClip is added to the _movieClips Array. You can add it to the most recently '.push'ed MovieClip, like this:
_movieClips[_movieClips.length-1].addEventListener(MouseEvent.CLICK, removePoubelle);
Do that on the next line after the line where MovieClip is pushed into the _movieClips Array.
Your event handler (the removePoubelle function) will be passed a MouseEvent and you can refer to the .target of this event to isolate WHICH MovieClip has been clicked:
private function removePoubelle(e:MouseEvent):void {
var mcToRemove:DisplayObject = e.target;
removeChild(mcToRemove); // note there is no need to refer to .parent as the MovieClip was added in this Class
// more code to come - see below
}
Also note: Because each MovieClip has an eventListener added WHEN IT IS CREATED, e.target will ALWAYS refer to whichever MovieClip was clicked.
The only other thing you may want to implement is removing the MovieClip from the _movieClips Array. This can be done in the removePoubelle function too:
var removalIndex:int = _movieClips.indexOf(MovieClip(e.target)); // here I am 'casting' the e.target to the type MovieClip. That basically just means I'm changing it's type from DisplayObject to MovieClip (which is a subclass of DisplayObject)
if (removalIndex>-1) {
_movieClips.splice(removalIndex, 1); // this line removes one item at the index returned from the _movieClips.indexOf... line above.
}
Let me know if any of this doesn't make sense.
}

The most lightweight button implementation in actionscript 3.0 [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
Since button is one of the most popular GUI components this question becomes hot when we talk about memory usage. Especially when you have tons of buttons in your application.
So how you can implement a button that uses minimum CPU and memory resources and yes, acts like normal button with mouse up, down and hand pointer behavior implemented. Label text is also required.
If you want to have tonnes of buttons with text and graphics, using the least amount of RAM and processing power, you should be using Bitmaps.
This gets a lot more complicated and involves your own preparation of the following:
A font in the form of a sprite sheet.
Classes that manage rendering text onto a Bitmap using that sprite sheet.
Bitmaps don't respond to MouseEvents, so you'll need to architect your own system for managing mouse input on the Bitmaps.
Firstly, lets take a look at the base memory consumption for some of the DisplayObjects that you would think we would be best using. This is our testing method:
function ram(type:Class):void
{
trace(getSize(new type()));
}
And this is the test:
ram(Sprite); // 408
ram(Shape); // 236
ram(TextField); // 1316
In your case, drawing 1000 buttons would result in over 1,724,000 bytes of memory being used.
Now let's look at what we will be using:
1x Bitmap acting as a Canvas that holds all buttons: 236 bytes.
1x BitmapData representing the initial state of every button.
1x BitmapData representing the rollover state of every button.
1x BitmapData storing our text as a sprite sheet for use by all buttons.
Each BitmapData will be quite large in memory consumption, and varies greatly depending on its content. But the trick here is that we only use one and refer to its content for every button that we want to draw.
I've set up a small amount of code to get you started. You still need to implement a click manager which loops over all the buttons and works out which is most relevant to trigger a click, as well as rendering the text on the buttons.
Here's the Button class:
public class BitmapButton
{
private var _text:String;
private var _position:Point = new Point();
public function BitmapButton(text:String)
{
_text = text;
}
public function render(canvas:BitmapData, font:BitmapData, state:BitmapData):void
{
canvas.copyPixels(state, state.rect, _position);
// Use font argument to render text.
// For you to implement.
}
public function get position():Point{ return _position; }
}
And here's the class that will manage rendering those buttons:
public class ButtonCanvas extends Bitmap
{
private var _fontSprite:BitmapData;
private var _baseState:BitmapData = new BitmapData(100, 30, false, 0xFF0000);
private var _overState:BitmapData = new BitmapData(100, 30, false, 0x00FF00);
private var _buttons:Vector.<BitmapButton> = new <BitmapButton>[];
private var _checkRect:Rectangle = new Rectangle();
public function ButtonCanvas(width:int, height:int)
{
bitmapData = new BitmapData(width, height, true, 0x00000000);
// Replace with actual loaded sprite sheet.
_fontSprite = new BitmapData(1, 1);
}
public function add(button:BitmapButton):void
{
_buttons.push(button);
}
public function render():void
{
if(stage === null) return;
bitmapData.lock();
for each(var i:BitmapButton in _buttons)
{
_checkRect.x = i.position.x;
_checkRect.y = i.position.y;
_checkRect.width = _baseState.width;
_checkRect.height = _baseState.height;
if(_checkRect.contains(mouseX, mouseY))
{
// Use roll over style.
// Need to implement depth check so you can't roll over buttons
// that fall behind others.
i.render(bitmapData, _fontSprite, _overState);
}
else
{
i.render(bitmapData, _fontSprite, _baseState);
}
}
bitmapData.unlock();
}
public function get buttons():Vector.<BitmapButton>{ return _buttons; }
}
And a small test:
var canvas:ButtonCanvas = new ButtonCanvas(stage.stageWidth, stage.stageHeight);
addChild(canvas);
for(var i:int = 0; i < 20; i++)
{
var button:BitmapButton = new BitmapButton("Hello");
button.position.x = Math.random() * stage.stageWidth;
button.position.y = Math.random() * stage.stageHeight;
canvas.add(button);
}
stage.addEventListener(MouseEvent.MOUSE_MOVE, update);
function update(e:MouseEvent):void
{
canvas.render();
}
canvas.render();
Now that you've read all of that, I'll point out that it's really unlikely you need to anywhere near this extreme, unless you have some type of game that revolves around buttons and buttons are actually particles that get generated every frame in the 100's. Using a standard Sprite + TextField is perfectly fine in almost all cases.
One of the traditional patterns is using Sprite + TextField
Adobe recommends using Shape instead of Sprite (when it makes sense):
a Sprite object is a display object container, whereas a Shape object is not. For this reason, Shape objects consume less memory than Sprite objects that contain the same graphics.
It would be great to use Shape, and we can do it, but we can not add TextField on it.
Now lets look at TextField inheritance chain:
TextField: InteractiveObject -> DisplayObject -> EventDispatcher -> Object
We can observe that a TextField object is much lighter than a Sprite object - wrong. Using only TextField will be lighter than using TextField + Sprite. I came up with this decision:
import flash.events.MouseEvent;
import flash.filters.BevelFilter;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.Mouse;
import flash.ui.MouseCursor;
public class Button extends TextField
{
private static const MOUSE_UP:Array =
[new BevelFilter(2, 45, 0xEEEEEE, .7, 0x444444, .7, 1, 1)];
private static const MOUSE_DOWN:Array =
[new BevelFilter(2, 225, 0xEEEEEE, .7, 0x444444, .7, 1, 1)];
private static const TEXT_FORMAT:TextFormat =
new TextFormat('Verdana', 12, 0xDDDDDD,
null, null, null, null, null, 'center');
public function Button(label:String, color:int = 0x166488)
{
width = 80;
height = 20;
background = true;
backgroundColor = color;
selectable = false;
defaultTextFormat = TEXT_FORMAT;
text = label;
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
addEventListener(MouseEvent.ROLL_OVER, onMouseRollOver);
addEventListener(MouseEvent.ROLL_OUT, onMouseRollOut);
onMouseUp();
}
private function onMouseRollOut(e:MouseEvent):void
{
Mouse.cursor = MouseCursor.AUTO;
}
private function onMouseRollOver(e:MouseEvent):void
{
Mouse.cursor = MouseCursor.BUTTON;
}
private function onMouseDown(e:MouseEvent):void
{
filters = MOUSE_DOWN;
}
private function onMouseUp(e:MouseEvent = null):void
{
filters = MOUSE_UP;
}
//kill method
}
This code draws nice lightweight button BUT I can not adjust vertical position of a text label so height of this button depends of font-size. Another issue is that I cann't move the text label a bit down-right when somebody clicks it.
Any ideas will be appreciated.
We can observe that a TextField object is much lighter than a Sprite object. That's completely incorrect. A Sprite uses 408 bytes in memory whereas a TextField uses 1316
Yes TextField will consume way more memory.
I would create the label text in a graphics program and create a sprite menu class.
TextField is not really lightweight but a pretty powerful class. If you want user input then TextField is the way to go.
Avoid any of the buttons built into the Flash library and just start simple and build functionality on the sprite class.
If you really want to optimize your interface, reduce the event handlers and any sort of transparency. This might just be good Flash advice in general but often overlooked.
Make a function that gets called every frame, tick();, think();, update(); something like this. Add a single event handler to the main class and within that call your update() function within the menu elements.
Adding a dozen event handlers to your menu elements is not only cumbersome but unsightly.
I would venture to say a sprite with the buttonMode property set to true for the "hand pointer" and then functions to handle the ROLL_OVER and ROLL_OUT MouseEvents.

ActionScripting Issue: adding/removing children

I have three movie clips all linked to the stage and I want them to behave like a button/ But I am not using a button because I have not found a way to have each part (up, over, down, hit) be animated and not just change when the mouse is in use with it. So far I have been able to have all three appear on my stage and show when I have the mouse over and as well when I click, but I think I'm doing something wrong with removeChild. Each MC should appear one at a time and now all three show up when I hover over and seem to "flash". Here's my code:
var mainMoon:swayingMoon = new swayingMoon();
mainMoon.x = 50;
mainMoon.y = 10;
addChild(mainMoon);
var hoverMoon:glowMoon = new glowMoon();
hoverMoon.x = 50;
hoverMoon.y = 10;
var movieMoon:clickedMoon = new clickedMoon();
movieMoon.x = 50;
movieMoon.y = 10;
mainMoon.addEventListener(MouseEvent.ROLL_OVER, showHoverMoon);
mainMoon.addEventListener(MouseEvent.ROLL_OUT, hideHoverMoon);
hoverMoon.addEventListener(MouseEvent.CLICK, startMovieMoon)
function showHoverMoon(event:MouseEvent):void
{
addChild(hoverMoon);
}
function hideHoverMoon(event:MouseEvent):void
{
removeChild(hoverMoon)
}
function startMovieMoon(event:MouseEvent):void
{
addChild(movieMoon);
}
I don't recommend doing it this way as it can make things needlessly complex. For a single button now, you have 3 times as many movie clips/sprites, add/remove child event handlers, and other variables to check/debug for. Multiply this by however many buttons you have.
Instead, I'd recommend using or extending the SimpleButton{} class or writing your own class to encapsulate the behaviour.
SimpleButton class: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/SimpleButton.html
I think you should encapsulate your three moon states into a separate MovieClip that will control all the moon phase changing by itself. If it's already so, fine. The general principle with such a MovieClip-Button type objects is that the listener is assigned to the parent instance, not the parts of that button.
public class Moon extends Sprite {
private var upState:swayingMoon=new swayingMoon();
private var overState:glowMoon=new glowMoon();
private var downState:clickedMoon=new clickedMoon();
private var areWeClicked:Boolean;
private var areWeOver:Boolean;
public function Moon() {
areWeClicked=false;
areWeOver=false;
addChild(upState);
addEventListener(MouseEvent.ROLL_OVER, showHoverMoon);
addEventListener(MouseEvent.ROLL_OUT, hideHoverMoon);
addEventListener(MouseEvent.CLICK, showClickedMoon);
addEventListener(Event.COMPLETE, hideClickedMoon);
}
private function showHoverMoon(e:MouseEvent):void {
areWeOver=true;
if (areWeClicked) return;
removeChild(upState);
addChild(overState);
}
private function hideHoverMoon(e:MouseEvent):void {
areWeOver=false;
if (areWeClicked) return;
removeChild(overState);
addChild(upState);
}
private function showClickedMoon(e:MouseEvent):void {
if (areWeClicked) {
downState.gotoAndPlay(1);
return;
}
if (overState.parent) removeChild(overState); else removeChild(upState);
addChild(downState);
downState.gotoAndPlay(1); // your clicked moon seems to be a playing MC, so starting it over
areWeClicked=true;
}
private function hideClickedMoon(e:Event):void {
if (e.target!=downState) return; // not our event
if (!areWeClicked) return;
areWeClicked=false;
removeChild(downState);
if (areWeOver) addChild(overState); else addChild(upState);
}
}
Now, your parent class is controlling what happens and when. I was under assumption that your clickedMoon MC will only play once fully, then dispatch an event Event.COMPLETE to itself, so that its parent will get notified and will act.
Originally, your event listener structure prevented you from hiding mainMoon MC, otherwise your other listener will never act, now you are using parent object to listen to events, and can safely remove parts of your moon.
Just for a custom button you are going too much complex way. one movieClip is enough for create the button. First create a movieClip and inside that movieClip's timeline create two more frame for 'hover' and click effect. here is little bit of code to start.
var myButton_btn:CustomButton = new CustomButton();
addChild(myButton_btn);
myButton_btn.x = 100;
myButton_btn.y = 100;
myButton_btn.addEventListener(MouseEvent.ROLL_OVER, onOver);
myButton_btn.addEventListener(MouseEvent.ROLL_OUT, onOut);
myButton_btn.addEventListener(MouseEvent.CLICK, onClick);
function onOver(event:MouseEvent):void {
//trace('over');
event.target.gotoAndStop('hover');
}
function onOut(event:MouseEvent):void {
//trace('normal');
event.target.gotoAndStop('normal');
}
function onClick(event:MouseEvent):void {
//trace('click');
event.target.gotoAndStop('click');
}

How Do I Reference a Dynamically Instantiated Object in AS3? (Added a Moviclip to the Stage)

This is something that has been bugging me since I took up AS3 a year ago.
Example. I make a class that extends a Movie Clip and call it "LoaderBar" all it is is some text that says "Loading" and below it another movie clip that is a plain rectangle that I call "lBar"
When I call a function to load my image I add the Loader to the stage as such..
function imageLoader(URL:String):void
{
var loader:Loader = new Loader(new URLRequest(URL));
loader.contentLoaderInfo.addEventListner(ProgressEvent.PROGRESS, progressHandler);
var loadBar:Loader Bar = new LoaderBar();
addChild(loadBar);
}
function progressHandler(e:Event):void
{
var pcent:Number = e.getBytesLoaded / e.getBytesTotal;
// HERE IS WHERE I'D LIKE TO MAKE DIRECT REFERENCE TO MY LOADBAR;
loadBar.lBar.width = pcent*100;
}
Essentially I just want to tell the lBar in the loadBar Movie Clip to be the width of the percent *100. (so that when the clip is loaded the loader bar is 100 pixels wide).
My problem is this. When I add the loadBar to the stage inside of a function, I cannot make reference to it inside of another function without doing some hack making a global variable outside of my function like...
var loadBarClip:MovieClip;
and inside the load function assigning the loadBar to the loadBarclip as such
loadBarClip = loadBar.
I feel like this is redundant. Does anyone know of anyway of accessing my loadBar without making a reference variable?
If it's just for that handler, you could make the handler anonymous and keep in inside the current scope.
var loadBar = new LoaderBar();
var loader:Loader = new Loader(new URLRequest(URL));
loader.contentLoaderInfo.addEventListner(
ProgressEvent.PROGRESS, function (e:Event):void {
var pcent:Number = e.getBytesLoaded / e.getBytesTotal;
loadBar.lBar.width = pcent*100; //Here you are making a direct reference.
}
);
If you really want to encapsulate your scopes you could use closures:
returnFromEncapulatingClosure = function(){
var loadBar = new LoaderBar();
var loader:Loader = new Loader(new URLRequest(URL));
return {
loadBar: loadBar,
loader: loader
};
}();
That allows you to bundle together some references so they won't clobber other parts of code, and you could refer to it with:
returnFromEncapulatingClosure.loader.contentLoaderInfo.addEventListner(ProgressEvent.PROGRESS, progressHandler);
function progressHandler(e:Event):void {
var pcent:Number = e.getBytesLoaded / e.getBytesTotal;
returnFromEncapulatingClosure.loadBar.lBar.width = pcent*100;
}
As a footnote, when you extend the Movie Clip, add a method that sets the lBar.width. Something like:
loadbar.setLBarWidth = function (w:number) {
this.lBar.width = w;
}
I don't see a major problem in having the variable declared outside of the imageLoader function. If you were writing this in a class instead of the timeline then it would just be a class member variable and there is nothing wrong with that. They exist for this very reason.
If your deadset wanting to keep the loadBar variable local then you could always do this:
in the imageLoader function:
var loadBar:Loader Bar = new LoaderBar();
loadBar.name = "loadBar";
addChild(loadBar);
in the progressHandler function:
getChildByName("loadBar");
Since lBar (or loadBar for that matter) is an element that you need to manage at a class level, you should indeed make it a class member. There is nothing wrong with it ;)