Changing levels of nested movieclips in Flash AS 3.0 - actionscript-3

I am working on a zooming square navigation project (visually like Windows Metro, but squares take over the screen when clicked on). I'm having trouble getting the squares to the top layer (setChildIndex). Here is the code for when a square is clicked on:
function zoom(event:MouseEvent):void {
// saves location and size of icons
returnto = event.currentTarget;
returntoX = event.currentTarget.x;
returntoY = event.currentTarget.y;
returntoWidth = event.currentTarget.width;
returntoHeight = event.currentTarget.height;
// turn off button behaviours
event.currentTarget.buttonMode = false;
event.currentTarget.alpha = 1;
event.currentTarget.removeEventListener(MouseEvent.MOUSE_OVER, rollover);
event.currentTarget.removeEventListener(MouseEvent.CLICK, zoom);
event.currentTarget.removeEventListener(MouseEvent.ROLL_OUT, rollout);
// put clicked box on top
setChildIndex(returnto, numChildren-1);
// fade out icon decor
TweenLite.to(returnto.decor, .25, {alpha:0});
// make square take over screen, call finished when done
TweenLite.to(returnto, 1, {x:stage.x,y:stage.y, width:stage.stageWidth, height:stage.stageHeight, ease:Expo.easeInOut, onComplete:finished});
}
the squares are all inside a main movieclip (named navigation) on the stage (so the first square is navigation.sq_1)
I am not a Flash developer, so I'm really struggling with AS3. Hopefully this is enough info for someone to help. Thanks!
S

Just try to add your returnto clip with addChild() method:
// put clicked box on top
addChild(returnto);
Display List's default behavior when addChild() is to put the child on top of all childs. Other children's indexes are recounted.

Related

AS3 - How to use pixel/point detection with mouse event instead of object detection

