Actionscript 3.0 : Controlling child movieclips added with an array - arrays

I'm trying to build a generic slide show flash file controlled with key presses... right and left arrow to go to next / previous slide, down and up to call / uncall the single animations / bullet points / whatever on a single slide.
So far I managed to get a file that can load all the pages from a movieclip and flick through them with right and left arrow and a nice fade out and fade in effect, even when flicking very fast.
What I do not get at all after several hours and lots of google searches is how to now control the single page movieclips. I uploaded the file so far:
http://www.broesel-brzelius.de/zeug/slide.zip
You can see that my (as of yet crude) attempt to control the pageHolder.Pages.page1 movieclip to gotoAndStop(2) just results in the pageHolder.Pages movieclip that was loaded to position 0 in the array to go to frame 2 and thus display the second page.
Code so far:
import flash.display.Sprite;
import flash.events.MouseEvent;
import com.greensock.*;
import flash.display.MovieClip;
// instantiate a variable to find number of pages
var numberOfPages:Pages = new Pages();
// instantiate an array to hold the page movieclips
var pageArray:Array = new Array();
// instantiate a container to hold the pages
var pageHolder:Sprite = new Sprite();
// declare variables that will hold reference to the current page IDs
var targetIDold:int = 0;
var targetIDnew:int = 0;
// declare a variable that will hold the current direction of slide movement
var movement:int = -1;
// call a function that builds the application
// pass in the number of pages in fl_prevSlide
buildApp(numberOfPages.totalFrames);
// this function builds the application
function buildApp(n:int):void
{
// declare variables for the pages
var p:Pages;
// instantiate a new Page, send its playhead to the current value of i+1
// it is necessary to add 1 to the value of i, since i starts at 0, while the timeline starts at 1
for (var i:int = 0; i < n; i++)
{
p = new Pages();
p.gotoAndStop(i + 1);
pageArray.push(p);
}
// set the position of the pageHolder relative to the buttonHolder
pageHolder.x = pageHolder.y = 0;
// add the first page (at index 0) from pageArray to pageHolder
pageHolder.addChild(pageArray[targetIDnew]);
// add pageHolder and buttonHolder to the stage
addChild(pageHolder);
}
//Adding Listener and corresponding function to change between slides
stage.addEventListener(KeyboardEvent.KEY_DOWN, f_changeSlide);
function f_changeSlide(evt:KeyboardEvent):void
{
if(evt.keyCode == 37) // left arrow
{
f_prevSlide();
}
else if (evt.keyCode == 39 || evt.keyCode == 32) // right arrow or space
{
f_nextSlide();
}
}
function f_prevSlide():void
{
if(targetIDnew > 0)
{
movement = -1;
targetIDnew -= 1;
f_addPage();
}
}
function f_nextSlide():void
{
if(targetIDnew < (numberOfPages.totalFrames - 1))
{
movement = 1;
targetIDnew += 1;
f_addPage();
}
}
function f_addPage():void
{
// use the targetID variable to access the corresponding index in the pageArray and assign it to a temporary variable
targetIDold = targetIDnew - movement;
var _mcOld:MovieClip = MovieClip(pageArray[targetIDold]);
var _mcNew:MovieClip = MovieClip(pageArray[targetIDnew]);
//avoid flickering
if (_mcNew.alpha == 1) {
_mcNew.alpha = 0;
}
// add the temp variable to pageHolder
pageHolder.addChild(_mcNew);
// tween the temp variable to the specified properties, then call a function to remove the previous page
// new page gets faded in, old page gets faded out
TweenMax.to(_mcNew, 1.5, {alpha:1});
TweenMax.to(_mcOld, 1.5, {alpha:0, onComplete:f_removePage});
}
function f_removePage():void
{
// previous page will always be at index 0; remove it
// the new page just added will drop down to index 0 when the previous page is removed
pageHolder.removeChildAt(0);
}
//Trying to advance / decrease the timeline in the movieclips of the single pages by pressing down / up
stage.addEventListener(KeyboardEvent.KEY_DOWN, f_changeFrame);
function f_changeFrame(evt:KeyboardEvent):void
{
if(evt.keyCode == 40) // down arrow
{
f_nextFrame();
}
else if (evt.keyCode == 38) // up arrow
{
f_prevFrame();
}
}
function f_nextFrame():void
{
var w:uint = pageHolder.numChildren - 1 ;
(pageHolder.getChildAt(w) as MovieClip).gotoAndStop(currentFrame + 1);
// for (var i:uint = 0; i < pageHolder.numChildren; i++)
// {
// trace (+i+'.\t name:' + pageHolder.getChildAt(i).name + '\t type:' + typeof (pageHolder.getChildAt(i)));
// }
trace ("down!");
}
function f_prevFrame():void
{
var w:uint = pageHolder.numChildren - 1 ;
(pageHolder.getChildAt(w) as MovieClip).gotoAndStop(currentFrame - 1);
// for (var i:uint = 0; i < pageHolder.numChildren; i++)
// {
// trace (+i+'.\t name:' + pageHolder.getChildAt(i).name + '\t type:' + typeof (pageHolder.getChildAt(i)));
// }
trace ("up!");
}
Edit: clarified code a bit
Edit2: TL;DR: Problem is the last bit of code with the three functions that fail to control the timeline of mc page1, which is a child of mc pages, which is pushed frame by frame into an array and loaded at runtime into the sprite pageHolder. How to control the timeline of the single page movieclips?

