AS3 + Starling - Find Unknown Sprite - actionscript-3

I have legacy code project. While Starling is running sometimes some Sprite appears and covers all application.
In pure flash I used "console" https://code.google.com/p/flash-console/wiki/GettingStarted to get object hierarhy in display tree. But it doesn`t work for Starling.
My idea is to add some listener to root, cause this Sprite is in display list tree.
And find who is this Spite's parent.
Is it possible?

And find who is this Spite's parent. Is it possible?
If you are trying to find a display object, and you have no idea where it is coming from, one thing you could try doing is in a frame event, keep track of all the new sprites that are added to the display list. Then you can work backwards from there. Eg.
// keep track of unique display objects
private var _displayList:Array = [];
public function Main()
{
addEventListener( Event.ENTER_FRAME, trackDisplayList );
}
private function trackDisplayList( event:Event ):void
{
trackAsset( stage );
}
private function trackAsset( asset:* ):void
{
var i:int = -1;
while( ++i < asset.numChildren )
{
var child:* = asset.getChildAt( i );
if ( _displayList.indexOf( child ) == -1 )
{
_displayList.push( child );
trace( "tracking display object: " + child.name );
}
if ( child.numChildren > 0 )
{
trackAsset( child );
}
}
}
Hopefully you don't have a boat load of sprites to sort through! This is one way you could recursively check all your unique display objects.

Related

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 transition between game and menu without using frames in as3?

