Flash AS3 button eventlistener array bug - actionscript-3

this is my first time posting a question here.
I have an array of 12 buttons on a timeline that when first visiting that part of the timeline, get a CLICK eventlistener added to them using a for loop. All of them work perfectly at that point.
When you click one it plays a frame label inside the specific movieClip and reveals a bio on the corresponding person with a close button and removes the CLICK eventlisteners for each button, again using a for loop. The close button plays a closing animation, and then the timeline goes back to the first frame (the one with the 12 buttons on it) and the CLICK eventlisteners are re-added, but now only the first 9 buttons of the array work. There are no output errors and the code to re-add the eventlisteners is exactly the same as the first time that works. I am completely at a loss and am wondering if anyone else has run into this problem.
All of my buttons are named correctly, there are absolutely no output errors (I've used the debug module) and I made sure the array with the buttons in it is outputting all 12 at the moment the close button is clicked to add the eventlisteners back.
for (var q = 0; q < ackBoDBtnArray.length; q++){
contentArea_mc.acknowledgements_mc.BoD_mc[ackBoDBtnArray[q]].addEventListener(MouseEvent.CLICK, showBio);
}
private function showBio(eo:MouseEvent):void {
trace("show the bio");
bodVar = ackBoDBtnArray.getIndex(eo.target.name);
contentArea_mc.acknowledgements_mc.BoD_mc.gotoAndPlay(ackBoDPgArray[bodVar]);
contentArea_mc.acknowledgements_mc.BoD_mc.closeBio_btn.addEventListener(MouseEvent.CLICK, hideBio);
for (var r = 0; r < ackBoDBtnArray.length; r++){
contentArea_mc.acknowledgements_mc.BoD_mc[ackBoDBtnArray[r]].mouseEnabled = false;
contentArea_mc.acknowledgements_mc.BoD_mc[ackBoDBtnArray[r]].removeEventListener(MouseEvent.CLICK, showBio);
}
}
private function hideBio(eo:MouseEvent):void {
trace("hide it!");
contentArea_mc.acknowledgements_mc.BoD_mc.closeBio_btn.removeEventListener(MouseEvent.CLICK, hideBio);
contentArea_mc.acknowledgements_mc.BoD_mc.gotoAndPlay(ackBoDClosePgArray[bodVar]);
for (var s = 0; s < ackBoDBtnArray.length; s++){
trace(ackBoDBtnArray[s]);
contentArea_mc.acknowledgements_mc.BoD_mc[ackBoDBtnArray[s]].mouseEnabled = true;
contentArea_mc.acknowledgements_mc.BoD_mc[ackBoDBtnArray[s]].addEventListener(MouseEvent.CLICK, showBio);
}
Thanks in advance for any help and insight you can provide...I have a slight feeling that its something that may be obvious to another set of eyes...haha.

Something that looks suspicious to me is that you're removing the event listener from closeBio_btn at the start of the hideBio(). Try placing that line after the for loop.
I'm having trouble justifying this hunch, but a couple of wild theories that have crossed my mind are:
This is a race condition. You're removing the listener for the handler from inside the handler itself, and since the handler was added dynamically, the runtime marks the handler for garbage collection. On the next sweep, the handler is deleted — but since gotoAndPlay probably takes a non-significant amount of time, the handler is still in the for loop adding click handlers when it gets killed, and has only added the first 9 at this point.
This is a data-corruption problem. Since the hide button and the array of 12 buttons are at least tangentially related through BoD_mc, the runtime is doing some strange optimization behind the scenes that makes accessing items through Bod_mc[with indexes] unreliable after closeBio_btn has been touched.
This is an interesting problem, I hope you find a solution :)

It seems to fix it I had to just add every btn dynamically instead of just having them on the timeline. Still strange though, thanks everyone

Related

Check the existence of an object instance

I'm surprised I don't know how to do this, but as it turns out I really don't; simply put, I'm trying to make a side-scrolling shooter game, a basic one and in it, I have 50 stars spawned on-screen through a "for" loop upon the game starting. There is a function which does this and a listener is at the beginning. Problem is, when you lose the game and go back to main menu, 50 more stars would be spawned, which isn't what I want. So, I'm trying to make an "if" statement check at the beginning, so that the game checks whether there is an instance/movie clip of the star object/symbol before determining whether the function that spawns stars should be called out with a listener. So, how do I do this? I looked through some other checks and they didn't help as the codes presented were vastly different there and so I'm just getting errors.
Let me know if a better explanation is needed or if you would like to see some of the code. Note that the game overall already has a lot of code, so just giving all of it would probably not be helpful.
I suggest you rethink your approach. You're focusing on whether stars have been instantiated. That's ok but not the most basic way to think about it.
I would do this instead
private function setup():void{
loadLevel(1);
addListeners();
loadMusic();
// etc...
// call all functions that are needed to just get the app up and running
}
private function loadLevel(lev:int):void{
addStars();
// call all functions that are needed each time a new level is loaded
}
private function restartLevel():void{
// logic for restarting level,
// but this *won't* include adding star
// because they are already added
}
There are other ways to do this but this makes more sense to me than your approach. I always break my game functions into smaller bits of logic so they can be reused more easily. Your main workhorse functions should (IMHO) primarily (if not exclusively) just call other functions. Then those functions do the work. By doing it this way, you can make a function like resetLevel by assembling all the smaller functions that apply, while excluding the part about adding stars.
Here's what I did to solve my problem... Here's what I had before:
function startGame():void
{
starsSpawn();
//other code here
}
This is what I changed it to:
starsSpawn();
function startGame():void
{
//other code here
}
when you said existance, so there is a container, i named this container, (which contain stars , and stars was added to it) as starsRoot, which absolutely is a DisplayObject (right?)
now, to checking whole childrens of a DisplayObject, we have to do this :
for (var i:int=0; i<starsRoot.numChildren; i++) {
var child = starsRoot.getChildAt[i];
}
then, how to check if that child is really star!?
as you said
whether there is an instance/movie clip of the star
so your stars's type is MovieClip, and they don't have any identifier (name), so how to find them and make them clear from other existing movieclips. my suggestion :
define a Linkage name for stars from library, thats a Class name and should be started with a capital letter, for example Stars
now, back to the code, this time we can check if child is an instance of Stars
for (var i:int=0; i<starsRoot.numChildren; i++) {
var child = starsRoot.getChildAt[i];
if (child is Stars) {
// test passed, star exist
break;
}
}

Possible to have multiple timelines? (separate part without affecting the keyframes of other layers) AS3

http://i.snag.gy/eu7iz.jpg
So im doing this generator/designer on flash. It has different features on it so the keyframes clashes with other features considering a lot of the action scripts deal with nextframes and gotos. Its getting confusing once i add little features.
Like right now i wanna add next buttons for the design part. I can do it easily with a blank stage, i can easily click next and back, but when applied to my project, its getting a little dizzying.
This is the script for the first frame:
stop();
small.addEventListener(MouseEvent.CLICK,play1);
function play1(event:MouseEvent):void{
gotoAndStop("3");
}
medium.addEventListener(MouseEvent.CLICK,play2);
function play2(event:MouseEvent):void{
gotoAndStop("6");
}
large.addEventListener(MouseEvent.CLICK,play3);
function play3(event:MouseEvent):void{
gotoAndStop("8");
}
item_mc.addEventListener(MouseEvent.MOUSE_DOWN, dragTheObject);
item_mc.addEventListener (MouseEvent.MOUSE_UP, itemRelease);
item_mc1.addEventListener(MouseEvent.MOUSE_DOWN, dragTheObject);
item_mc1.addEventListener (MouseEvent.MOUSE_UP, itemRelease);
item_mc2.addEventListener(MouseEvent.MOUSE_DOWN, dragTheObject);
item_mc2.addEventListener (MouseEvent.MOUSE_UP, itemRelease);
function dragTheObject(event:MouseEvent):void {
var item:MovieClip=MovieClip(event.target);
item.startDrag();
var topPos:uint=this.numChildren-1;
this.setChildIndex(item, topPos);
}
function itemRelease(event:MouseEvent):void {
var thisItem:MovieClip=MovieClip(event.target);
thisItem.stopDrag();
};
This is the fla file: https://www.dropbox.com/s/77euop1luqjreos/FINAL.fla
MovieClips have their own timeline.You may want to modularize your program into Movieclip components and export for Actionscript manipulation that you can instantiate at run time as necessary. Now that is one way to do it to avoid code spread across one single timeline.But If you still want to stick to your way (use of single timeline), You still could achieve your next/previous implementation without affecting frame logic at any rate.A simple way to do this goes like this:
Encapsulate all logic into functions and put these functions solely on frame 1 and nothing else.
This keeps logic clean and separate from the components.Also, the logic layer in principal should not have nothing else.Why on Frame 1?. Well, we want to expose and keep in memory first the logic, so that whatever component related code that follows on the subsequent frames should be aware of the previous logic and hence throw no run time errors when interacting frame 1 logic.
Spread your components and related code across the subsequent frames respectively.
Put only component related code on a frame that has the component in question. registering event listeners could have targets as their dependency. define event listeners and put them on frame 1 as part of the logic and simply put code for registering listeners on the component frames as per demand.
Example:
//On Frame 1
function onAMouseClick(event:Event):void
{
//implement logic
}
function onBMouseClick(event:Event):void
{
//implement logic
}
//Implemented function for next/back buttons
//Also on Frame 1
function navigate(event:Event):void
{
var frame:int;
switch(event.target.name)
{
case "nextBtn":
frame=currenFrame<numFrames?+1:numFrames;
gotoAndStop(frame);
break;
case "backBtn":
frame=currenFrame>2?-1:currentFrame;
gotoAndStop(frame);
break;
}
}
//On Frame 2 for A component
A.addEventListener(MouseEvent.CLICK, onAMouseClick)
//On Frame 3 for B component
B.addEventListener(MouseEvent.CLICK, onAMouseClick)
Put Next/Back button components on a single layer spreading from frame 2 all the way to the end of the last frame where you want the buttons to be visible. Then implement related code having its visibility spanning across between frame 2 and the last frame as the following shows:
//navigate handler is declared and implemented on **frame 1**
nextBtn.addEventListener(MouseEvent.Click, navigate)
nextBtn.addEventListener(MouseEvent.Click, navigate)
Well, that is your way of doing things (Single timeline scripting). Not bad for simple timeline scripting.You may try the other way also of instantiating Movieclips (exported for action scripiting)
at run time and the add these to the display list as per demand as on clicks next/back buttons.In doing so you will not only have one single point of logic but will have MC code encapsulated in each individual component.
Hope the foregoing helps. Thanks.
Do not be scared to change code! Let alone afraid to run into errors! That's way forward you want to learn to fix things that's a beauty of it!
.....cheers!.

changing skins in flash as3

hello im making game and im trying to add skins for layers in this game so i made a vovieclip
and inside it there are skins http://prntscr.com/2as4mp and inside that when it be clicked there are http://prntscr.com/2as4pm i want it that when i ress this button the skin will change in the menu i tried this
select_2.addEventListener(MouseEvent.CLICK, open1243)
function open1243(event:MouseEvent){
bk = 1
gotoAndStop(2)
}
stop ();
well that dint work um where there is goandsto 2 that goes to menu thats it nothing works pls help
In your event listener, you're not specifying that you want select_2 to gotoAndStop, rather, it will apply that method call to whatever this is in the scope of that method. I'm guessing, at that point, it's the stage.
select_2.addEventListener(MouseEvent.CLICK, open1243);
function open1243(event:MouseEvent){
bk = 1
select_2.gotoAndStop(2);
}
You should probably make sure your event listener could be used for any object similar to select_2, in which case, I would suggest you change that line of code to:
MovieClip(event.target).gotoAndStop(2)

Applying action to a button in one frame makes it apply to whole movieclip in Flash CS6

I have a movieclip with several layers and frames. Some of those contain buttons. When I click button 1 I want it to go to the next frame. When I click button 2 I want it to go to frame 23. This is the code I'm using for button 1:
this.addEventListener(MouseEvent.CLICK, convnext);
function convnext(evt:MouseEvent):void
{
MovieClip(parent).gesprekkencivcen.nextFrame();
}
and this is the code for button 2:
this.addEventListener(MouseEvent.CLICK, convend1);
function convend1(evt:MouseEvent):void
{
MovieClip(parent).gesprekkencivcen.gotoAndStop(23);
}
What happens now is that when I click either of the buttons, or in fact anywhere inside the movieclip (even layers I haven't applied actions to) it executes both functions at the same time so I end up going to frame 24. Can somebody provide an answer to this problem?
Obviously this in both cases refers to the same object, and not to the buttons in particular. Record the names of those buttons as you've named their instances on the timeline, say button1 and button2 and write the code employing those names.
button1.addEventListener(MouseEvent.CLICK, convnext);
function convnext(evt:MouseEvent):void
{
parent.parent.gesprekkencivcen.nextFrame();
}
button2.addEventListener(MouseEvent.CLICK, convend1);
function convend1(evt:MouseEvent):void
{
parent.parent.gesprekkencivcen.gotoAndStop(23);
}
With this, however, you will need to update the link to gesprekkencivcen in both listeners, as those buttons will have this as parent, and their target apparently is not a child of this. I have tried to plainly set a call to parent.parent.gesprekkencivcen, which might not work.

Set a button to invisible once clicked

I'm trying to set a button to invisible in AS3 however when the I leave the frame and come back to it the button is visible again. This is for a jeopardy game I make making for comm tech class.
Here is what I currently have:
a1.addEventListener(MouseEvent.CLICK, a1mouseClick);
function a1mouseClick(mouse:MouseEvent) {
a1.visible = false;
gotoAndStop("A1");
trace("Going to A1");
}
however when it comes back to the frame with the a1 button it is visible again.
Here is my current animation: https://dl.dropbox.com/u/23938245/jeporady.fla
While moving through the timeline flash player can recreates sprites, movie clips and text fields, so your buttons appears visible again. To prevent recreation move all controls to separate level without key frames. If key frames are required try to set the same instance name for this button in all keyframes.
#fsbmain and #prototypical they are right.
While moving through the timeline flash player can recreates sprites, movie clips and text fields, so your buttons appears visible again. To prevent recreation move all controls to separate level without key frames. If key frames are required try to set the same instance name for this button in all keyframes.
I was looking at your project, and offer a quick fix, you need to do the following:
Create a new layer on top to manage a few of actions availables for all frames with the following actions:
import flash.display.DisplayObject;
// Manages the buttons visible state
var buttonsStates:Object = {
"a1":true, "b1":true, "c1":true, "d1":true, "e1":true,
"a2":true, "b2":true, "c2":true, "d2":true, "e2":true,
"a3":true, "b3":true, "c3":true, "d3":true, "e3":true,
"a4":true, "b4":true, "c4":true, "d4":true, "e4":true,
"a5":true, "b5":true, "c5":true, "d5":true, "e5":true
};
// Checks the buttons visibility
function checkVisibility () {
for (var buttonName:String in buttonsStates)
{
var child:DisplayObject = this.getChildByName(buttonName);
child.visible = buttonsStates[buttonName];
}
}
// Saves the visible satatus to false
function setVisibilityToFalse(target:*) {
buttonsStates[target.name] = false;
target.visible = false;
}
Every time you want to check the visibility of the buttons you must call the checkVisibility() function. For example, every time you return to the button list.
Finally the event handler for each button must be like this:
function a1mouseClick(mouse:MouseEvent) {
setVisibilityToFalse(mouse.currentTarget); // Saves the visible state to false
gotoAndStop("A1");
trace("Going to A1");
}
You can download the edited file here http://cl.ly/Lt6X
You are missing a fundamental aspect of how the flash timeline and keyframes function. Once you move away from that frame, the stage instance of the content of that frame and it's properties/states are gone. When you return to that frame, the instance is created again based on the keyframe contents.
I think the best solution given your current approach is to put the main board persistent throughout all the frames. You can do that by creating a layer for it, and have it's keyframe extend from frame 2 to frame 27. However, your next issue will be adjusting visibility of all the elements on that screen when you don't want them visible.
My suggestion would be to put all the elements of that screen into a movieclip symbol of it's own and add that movieclip, and all code for it's listeners, to this new layer you created. For example you might name that instance - main_board and therefore you could modify it's visibility with main_board.visible property. If you did choose that solution, you would need to modify all the code on that frame to use that instance name as well ie :
main_board.a1.visible = false;
Also, you'd need to modify all you addEventListener lines as well :
main_board.a1.addEventListener(MouseEvent.CLICK, a1mouseClick);
Your approach for this game could be greatly simplified, but even further beyond the scope of this question than I have already gone!