This seems like it should be so easy that I'm embarrassed to ask, but I just can't get it.
I have a large round MovieClip (being used as a button). This MovieClip contains a PNG with a transparent background inserted into the MovieClip.
Due to its size there are large empty registration areas on the 4 corners (the bounding box).
How can I have the mouse register as being over only the circle pixels and not the blank space (of Alpha channel pixels) in the square boundary box?
Simple sample code:
public function simpleSample () : void
{
mc1.buttonMode = true;
mc1.addEventListener(MouseEvent.CLICK, doStuff);
}
public function doStuff (event:MouseEvent) : void
{
mc2.gotoAndStop(2);
}
Here are 3 different ways to accomplish this.
EDIT Since you've later explained that your button is an image, this first option won't work for you
If the shape flag on hitTestPoint works with your button (eg it's a shape), you can use hitTestPoint inside your mouse click handler to figure out if the click is actually over the object:
public function doStuff(event:MouseEvent){
//only continue if hit test point is true,
//the x and y values are global (not relative to the mc your testing as one might suppose)
//the third parameter should be true, so it takes into account the shape of object and not just it's bounds
if(mc1.hitTestPoint(stage.mouseX, stage.mouseY, true)){
mc2.gotoAndStop(2);
}
}
If the above doesn't work because you have bimtap data in your button, then an easy way to accomplish this is to just add a shape mask to the button.
So, either inside your button using FlasPro, mask everything with a circle shape, or, do it via code by doing the following when you first show the button:
var s:Shape = new Shape();
s.graphics.beginFill(0);
s.graphics.drawCircle(mc1.x + (mc1.width * .5), mc1.y + (mc1.height * .5), mc1.width / 2);
addChild(s);
mc1.mask = s;
If using an image as the button, or you want to set a threshold of how transparent to consider a click, then you can check the transparency of the pixel under the mouse:
function doStuff(event:MouseEvent){
//only continue if pixel under the mosue is NOT transparent
//first, you need a bitmap to work with
//if you know for sure the position of your bitmap, you can do something like this:
var bm:Bitmap = mc1.getChildAt(0) as Bitmap;
//annoyingly though, FlashPro makes timeline bitmaps shapes,
//so the above won't work UNLESS you take your bitmap in the FlashPro Library
//and export it for actionscript, giving it a class name, then it will be an actual bitmap on the timeline.
//As an alternative, you could (very CPU expensively) draw the whole button as a bitmap
var bmd:BitmapData = new BitmapData(mc1.width,mc1.height,true,0x00000000);
bmd.draw(mc1);
var bm:Bitmap = new Bitmap(bmd);
//we get the 32bit pixel under the mouse point
var pixel:uint = bm.bitmapData.getPixel32(bm.x + event.localX,bm.y + event.localY);
//then we grab just the Alpha part of that pixel ( >> 24 & 0xFF ).
//if the value is 0, it's totally transparent, if it's 255, it's totally opaque.
//for this example, let's say anything greater than 0 is considered good to be a click
if((pixel >> 24 & 0xFF) > 0){
mc2.gotoAndStop(2);
}
}

AS3 parallax effect in Flash banner preventing Movieclip buttons from functioning

I'm building a rich media Flash banner ad. I have 3 to 4 round MovieClip buttons that move around with an AS3 parallax effect. It looks like an EnterFrame loop creates the effect in AS3. But the individual Movieclip buttons won't function when I assign MouseEvent Click events. I want the parallax movement to stop and playhead advance to a specific label on main timeline when the MC's are clicked.
I'm sure this can be done but I lack the expertise. Here is the code:
//add an event listener to trigger every frame
addEventListener(Event.ENTER_FRAME, onFrame);
//set a constant that marks the centre of the stage
//the stage is 600 x 400, so we halve that
const stageCentre:Point=new Point(180,300);
//set an easing constant
const ease:Number=0.2;
function onFrame(e:Event) {
//create a point to store the distances the mouse is from the centre
var mouseOffset:Vector3D=new Vector3D(stageCentre.x-mouseX,stageCentre.y-mouse Y, 0);
//move each background layer by a different percentage of offset
//the easing constant is used here to create smoother results
//foreground moves the most; 75% of the mouse offset
clip1_mc.x+=(stageCentre.x+mouseOffset.x*0.70 - clip1_mc.x)*ease;
clip1_mc.y+=(stageCentre.y+mouseOffset.y*0.50 - clip1_mc.y)*ease;
//mid-ground moves a medium amount; 50% of the mouse offset
clip2_mc.x+=(stageCentre.x+mouseOffset.x*1.00 - clip2_mc.x)*ease;
clip2_mc.y+=(stageCentre.y+mouseOffset.y*1.00 - clip2_mc.y)*ease;
//background moves the least; 25% of mouse offset
clip3_mc.x+=(stageCentre.x+mouseOffset.x*1.75 - clip3_mc.x)*ease;
clip3_mc.y+=(stageCentre.y+mouseOffset.y*1.00 - clip3_mc.y)*ease;
}
//Click on button to go to and Play "kelsey" label (this does NOT work)
clip1_mc.kelsey_btn.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler);
function fl_MouseClickHandler(event:MouseEvent):void
{
MovieClip(root).gotoAndStop("kelsey");
}
Make sure the clip1_mc is on the top most layer, it is probably overlapped by other clips that are catching up the mouse click events.
If you have buttons in all layers you will need to disable mouse events for everything except the buttons. For example, if you have a "button" and a "background" in each movieclip and you want to only keep the buttons clickable, do something like this inside of that movieclip:
background.mouseEnabled = false;
background.mouseChildren = false;
This way the background will not listen for any mouse interactions
add removeEventlistener when the mouse clicks the movieclip
clip1_mc.kelsey_btn.addEventListener(MouseEvent.CLICK, fl_MouseClickHandler);
function fl_MouseClickHandler(event:MouseEvent):void
{
this.removeEventListener(Event.ENTER_FRAME, onFrame);
MovieClip(root).gotoAndStop("kelsey");
}

AS3 Dynamically Load and Autoscroll Random size Movieclips

