I have 7 buttons on my stage (buttonA, buttonB, etc) all MC's.
I would like to control the RollOver, RollOut & CLick with AS3.
At first I thought I could just tell my button Listener where to go
i.e. gotoAndStop(2) which is RollOver state.
or gotoAndStop(3) which is Click state.
and gotoAndStop(1) which is RollOut state.
But when I "Click" and then rollout, I need the button to stay "clicked" until some other button is "clicked".
can't seem to figure this out. Any help would be appreciated.
it would be quicker and more efficient if you name your buttons numerically. button1, button2, button2.. this will allow you to write for loops to perform functions easily
// loop through the buttons and give them mouse click listeners
for ( var i:int = 1 ; i <= 7; i++ ){
var curButton:MovieClip = getChildByName ("button"+i);
// set click lisitener
curButton.addEventListener ( MouseEvent.CLICK, buttonClickHandler );
// set rollover listener
curButton.addEventListener ( MouseEvent.ROLLOVER, buttonRollOverHandler );
// set rollout listener
curButton.addEventListener ( MouseEvent.ROLLOUT, buttonRollOutHandler );
// set initial state
curButton.gotoAndStop(1);
}
function resetStates (){
for ( var i = 1; i<=7; i++){
var curButton = getChildByName("button"+i);
curButton.gotoAndStop(1);
}
}
function buttonRollOverHandler ( evt:MouseEvent ){
resetStates();
evt.target.gotoAndStop(2);
}
function buttonRollOutHandler ( evt:MouseEvent ){
resetStates();
}
function buttonClickHandler ( evt:MouseEvent ){
resetStates ();
evt.target.gotoAndStop(3);
}
Hmm...
I would keep the roll over and press functionality within the scope of a button element and do a frame two for the permanent clicked state.
[this is quickly written code, but you'll get the idea]
It seems you'll need have some parent "reset" function and individual click handlers for each button.
buttonA.addEventListener(MouseEvent.MOUSE_DOWN, buttonADown);
function resetButtons(){
buttonA.gotoAndStop(1);
buttonB.gotoAndStop(1);
buttonC.gotoAndStop(1);
buttonD.gotoAndStop(1);
buttonE.gotoAndStop(1);
buttonF.gotoAndStop(1);
buttonG.gotoAndStop(1);
}
function buttonADown(e.Mouse_Event):void{
resetButtons(); //RESET ALL PREVIOUSLY CLICKED BUTTONS
buttonA.gotoAndStop(2);
}
// and so on down the line....
Related
In Adobe Animate (HTML5 Canvas) I need to check if two buttons have been clicked on and advanced to the second frame and if both have then display a movieclip.
I am new to programming and not sure how to even begin. I thought of using eventListner or an if/else statement. Not getting either to work. Any help would be greatly appreciated.
I'll try and answer the question to my knowledge, hopefully, it is simple enough to follow, but I'm sure there are probably better ways to write these functions.
buttonClick(evt) allows the buttons to be toggled on and off, assuming that frame 0 of the movieclips handles the off state of the buttons and frame 1 handles the on state. A function is then called on every click to check the states of the buttons.
checkButtonStates() is a conditional statement that just checks the current frame of the buttons, and if both are at 1 the movieclip is displayed and played.
const button1 = stage.children[0].button1_mc;
const button2 = stage.children[0].button2_mc;
const movieclip3 = stage.children[0].movieclip3_mc;
button1.addEventListener("click", buttonClick);
button2.addeventListener("click", buttonClick);
function buttonClick(evt) {
button = evt.currentTarget;
// check current frame of button and changes frame
if (button.currentFrame == 0){
button.gotoAndStop(1);
} else {
button.gotoAndStop(0);
}
checkButtonStates();
}
function checkButtonStates(){
// if both buttons have been clicked hide buttons and play movie
if (button1.currentFrame == 1 && button2.currentFrame == 1){
showButtons(false);
showAndPlayMovie(true);
}
}
// buttons visible property set by passed parameter
function showButtons(bool){
button1.visible = bool;
button2.visible = bool;
}
// changes visible property of movie by passed parameter. If movie is visible the movie is played.
function showAndPlayMovie(bool){
movieclip3.visible = bool;
if (movieclip3.visible == true){
movieclip3.play();
}
}
tldr:
how does one tell a mouse event to only fire for the youngest child under the mouse, and not its parent?
I'm just making a solitaire game to try to better learn OOP. I have a Card class and a CardHome class. Each Card contains a CardHome which can hold a card, and so on. Thus, moving a card will move all the cards contained in it. The problem is that my mouse event is detecting the parent of all the cards. So what I think I need is a way to say "run this mouse event only for the youngest child of the target". Any way to do that?
private function pressCard(me:MouseEvent):void{
var c:Object = me.target as Card;
// place card in center of container x wise
c.x = 0;
// and down 5
c.y = 5;
// bring drag container up to front z order
addChild(c as Card);
// begin dragging drag container
c.startDrag(true);
// hide mouse
Mouse.hide();
// add listener to this card for mouse up
c.addEventListener(MouseEvent.MOUSE_UP,dropCard);
}
private function dropCard(me:MouseEvent):void{
var c:Object = me.target as Card;
// release drag container
c.stopDrag();
// remove mouse up listener from card
c.removeEventListener(MouseEvent.MOUSE_UP,dropCard);
Mouse.show();
// check for colision with a card that has a matching holder
for (var i:int = 0; i < deckArray.length; i++){
if (c.hitTestObject(deckArray[i]) && c != deckArray[i]){
trace("hit",deckArray[i]._containerOwned._occupied,deckArray[i]._flippable);
if (deckArray[i]._number == c._number + 1 && deckArray[i]._flippable == false && deckArray[i]._suit != c._suit && deckArray[i]._containerOwned._occupied == false){
c._containedIn.parent._containerOwned._occupied = false;
makeFlippable(c._containedIn.parent);
deckArray[i]._containerOwned.addChild(c as Card);
c._containedIn = c.parent;
c.x = 0;
c.y = 0;
break;
}
}
}
// add selected card back to home spot
c._containedIn.addChild(c);
c.x = 0;
c.y = 0;
}
private function mOver(me:MouseEvent):void{
trace(me.target._number);
trace(getHighestZ());
}
private function getHighestZ():Card{
var highestZ:int = 0;
var c:Card;
for (var i:int = 0; i < deckArray.length; i++){
if (deckArray[i].hitTestPoint(stage.mouseX,stage.mouseY) && deckArray[i].getChildIndex() > highestZ){
c = deckArray[i];
highestZ = deckArray[i].getChildIndex();
}
}
return c;
}
Each card is a child of a container in the card above it. I want to be able to select a card and drag it (and its children). The problem is that when I click card B, it selects card E. So what I need is to select the youngest child in the mouse event, and NOT its parent. Any way to control that?
I know I can do mouseChildren true or false. That's fine. It has to be set to true for all of the cards so that I can select them independent of their parent card. Do I need to do something with bubbling my event? That's something I've never understood.
Note: I realize there are problems with mouse mouse over function. Fixing that will be trivial and is kind of a placeholder for now.
tldr:
use a combination of useCapture set to true and the stopPropagation method. This allows a user to click an element and have the eventHandler only called for the target, not any of the elements between the target and the stage!
So the key here is to access the as3 event flow. When a mouse event gets triggered it propagates from the stage up through all objects that can receive that type of mouse event. In this case, the cards parents. Then it bubbles back down to the stage. So what I needed was to set the capture setting to true which switches the phase in which the event handler is used to the bubbling phase (this causes the first element to actually call the function to be the actual target) and then, instead of letting the event propagate back down to the stage, call the stopPropagation method. Looks something like this:
c.addEventListener(MouseEvent.MOUSE_OVER, mOver, true);
The true here says to fire the function on each element on the way back to the stage from the target. Then during the mOver function I do:
stopPropagation();
trace(c._Number);
// and any other code to do on this target
Which keeps the event from flowing back down through the other cards under the mouse.
Now instead of an output like
5
3
9
when the mouse is over the 9 card (which is over a 3 which is over 5) I now get simply
9
and then when I mouse over the 3 I get
3
So now the mouse is calling a function for the target only. Surprisingly non intuitive but allows for some serious flexibility and control!
from Adobe on Event Flow
I try to add a MovieClip to an existent SWF on the fly - inject an small code who do something like:
this.obj = new MovieClip(); // it is inside an object
obj.name = 'FLOOR';
obj.graphics.beginFill(0xFFFFFF, 0);
obj.graphics.drawRect(0,0,self.width, self.height);
obj.graphics.endFill();
obj.buttonMode = true;
self.addChildAt( floorLayerMC , 0); /* self is an reference for the this keyword, reference for the entire swf */
My question is: this SWF has many elements like images and textfields, and some of this elements has no event handler for click. I Need to find a way to "redirect" all of the events to my "FLOOR" element, using something like bubbling the event.
Of course, I can add the FLOOR in top of any elements BUT I have some elements with click handler. I can't ignore all of the elements. So my problem is:
if I click over an MovieClip with click handler, perform the original action.
if I click over an MovieClip without click handler, perform the FLOOR action.
I can't add a event handler in all of the elements.
Any Idea?
Listen for a click on the container movieclip's own stage (the movieclip that contains the FLOOR). In the handler method for the click event, do a hit test using hitTestPoint with the mouseX and MouseY of the container movieclip, and if the mouse is over any clickable objects, ignore the stage click. Store all the objects that are clickable in an array to do that test.
This code is untested but it would go something like this:
var exemptArray:Array = [ btn_mc1, btn_mc2, btn_mc3 ];
containerMC.stage.addEventListener(MouseEvent.CLICK, onClickMyMC);
function onClickMyMC( event:Event ):void
{
for(var i:int = 0; i < exemptArray.length; i++)
{
if( exemptArray[i].hitTestPoint(containerMC.mouseX, containerMC.mouseY) )
{
// do nothing, ignore the stage click ( and let the object with the click respond )
break;
}
else
{
// respond to the stage click
}
}
}
To build the exemptArray without knowing what objects are clickable ahead of time:
( untested but should be close enough to give you an idea ).
var exemptArray:Array = buildExemptArray();
function buildExemptArray():Array
{
var arr:Array = [];
for(var j:int = 0; j < containerMC.numChildren; j++)
{
if( containerMC.getChildAt(i).hasEventListener(MouseEvent.CLICK) )
{
arr.push( containerMC.getChildAt(i) );
}
}
return arr:
}
EDIT TO ANSWER QUESTION IN COMMENTS:
this.addEventListener(MouseEvent.CLICK, onClick) will add a click event to the whole object, children included.
this.stage.addEventListener(MouseEvent.CLICK, onClick) will add a click only to the movieclip's stage, not its children as well.
In as3 all movieclips have a stage property. If you wrote on the main timeline this.stage.addEventListener(MouseEvent.CLICK, onClick); that would be adding a stage click to the whole swf. But, if you wrote something like myMC.stage.addEventListener(MouseEvent.CLICK, onClick); it would only add a click to that movieclip's stage (myMC's stage). Since stage is below the display list, you can capture a click there in any movieclip. If you don't have access to all the objects that have mouse events ahead of time, you could loop through all the container's children and check if they have a mouseEvent with .hasEventListener(MouseEvent.CLICK);, create your exemptArray from that, then use the same logic above to ignore items in the exemptArray.
On stage, I have a MovieClip named "mc" with a simple rectangle drawn inside. mc also has a Button child named "btn" which is another simple rectangle ( smaller than mc's rectangle obviously). Then I have this code on stage.
function mcDown( _e:MouseEvent):void{
trace( "mc" );
}
function btnClick( _e:MouseEvent):void{
trace( "btn" );
}
mc.addEventListener( MouseEvent.MOUSE_DOWN, mcDown );
mc.btn.addEventListener( MouseEvent.CLICK, btnClick );
The problem I am having is when click the button, mcDown event is also triggered and traces both "mc" and "btn".
How can I make it so that when I click the button, it triggers only btnClick and not mcDown along? I tried MOUSE_UP instead of CLICK, same problem. And mcDown event has to remain MOUSE_DOWN.
There is no way to prevent bubbling except setting the bubbles parameter as false in the dispatchEvent.
dispatchEvent(EVENT_TYPE, BUBBLES,....);
However, you can avoid the bubbling by doing a check. Just have the below line as first line of your listener function it avoids the events dispatched from all objects other then targets.
if(e.eventPhase != EventPhase.AT_TARGET) return;
So, for your sample code, when you click on the button both the events dispatches but in the mcDown function it won't execute after the above said line.
If you add button in a MC, and you click on the button, you also click on the MC, because the part of the MC that is under the button is still there and it runes the function for the whole of the MC, you can't remove it.
So it's good idea to make a function that will check if the button is pressed, otherwise it will run the function for the whole of the MC.
This one should do it.
//add this in you constructor
mc.addEventListener(MouseEvent.MOUSE_DOWN, myReleaseFunc);
function myReleaseFunc(e:MouseEvent):void {
if(e.currentTarget.name == Btn1) //Btn1 is instance name for a button
{
Btn_func1();
}
else if(e.currentTarget.name == Btn2) //Btn2 is another button.
{
Btn_func2();
//For every button you'll need to add another function and if statement to check if that button was clicked.
}
else
{
Mc_func();
}
}
// this outside the main class
function Mc_func():void{
//you code here
}
function Btn_func1():void{
//you code here
}
function Btn_func2():void{
//you code here
}
I think that this way is much more effiecient and it will works better and faster and you'll have a lot smaller chance of overloading the system.
I am trying to remove an eventlisnter on a button so when the button is pressed the animation completes before you can press the button again. But based on my code below you can push the button as many times as you like:
var LeftButt:MovieClip = new left_button();
var RightButt:MovieClip = new right_button();
var topClip:Sprite = new Sprite();
addChild(topClip);
LeftButt.addEventListener(MouseEvent.MOUSE_UP, function(e){moveItems(e, "left");});
RightButt.addEventListener(MouseEvent.MOUSE_UP, function(e){moveItems(e, "right");});
function clothingApp(event:MouseEvent):void{
topClip.addChild(RightButt);
topClip.addChild(LeftButt);
}
function moveItems(event:MouseEvent, SlideDirection:String):void{
LeftButt.removeEventListener(MouseEvent.MOUSE_UP, function(e){moveItems(e, "left");});
RightButt.removeEventListener(MouseEvent.MOUSE_UP, function(e){moveItems(e, "right");});
trace(SlideDirection);
}
So technically this code should only run once because I never set up the eventListener again. But you can press the buttons as many times as you like.
If you want to remove event listeners, you can't add them using anonymous functions.
Create a wrapper function with the same functions as your anonymous function, and you'll be fine.
function moveLeft(event:MouseEvent):void
{
moveItems(event, "left");
}
function moveRight(event:MouseEvent):void
{
moveItems(event, "right");
}
LeftButt.addEventListener(MouseEvent.MOUSE_UP, moveLeft);
RightButt.addEventListener(MouseEvent.MOUSE_UP, moveRight);
LeftButt.removeEventListener(MouseEvent.MOUSE_UP, moveLeft);
RightButt.removeEventListener(MouseEvent.MOUSE_UP, moveRight);