Finally got it to work. This will work as a perfect powerpoint replacement :D
You will need to name the single slides (one movieclip on each frame of the mc pages) as page0, page1 etc. and then can control their timeline with this code (just replace the functions listed in the question above):
function f_nextFrame():void
{
var temp:uint = pageHolder.numChildren - 1;
var frame:uint = ((pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].currentFrame);
(pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].gotoAndStop(frame + 1);
}
function f_prevFrame():void
{
var temp:uint = pageHolder.numChildren - 1;
var frame:uint = ((pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].currentFrame);
(pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].gotoAndStop(frame - 1);
}

Related

targeting multiple movieclips of the same class to change frame number as3

I'm having this issue with this memory match game. I want the cards to show face up so the user can memorize them, and after 3 seconds I want them to turn over and show their back face.
I got the entire game working perfectly, btw. I'm just missing this detail!
the code below is how my cards get created (they are all 1 movieclip (mcCartas) with 37 frames inside (36 types of card and back card))
const QUANT_CARTAS:int = 36; ///// number of cards (faces drawn, frame numbers)
const CARTAS_POR_LINHA:int = 6; /// number of lines (6x6 grid)
var cartas:Array = new Array(); // cards array
var cartasColetadas:Array = new Array(); // clicked cards array
////////gets the 36 cards into the array
for(var i:int=0;i<QUANT_CARTAS;i++)
{
cartas.push(i);
}
/////////shuffles the cards
for(var moeda:int = QUANT_CARTAS-1;moeda>0;moeda--)
{
var pos:int = Math.floor(Math.random() * moeda);
var carta:int = cartas[moeda];
cartas[moeda] = cartas[pos];
cartas[pos] = carta;
}
////////// puts them on the table
for(i=0;i<QUANT_CARTAS;i++)
{
var novaCarta:Carta = new Carta();
novaCarta.tipoCarta = cartas[i];
novaCarta.x = 5 + (novaCarta.width + 2.7) * (i % CARTAS_POR_LINHA);
novaCarta.y = 5 + (novaCarta.height + 2.7) * (Math.floor(i/CARTAS_POR_LINHA));
novaCarta.gotoAndStop(cartas[i]+1); // this line they all face their number OR
//novaCarta.gotoAndStop(QUANT_CARTAS + 1); // this line they all face back (last frame)
novaCarta.buttonMode = true;
novaCarta.addEventListener(MouseEvent.CLICK, cartaClicada);
addChild(novaCarta);
trace (cartas);
if(i == 35)
{
podeJogar = false;
mcContagem.x = 884;
mcContagem.y = 511;
addChild(mcContagem);
intervalo = setInterval(desviraCartas, 3000);
function desviraCartas()
{
for(var j:int = 0;j < QUANT_CARTAS; j++)
{
//here I'm trying to make them go back to their back (last frame)
//something????.gotoAndStop(QUANT_CARTAS + 1);
if(j == 35)
{
clearInterval(intervalo);
iniciaJogo();
}
}
}
}
}
this code IS working, but a couple lines up where I typed "something????.gotoAndStop etc" ... that's where I have tried everything... heeelppppp plzzzzz
You should store the new Cartas in an array when you add them. Then use that array to reference them: Push into array:
aCartasMCs.push(novaCarta);
Then you can reference like:
aCartasMCs[i].gotoAndStop(QUANT_CARTAS + 1);

AS3 Array items not splicing properly