I'm very new to AS3 & Flash and wonder if anyone can take a look at this test piece.
Within my Flash file I have a number of movieclips that are randomly & dynamically added to the stage when a button is pressed. All the movieclips are the same width but have different heights and all the movieclips have their registration points positioned bottom-left (I'm using the button at this stage for test purposes only).
What I would like to do is 'automatically' (rather than using a button) add an endless stream of randomly chosen mc's from the library to the stage (that is to say mc's should be added in succession - i.e. one after the other without a gap between each one) that auto-scroll vertically downwards from the top of the stage towards the bottom (in the fashion of a never ending conveyor belt) and are then returned to the library when they are no longer visible on stage.
Anyone have any ideas.
//mc's are dynamically loaded & returned to the library
//mc's have 'export for Actionscript' property
//mc's have their anchor point placed bottom left
//stop all
stop();
//Speed of the vertical auto-scroll movement
var scrollSpeed:uint = 1;
//auto load random mc from library & place top left corner of stage
//load random mc via button for test purposes
McButton.addEventListener(MouseEvent.CLICK,attachMovieclip);
function attachMovieclip(event:MouseEvent):void{
//create a random number for choosing a mc from the array
var newNumber:int = (Math.random ()*14)
//define the mc's
var mc1:Red01 = new Red01();
var mc2:Red02 = new Red02();
var mc3:Red03 = new Red03();
var mc4:Orange01 = new Orange01();
var mc5:Orange02 = new Orange02();
var mc6:Orange03 = new Orange03();
var mc7:Yellow01 = new Yellow01();
var mc8:Yellow02 = new Yellow02();
var mc9:Green01 = new Green01();
var mc10:Green02 = new Green02();
var mc11:Blue01 = new Blue01();
var mc12:Blue02 = new Blue02();
var mc13:Purple01 = new Purple01();
var mc14:Purple02 = new Purple02();
//create an array which holds all the mc's
var Mcarray:Array = newArray(mc1,mc2,mc3,mc4,mc5,mc6,mc7,mc8,mc9,mc10,mc11,mc12,mc13,mc14);
//add child (or random mc) to the stage
addChild(Mcarray[newNumber]);
//place mc at specific starting point coordinate - i.e. top of the stage
Mcarray[newNumber].x=0
Mcarray[newNumber].y=0
//trace mc random numeric value for test purposes
trace(newNumber);
//auto-scroll the randomly chosen mc vertically down the stage
stage.addEventListener(Event.ENTER_FRAME, moveScroll);
function moveScroll(e:Event):void{
Mcarray[newNumber].y += scrollSpeed;
//once first mc is completley on stage load the next random mc
//once a mc has completely left the bottom of the stage return it to the library
}
}
Off the top of my head so it may be a bit rough but....
Start with the first clip and store it in an "onscreenClips" array (will be used like a queue):
1.) set the starting clip in "onscreenClips" to y = -height
This would line up the bottom of the clip to the top of the stage.
Then within your enter frame loop:
1.) move any clips in "onscreenClips" down by the speed
2.) check if the first object in "onscreenClips" has reached the bottom yet (the y property would be equal to stage height). If so, remove it from display (as it would be offscreen now) and off the queue. The first object is always the 'oldest' in the queue.
3.) check if the last object in "onscreenClips" has reached the top of the stage yet (y property has reached 0 and is no longer negative). This would mean that the top edge is lined up with the top of the stage, if moved down any more, there would be a gap. If this happens, then add the next clip set at y = -height, then push it on to the queue.
4.) Continue until no more objects to add. Then keep checking the step 2 condition until the "onscreenClips" array/queue is empty.
Managed to progress a little further with this one.
Have added checks to determine when the full height of a mc is visible & 'on stage' and when the full height of a mc is non-visible & 'off stage' and both are functioning correctly.
I've also added a remove child statement, so that when the mc has become non-visible & offstage it is removed from the stage and returned to the library. This works in visual terms (i.e. the mc does disappear from the stage) however, according to the output results the mc (though not visible) still appears to be present & travelling ever further past the visible stage limit.

action script 3 - is it possible to trigger click event only when mouse is clicked on the image part?

