How to call on a variable from parent? AS3 - actionscript-3

Let's say I have a variable in the actionscript panel on a movieclip
parent_mc:
var ispaused:Boolean = false;
inside this movieclip is another movieclip with its own actionscript
child_mc:
if (!ispaused)
{ gotoAndPlay(1); }
How do I call for a variable from the parent when dealing the actionscript inside a child movieclip?

Ahem...
if (parent.ispaused) // ...
And if this does not work due to compile time type casting:
if (parent["ispaused"]) // ...
Or you can typecast:
if ((parent as ParentClass).ispaused) // ...

I have actually found that this is the proper way to call upon a variable from the parent, quite literally MovieClip(root) is not to change.
if ((MovieClip(root).entervariablename))
{ dothisaction; }
or to modify the variable value
if (thistrigger)
{ (MovieClip(root).entervariablename) = desiredvalue; }

On the parent MovieClip Timeline (I suppose that Your code is placed on it) :
var isPaused:Boolean=false;
function doSometing():void{
trace("function doSomething called in parent MovieClip");
}
On the child MovieClip Timeline:
import flash.display.MovieClip;
var parentClip:MovieClip = (parent as MovieClip);
var parentPaused:Boolean = parentClip.isPaused;
if (!parentPaused){
trace("parent clip isPaused = " + parentPaused);
parentClip.doSometing();
// Do what you want here
}
Output:
parent clip isPaused = false
function doSomething called in parent MovieClip

Related

How to reference many objects at once in Actionscript?

So, the question is as follows:
I have a set of objects and I want a function to use all those elements of a set in a function. What can I do?
For example:
I have many instances of the same MovieClip. I want the function NextFrame() to be executed only for the MovieClips whose name matches the keyCode of a key I've just pressed.
How can I reference all these symbols at once in an "if" statement?
You'll need to use a loop.
Assuming your clips are just placed on the timeline there are few ways you could do this.
Walk the display list and search for all clips that meet your criteria.
This way can be expensive if you have tons of clips on the stage.
var i:int=this.numChildren;
while(i--){
if(this.getChildAt(i).name == nameCriteria){
//do something here, it matches
}
}
Keep your movie clips of that class in an array, and loop through that array:
var myArray:Array = [mc1,mc2,mc3,mc4];//whatever your movie clips instance names are...
for each(var clip:MovieClip in myArray){
if(clip.name == nameCriteria){
//do something
}
}
Create a custom class that automatically adds any instance of this Movie Clip to an array you loop through.
Save the following code in a file called MyKeycodeClip.as (or whatever you'd like to call it) next to your .fla file.
package {
import flash.display.MovieClip;
import flash.events.Event;
public class MyKeycodeClip extends MovieClip {
public static var allClips:Array = new Array();
public function MyKeycodeClip(){
this.addEventListener(Event.ADDED_TO_STAGE, addedToStage, false,0,true);
this.addEventListener(Event.REMOVED_FROM_STAGE, removedFromStage,false,0,true);
}
private function addedToStage(e:Event):void {
allClips.push(this);
}
private function removedFromStage(e:Event):void {
for(var i:int=0; i < allClips.length;i++){
if(allClips[i] == this){
allClips.splice(i,1);
return;
}
}
}
}
}
Then you can attach that class to your library object. Go to your library objects properties, and choose "Export For Actionscript", in the class input box, put: MyKeycodeClip.
Now whenever you drop that library object on the stage, the code from that file will be attached to it (which automatically adds/removes it from an array). You could loop through each item like so:
for each(var clip:MovieClip in MyKeycodeClip.allClips){
if(clip.name == nameCriteria){
//do something
}
}

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.
}

want to change the code actionscript2 to actionscript3?

i am newbie to flash.i need to change the below actionscript code to actionscript 3.0 code.
i am currently working on drag and drop. so i want to duplicate the movieclip while dragging i found the code on internet but it is actionscript 2.0 so please convert it to as3. the box is a instance name of a movieclip.
the code blocks are:
var num:Number = 0
box.onPress = function(){
num++
duplicateMovieClip(box ,"box"+num, _root.getNextHighestDepth())
_root["box"+num].startDrag();
}
box.onReleaseOutside = function(){
trace(_root["box"+num])
stopDrag();
}
If you dont want to use seperate .as file, follow this steps:
1- assign AS linkage to box movieClip (in library panel):
2- Select frame 1 on the timeline, and paste this code in the Actions panel:
var boxes:Array=[];
//var box:Box=new Box();
//addChild(box);
box.addEventListener(MouseEvent.MOUSE_DOWN,generateBox);
function generateBox(e:MouseEvent):void{
var newBox:Box=new Box();
newBox.x = e.target.x;
newBox.y = e.target.y;
newBox.startDrag();
newBox.addEventListener(MouseEvent.MOUSE_UP,stopD);
newBox.addEventListener(MouseEvent.MOUSE_DOWN,startD);
boxes.push(newBox);
addChild(newBox);
}
function startD(e:MouseEvent):void{
e.target.startDrag();
}
function stopD(e:MouseEvent):void{
e.target.stopDrag();
}
Unfortunately, there's no duplicateMovieClip analog in AS3, so you'll have to create a Class for your box movieClip template. Let's say it will be called BoxTemplate. (You can google how to create Classes for your library object). Add a Class with this name and add this code (event subscription in the constructor and a private event listener). You'll get something like this:
package
{
public class BoxTemplate
{
public function BoxTemplate()
{
addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
}
private function onMouseUp(e:MouseEvent):void
{
stopDrag();
}
}
Leave your present instance of this symbol on the stage. This is your code in the frame:
import flash.event.MouseEvent
box.addEventListener(MouseEvent.CLICK, onClick);
function onClick(e:MouseEvent):void
{
var newBox:BoxTemplate = new BoxTemplate();
newBox.x = e.target.x;
newBox.y = e.target.y;
addChild(newBox);
newBox.startDrag();
}
It will allow you to infinitely clone your boxes. Of course, you can add all of them in the array to keep the references.

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

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)

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