I'm attempting to make a small game where the user mouses over the circles that fall from the ceiling for points. The circles are added to a container and pushed into an array to hold them, and are removed and spliced when they are mouse-over'd or go off stage.
Everything works fine, until two circles are removed at nearly the same time, whether it be from falling off stage at the same time or mousing over two of them extremely fast. When this happens, the child on stage is removed, but the object is still left in the array, meaning another circle cannot take its place, leaving one less circle spawning every time the issue happens.
Code on main timeline:
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.MovieClip;
import flash.display.Sprite;
var ballContainer:Sprite = new Sprite();
addChild(ballContainer);
var maxBalls:uint = 10;
var balls:Array = [];
var ballTypes:Array = [GreenBall];
var ballChances:Array = [800];
var ballVelocities:Array = [1.5];
var ballAccelerations:Array = [1.02];
stage.addEventListener(Event.ENTER_FRAME, onTick);
function onTick(e:Event):void {
while (balls.length < maxBalls){
addBall();
}
}
function addBall():void {
var ballType = ballTypes[0];
var ball = new ballType;
ball.x = Math.ceil(Math.random()*(stage.stageWidth - ball.width));
ball.y = 0 - (ball.height*1.5);
ballContainer.addChild(ball);
balls.push(ball);
}
Code in GreenBall:
import flash.events.Event;
var mainStage = Sprite(root);
var index = mainStage.balls.indexOf(this);
var velocity:Number = mainStage.ballVelocities[0]*randomNumber(0.5, 1.5);
var acceleration:Number = mainStage.ballAccelerations[0];
this.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
function onMouseOver(e:MouseEvent):void {
this.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
removeBall();
}
this.addEventListener(Event.ENTER_FRAME, onTick);
function onTick(e:Event):void {
this.y += velocity;
velocity = velocity*acceleration;
if (this.y > stage.stageHeight + this.height){
this.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
removeBall();
}
}
function removeBall():void {
mainStage.balls.splice(index, 1);//doesn't get spliced if balls are removed too quickly
mainStage.ballContainer.removeChild(this);
this.removeEventListener(Event.ENTER_FRAME, onTick);
}
function randomNumber(min:Number, max:Number):Number {
return Math.random()*(max - min) + min;
}
So what's going on? Did I set something up incorrectly? How can I go about fixing this issue?
Any help would be appreciated greatly.
Your logic is flawed - the index should be calculated when the removal occurs. When you remove objects from an array via splice, the index of all the elements after the one you removed is decreased by one.
This means that if you have 10 balls and remove the first, the index value you have for every other ball will be incorrect and you'll be removing the wrong ball from your array on subsequent removals.
Moving the indexOf statement to the removeBall method should solve the issue:
function removeBall():void
{
var index:int = mainStage.balls.indexOf(this);
if(index >= 0)
{
mainStage.balls.splice(index, 1);
mainStage.ballContainer.removeChild(this);
this.removeEventListener(Event.ENTER_FRAME, onTick);
}
}
To make it easy on yourself, you could extend Array and make a remove function:
public dynamic class List extends Array
{
public function remove(item:*):void
{
var i:int = indexOf(item);
if(i >= 0) splice(i, 1);
}
}

Mouse Events for objects in an array in Flash