I have a problem and I have potential solution. But I wanted to confirm if there is an easy and simple way to solve my problem.
App type:
Isometric Game
Problem statement:
I am loading images in my flash app and have mouse events attached to them.
The images I load are prop images like vehicles, trees, buildings etc., and all of them are transparent.
Example: Red ball asset (please ignore the yellow background which I applied to describe the problem)
If I click on the actual image area (colored in red), then every thing works perfect
I don't want to trigger mouseevent when I click on empty image part (or transparent area, which I have shown in yellow color)
There is one way I know by creating masks in flash. I don't want to do it unless that is the final option left because I load image assets instead of flash assets and I don't want to create a new mask asset for all the assets
There is another method I was going to adopt by using getPixel method of Bitmap. Which is discussed here.
But there is another problem with this method.
I might be able to ignore the click event when I click on the empty part of the asset but if there is some other asset is behind the image in the same location, then I need to process the click event for the occluded image.
Well, thinking of solution to this problem takes me to the getObjectsUnderPoint where I can scan the occluded assets
Well, what you proposed as a solution is 100% valid. Just move the logic of determining what game object is clicked outside of that object.
Listen for MOUSE_DOWN/MOUSE_UP events at container which contains your game objects.
Catch an event
Check if the game object which is the target of this event is transparent at this point using BitmapData.getPixel32
If it is use getObjectsUnderPoint to find out all other game objects at this point
Find in a loop the first object which is not transparent at this point
Now you got the actual object which is hit.
One interesting solution is to use Sprite objects with the individual non-transparent pixels burnt onto them.
Suppose this is your Loader "complete" handler:
private function loaderCompleteHandler(event:Event):void
{
// Loader is not our child, we use a Sprite instead (below).
var loader:Loader = Loader(event.target);
var sprite:Sprite = new Sprite();
addChild(sprite);
var w:Number = loader.content.width;
var h:Number = loader.content.height;
// Use transparent bitmap.
var bitmapData:BitmapData = new BitmapData(w, h, true, 0);
bitmapData.draw(loader.content);
// Now burn the image onto the Sprite object, ignoring
// the transparent pixels.
for (var xPos:int = 0; xPos < w; xPos++) {
for (var yPos:int = 0; yPos < h; yPos++) {
var pixel32:uint = bitmapData.getPixel32(xPos, yPos);
var alpha:int = pixel32 >>> 24;
if (alpha != 0) {
sprite.graphics.beginFill(pixel32 & 0xFFFFFF, alpha / 0xFF);
sprite.graphics.drawRect(xPos, yPos, 1, 1);
sprite.graphics.endFill();
}
}
}
}
Essentially you want "empty" pixels that aren't clickable, and fully transparent pixels aren't quite the same thing. With this solution you get empty pixels.
Only problem is that this might be slow. Give it a shot.

Flash: Stage.FullscreenInteractive -> Difference if class or 1st frame

example 1: 1st frame of my app
var screenBounds = Screen.mainScreen.bounds; //Bounds of current screen
var full:Sprite = new Sprite(); //Sprite Fullscreen
//Enter Fullscreen
function goFullScreen(e:Event = null) {
//myClass.goFullscreen();
full.graphics.clear();
full.graphics.beginFill(0xccff00);
full.graphics.drawRect(0,0,screenBounds.width, screenBounds.height);
full.graphics.endFill();
addChild(full);
this.stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
}
example 2: a normal as class package
private var full:Sprite = new Sprite(); //Sprite to show fullscreen
private var screenBounds = Screen.mainScreen.bounds; //Bounds of current screen
public function favoritesFullscreen():void {
full.graphics.clear();
full.graphics.beginFill(0xccff00);
full.graphics.drawRect(0,0,screenBounds.width, screenBounds.height);
full.graphics.endFill();
addChild(full);
this.stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
}
So, tell me WHERE IS THE DIFFERENCE?
I'm a macuser, you know, at the top the menubar and in my case the dock is aligned left
It's weird but example 1 does exactly what it should. It creates a fullscreen rectangle across the ENTIRE screen (from 0,0 to the right bottom)
However, example 2 kind of calculates the width of the top-menubar and the dock and starts the fullscreen rectangle a approx 40px from the left edge of the screen (dock) and 20px from the top (menubar)... i don't get why a external class acts different than as on the first frame.
??? thank you for your help!
I'm guessing it has to do with when the Screen.mainScreen.bounds are retrieved. On compile time, flash internally moves all frame code within functions in the DocumentClass and calls them after the stage has completly initialized. So, in the 1st example, you are retrieving the screenBounds after everything is initializated, but in the 2nd example you are doing it much earlier.
I guess waiting for the ADDED_TO_STAGE event would be enough. If it's not, the first ENTER_FRAME event dispatched should definitely work.