I have simplified this code for easier reading
I have one class called LevelSelect,
one class called Game,
and one class called NavButtons.
*/
The code below i have added levelSelect MovieClip to the stage and
added level numbers to level boxes
onclick it goes to my selected level.
public class LevelSelectPage extends Game
{
public var levelSelectScreen:level_selection;
public var list:Array;
// add movieClips to L1,L2 etc...
public static var Level:String = "No Level Selected";
public static var shared:SharedObject;
public function LevelSelectPage( )
{
setUpLevelSelect();
}
public function setUpLevelSelect():void
{
shared = SharedObject.getLocal("savegame");
shared.clear(); // remove when game completed
if (shared.data.level_passed == undefined)
{
shared.data.level_passed = 3;
}
if (shared.data.playing_level == undefined)
{
shared.data.playing_level = "L0";
}
else
{
shared.data.playing_level = "L0";
}
levelSelectScreen = new level_selection();
addChild(levelSelectScreen);
AddMovieClipsForLevels();
}
public function AddMovieClipsForLevels():void
{
// array of movieClip instances... level pictures.
list = [
levelSelectScreen.L1,
levelSelectScreen.L2,
levelSelectScreen.L3
];
list.forEach(setupSquare);
}
public function setupSquare(square:MovieClip, index:int, array:Array):void
{
// add numbers on top of level images
// get numbers of the levels... as index starts from 0, add 1
var LevelNumber:Number = index + 1;
// convert number to string
var imageLevelNumber:String = LevelNumber.toString();
// set textfield // get childs instance name
var insertlevelNumber:TextField
= square.getChildByName("levelNumberText") as TextField;
// output text
insertlevelNumber.text = imageLevelNumber;
}
public function onSquareClick(me:MouseEvent):void
{
switch(me.currentTarget.name)
{
case "L1": // trace("level 1 was selected");
// startGame();
break;
}
}
}
}
public class Game
{
// imagen a blank screen, it doesn't matter
}
The code bellow - i have added the nav buttons to the game stage, and on click of the back button it outputs a trace statment
but how do i run the LevelSelectPage again instead ?
public class NavButtons
{
private var m_NavDisplayIcon:ReferenceArray;
private var m_stage:Stage;
public function NavButtons( stage:Stage )
{
// add navigation to stage
m_stage = stage;
m_NavDisplayIcon = new ReferenceArray( navDisplayFla );
var icon:navDisplayFla = new navDisplayFla( );
icon.x = 5;
icon.y = 5;
m_NavDisplayIcon.Add( icon );
m_stage.addChild( icon );
// addEventlisteners for backButton, pause game and information
icon.backIcon.addEventListener(MouseEvent.CLICK, goBackToLevelSelectPage);
icon.pauseIcon.addEventListener(MouseEvent.CLICK, pauseGame);
icon.informationIcon.addEventListener(MouseEvent.CLICK, showInformation);
}
public function goBackToLevelSelectPage( event:MouseEvent ):void
{
//var levelSelectScreen:level_selection = new LevelSelectPage();
//m_stage.addChild(levelSelectScreen);
trace("you pressed the backButton");
}
Think of the stage as a blank piece of paper onto which you can stick post-it notes. So when you begin the game, your piece of paper is blank, and you add a post-it note onto this blank "stage" that contains your level selectscreen.
You do this by adding movieclips or sprites to the stage, or better yet add one container to the stage to which you will then add all additional elements (this will make it easier later because you can remove the container and everything in it will be removed as well in one go).
Now, when the player clicks on a button on your level select screen, what you need to do is:
Remove the current post-it note that's on the paper (i.e, remove the container that has your level select screen).
Place a new post-it note that contains the level the player clicked on (i.e, now you add another container to the stage, and add all the elements of the new level into it.)
When the user wants to go back to the level select screen, all you do is again remove the post-it note that is on the paper currently, and add the post-it note that contains the level select screen.

AS3 removing all children from array in a class from parent class

In the Level 1 class (Parent) I generate citizens as seperate objects (c) to walk from left or right across the stage. all of these get added to an array called citizens:
if (citizens.length < 10)
{
// create citizen
var c:Citizen = new Citizen(side,speed,yPos);
addChildAt(c, 2);
citizens.push(c);
}
I want to remove each instance of the class and also remove the event listener that is attached to it in the class:
this.addEventListener(Event.ENTER_FRAME,moveCitizen);
Would I use a for each then splice from the array? E.G
for each (c in citizens) {
removeEventListener(Event.ENTER_FRAME,moveCitizen);
splice();
}
You could do something similar to the following:
// Creation
if (citizens.length < 10) {
// create citizen
var c:Citizen = new Citizen( side, speed, yPos );
addChildAt( c, 2 );
citizens.push( c );
}
// Removal
for( var i:int = 0; i < citizens.length; i++ ) {
var c:Citizen = citizens[ i ].pop();
removeChild( c )
c.cleanUp();
}
// In Class Citizen
public function cleanUp():void {
removeEventListener( Event.ENTER_FRAME, moveCitizen );
}
#user1878381 you have to make one method in citizens class if you needed to call in every object say reset(). when you removed a object you must call reset function of that object which reset the citizes by removing all eventlistner and reset all property of citizens in that object.
you can use array.pop() method to popout a latest pushed object from array by for each looping. if you are using array then array have many functionality like shift, pop, splice(i,1). etc...
i think for each is the best one among all..

adding Bitmaps to various frames in movieClip in as3

I have a movieClip and I want attach to this movieClip bitmaps. I want attach each bitmap to different frame of movieClip. I have tried something like this, but it is not working. It is for memory game I am creating.
for(var i : int = 0; i < cardList.length; i++){
var helpVar : int = cardList[i].pictureOfCard;
cardList[i].gotoAndStop(cardList[i].pictureOfCard+2);
var bitmap : Bitmap = new Bitmap(bitMapArray[helpVar].bitmapData.clone());
cardList[i].addChild(bitmap);
cardList[i].gotoAndStop(1);
}
You could simply load all Bitmaps and only show the one, that should be visible right now. e.g.
function ShowFrame(nr:int):void{
for(i:int = 0; i<bitMapArray.length; i++){
bitMapArray[i].visible = false;
}
bitMapArray[nr].visible = true
}
My AS3 skills are rusty, so this might need some syntax correction, but it works in theory.
var i :int = 0;
processNext();
function processNext():void
{
cardList[i].addEventListener(Event.FRAME_CONSTRUCTED, onFrameConstructed );
cardList[i].gotoAndStop(cardList[i].pictureOfCard+2);
}
function onFrameConstructed( e:Event ):void
{
cardList[i].removeEventListener(Event.FRAME_CONSTRUCTED, onFrameConstructed );
var helpVar : int = cardList[i].pictureOfCard;
var bitmap : Bitmap = new Bitmap(bitMapArray[helpVar].bitmapData.clone());
cardList[i].addChild(bitmap);
if( i < cardList.length - 1 )
{
i++;
processNext();
{
else
trace("All done");
}
After some time I discover that what I was trying to achieve is probably not possible in as3. The way I solved my problem is that everytime i gotoAndStop to frame I need have there bitmap I addChild(bitmap) and everytime I gotoAndStop other frame where there should not be bitmap I removeChild(bitmap) from movieClip. (So it is very similar aproach to makeing bitmap visible and invisible)

How do I access all of the children of a DisplayObject programatically?

How do access all of the children of a DisplayObject using code? (I'm looking for something like movieclip.children)
I'm using this in two cases:
1) To loop through and reposition all of the children of an enclosing MovieClip.
Or
2) To loop through and delete all of the children of a MovieClip
Also, this is a Flash CS5 project.
This loop will touch every child inside movieclip foo. I'm not sure what you're going to do to them, but you can run whatever methods you need inside the loop.
for (var i:uint=0; i<foo.numChildren;i++){
foo.getChildAt(i).whateverMethodYouNeed();
}
Is your object just a display object? If it is an UIComponent, you could use getChildAt() and getChildByName() along with the numChildren property to loop over them. Is this part of a flex project or an actionscript only project?
A DisplayObject itself does not have the mechanisms to describe its children. The lowest level type that knows about children is the DisplayObjectContainer. You may have to convert the object into at least a DisplayObjectContainer to be able to do what you want. I would go with an UIComponent though if you have the flex framework to work with.
DisplayObject
DisplayObjectContainer
UIComponent
If you need to access all the children, inclusive of a child's children, you can try
this:
function doWhatever( mc:DisplayOjectContainer ):void
{
if( mc.numChildren > 0 )
for( var i:int ; i < mc.numChildren ; ++i )
{
//if you need to reposition
//set the points properties here
var point:Point = new Point( _x , _y );
setPosition ( mc.getChildAt(i ) , point );
//if you need to remove all children
//do it recursively
//remove( mc , mc.getChildAt( i );
}
}
function setPosition(mc:DisplayObject , point:Point ):void
{
mc.x = point.x ;
mc.y = point.y;
}
function remove(container:DisplayObjectContainer , child:DisplayObject ):void
{
//this will remove all children before being removed
if( child is DisplayObjectContainer )
{
var doc:DisplayObjectContainer = child as DisplayObjectContainer;
doWhatever( doc );
}
container.removeChild( child );
child = null;
}