I am not sure how to make this clear but : Is there a way to allow mouse event register with objects in an array? I have multiple objects being added to stage from an array and i would like to call different functions after said objects are clicked on ? I have this:
function makeEnemies():void
{
//create humans
var tempEnemy:MovieClip;
var wolf:MovieClip;
tempEnemy = new Enemy2();
tempEnemy.cacheAsBitmap = true;
tempEnemy.speed = 20;
tempEnemy.x = Math.round(Math.random() * 800);
tempEnemy.y = Math.round(Math.random() * 480);
addChild(tempEnemy);
enemies.push(tempEnemy);
}
function moveEnemies():void
{
var tempEnemy:MovieClip;
for (var i:int =enemies.length-1; i>=0; i--)
{
tempEnemy = enemies[i];
if (tempEnemy.x > stage.stageWidth)
{
tempEnemy.x = stage.stageWidth;
}
if (tempEnemy.y > stage.stageHeight)
{
tempEnemy.y = stage.stageHeight;
}
tempEnemy.x += Math.round(Math.random() * tempEnemy.speed);
tempEnemy.y -= Math.round(Math.random() * tempEnemy.speed);
tempEnemy.addEventListener(MouseEvent.CLICK, scoreM);
function scoreM(event:MouseEvent):void
{
makeBite(tempEnemy.x, tempEnemy.y);
removeEnemy(i);
score++;
score_txt.text = String(score);
}
function removeEnemy(idx:int)
{
removeChild(enemies[idx]);
enemies.splice(idx,1);
}
And i get an error
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/removeChild()
at veinsVtest_fla::MainTimeline/removeEnemy()
at MethodInfo-67()
You won't need an Array for this job. Like Man of Snow said. Use event.currentTarget will point to the "clicked on Enemy"
tempEnemy.addEventListener(MouseEvent.CLICK, scoreM);
function scoreM(event:MouseEvent):void
{
var clickedOnEnemy:MovieClip = event.currentTarget as MovieClip;
//Now you have your enemy, do whatever you please with him.
makeBite(clickedOnEnemy.x, clickedOnEnemy.y);
//And farewell, my enemy ... time to remove him.
removeChild(clickedOnEnemy);
clickedOnEnemy = null;
score++;
score_txt.text = String(score);
}
* EDIT **
There are several ways to implement "removeAllEnemies".
One way to do it is to use have another MovieClip to hold all enemies created.
So create a movieClip and add it to stage.
var enemiesWrapper : MovieClip = new MovieClip();
addChild(enemiesWrapper);
And then instead of add enemy to root
addChild(tempEnemy); //Instead of doing this
Add them to this MovieClip instead.
enemiesWrapper.addChild(tempEnemy); //Do this instead
Note that your removeChild has to be updated accordingly
enemiesWrapper.removeChild(clickedOnEnemy);
And for "removeAllEnemies" function
function removeAllEnemies() {
while(enemiesWrapper.numChildren > 0) {
enemiesWrapper.removeChildAt(0);
}
}
Replace removeEnemy(i); with removeEnemy(event.currentTarget);
You cannot remove an integer, because it isn't a child. However, I'm assuming you want to remove the MovieClip that was clicked, and it looks like removeEnemy calls removeChild() on the parameter. If not, do you mind showing your removeEnemy function?

Angular js increment decrements with popover

Had done some code on angular.js for creating the following customized component Bump Up/ Bump Down middle menu container when clicks on the middle field.
present middle field is working fine + - is not working which we have to used for bump up/ down the same values.
Thx in advance.Code link
I have updated the code , if I understood your requirement clearly. See it here
Added a function
var findIndex = function(currentColor, colorArray){
var index = 0;
for(var i = 0; i < colorArray.length; i++){
if(currentColor.name == colorArray[i].name){
index = i;
return index;
}
}
return index;
}
And on decrement or increment
scope.decrementclick = function() {
var index = findIndex(scope.currentColor, scope.colors);
if(index > 0){
index--;
}
scope.selectColor(scope.colors[index]);
//alert("down clicked");
};

addEventListener(object[i]) in for loop gives me one object not multiple

i am making a leaderboard for a game. this leaderboard gets its scores from the an array. but when i add the eventListener i get only one of the object from the array.
this is mine object array:
[{gamenr:1,naam:"wilbert", score:60},{gamenr:1,naam:"joost", score:20},
{gamenr:2,naam:"harry", score:50},{gamenr:2,naam:"john", score:10},
{gamenr:3,naam:"carl", score:30},{gamenr:3,naam:"dj", score:16}]
code:
public function toonHighscoreArray():Array {
highScoreTabel.sortOn(["score"], [Array.NUMERIC]).reverse();//get highest score on top
var returnArray:Array = new Array();
for ( var i:int = 0; i < 6; i++ ) {
var scores:TextField = new TextField();
scores.addEventListener(MouseEvent.CLICK, function(e:MouseEvent){toon2deSpeler(highScoreTabel[i])});
scores.y = (i * 50) - 50;
scores.height = 50;
scores.text = "" + (i + 1) + ".\t" + highScoreTabel[i].naam + " met " + highScoreTabel[i].score + " punten.";
scores.autoSize = "left";
returnArray.push(scores);
}
return returnArray;
}
private function toon2deSpeler(score:Object) {
trace(score.naam);
}
i want the function toon2deSpeler to trace wilbert when i click the textfield where wilbert is in the textfield is clicked and harry when harry's textfield is clicked
but it gives me joost when i click wilbert but also when i click harry or joost ect.
how do i get the right object as parameter in toon2deSpeler?
Closures inside loops are not going to work as you expect, once the event handler is called it will use the last value of i.
Change your for loop to this :
for ( var i:int = 0; i < 6; i++ ) {
var scores:TextField = new TextField();
addScoreListener(scores, i);
scores.y = (i * 50) - 50;
scores.height = 50;
scores.text = "" + (i + 1) + ".\t" + highScoreTabel[i].naam + " met " + highScoreTabel[i].score + " punten.";
scores.autoSize = "left";
returnArray.push(scores);
}
private function addScoreListener(score:TextField, index:int):void
{
scores.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void{
toon2deSpeler(highScoreTabel[index]);
});
}
Functions run in the scope in which they were created (See this page on Function scope), so your inline listener:
function(e:MouseEvent){toon2deSpeler(highScoreTabel[i])}
is using the i from toonHighscoreArray(), not it's "own" copy of i. Given your code there though, you would be getting a null object reference instead of "joost", as you're trying to access highScoreTabel[6].
I would really recommend extending TextField and creating objects with the properties from highScoreTabel, then using Barış Uşaklı's method. However, it is possible to create each listener function in its own scope like this:
function getScoreClickListener(scoreID:uint):Function {
return function(e:MouseEvent){toon2deSpeler(highScoreTabel[scoreID])}
}
Then use that when adding your even listener:
scores.addEventListener(MouseEvent.CLICK, getScoreClickListener(i));
This makes it difficult to remove the event listeners later though, so you'd need to keep track of them separately.

Resources