Moving pipes query in a Flappy Bird game - arrays

I am trying to build a Flappy Bird like game in Adobe Flash, using Action Script 3 as a school project. I am using an array of Pipe objects to create the number of Pipes for each level (at this stage my code is with one level/stage).
I have one problem though, after I created the array of Pipes and them added each Pipe object to the stage, I tried to use other function to move the pipes, iterating through the array and changing the x dimension value to every Pipe object, but it doesn't work.
Here is my code:
import flash.events.MouseEvent;
stop();
Bird_mc.stop();
//array of pipes;
var pipeArray:Vector.<Pipe > = new Vector.<Pipe > ;
var birdVelocity:int = 0;
var stageGravity:int = 2;
stage.addEventListener(Event.ENTER_FRAME,birdFall);
stage.addEventListener(Event.ENTER_FRAME,createPipesAndLines);
stage.addEventListener(Event.ENTER_FRAME,movePipesAndLines);
stage.addEventListener(MouseEvent.CLICK,birdFly);
function birdFall(event:Event):void
{
Bird_mc.y += birdVelocity;
birdVelocity += stageGravity;
}
function birdFly(event:MouseEvent):void
{
birdVelocity = -16;
Bird_mc.play();
}
/*Move Pipes and Lines to the left - TO BE MADE*/
function createPipesAndLines(event:Event):void
{
for (var i:int = 0; i<10; i++)
{ //use of of-else to separate the pipes up and down and rotate em
if (i%2==0)
{
pipeArray.push(new Pipe());
addChild(pipeArray[i]);
pipeArray[i].x = i * 250;
pipeArray[i].y = 50;
}
else
{
var tempPipe:Pipe = new Pipe();
tempPipe.rotation = 180;
pipeArray.push(tempPipe);
addChild(pipeArray[i]);
pipeArray[i].x = i * 300;
pipeArray[i].y = 400;
}
}
}
//move the pipes to left
function movePipesAndLines(event:Event):void
{
for (var i:int = 0; i<10; i++)
{
pipeArray[i].x -= 0.5;
}
}

Firstly I'd recommend having a single event function that calls every other function that need updating.
stage.addEventListener(Event.ENTER_FRAME, loop);
function loop(event:Event):void {
birdFall();
birdFly();
movePipesAndLines();
}
Secondly you don't really want to add all pipes from the start of the game. I would imagine Flappy Bird attaches new Pipe objects as the screen moves. It's a whole lot more efficient that way.
Other than that the code looks good. I would however like to see what's inside the Pipe object. Perhaps Pipe.x is an int instead of Number?

Related

How to tweenLite all objects in array and keep distance position?

Hey everyone so I have an array of Movie Clip Objects called aPlanetArray and what I am trying to accomplish is having all the objects in the array move down to a certain positing and then stop using tweenLite or any other method that would accomplish this. I know I can do it with y+=2 but I want all objects to move down the screen real quick in a bounce like effect using Tweenlite and to keep their distance ratios.
Here is how I have them setup when added to the stage:
//Numbers
xSpacing = 100;
ySpacing = 180;
startPoint = new Point((stage.stageWidth / 2), (stage.stageHeight / 2) );
private function addOuterPlanets():void
{
for (var i:int = 0; i < nPlanets; i++)
{
outerPlanets = new mcOuterPlanets();
outerPlanets.x = startPoint.x + (xSpacing * i);
outerPlanets.y = startPoint.y - (ySpacing * i);
stage.addChild(outerPlanets);
aPlanetArray.push(outerPlanets);
}
}
and when I tween them I am using this tweenlite function:
for each(var allPlanets:mcOuterPlanets in aPlanetArray)
{
TweenLite.to(allPlanets, 5.0, {y:550, ease:Back.easeOut});
}
This works perfect but all objects in array line up together and don't keep their spacing against one another. Any ideas would be appreciated thank you!
The simplest way would be to just have all the planets in a parent container and then move the container instead of the planets.
var planetContainer:Sprite = new Sprite();
function addPlanetsToContainer():void{
for (var i:int = 0; i < aPlanetArray.length; i++){
planetContainer.addChild(aPlanetArray[i]);
}
}
And now you can do your tween on planetContainer
Now to put the character on a planet, you can either do
planet.addChild(character);
or
character.x = planet.x + planet.parent.x;
character.y = planet.y + planet.parent.y;

How to remove a child from a random array?

I have an array that randomly creates 10 dots. However there's a certain area where I do not want them to be created. How can I achieve this? My code gives me error 2025.
"The supplied DisplayObject must be a child of the caller."
It will occasionally output the totalDots as instructed, (trace""+totalDots), but 90% of the time it will give me the error.
public var numDots:Array = [];
public var totalDots:int = numDots.length;
public var box:Box = new Box();
public function addBox():void
{
box.x = stageWidth/2;
box.y = stageHeight/2;
addChild(box);
}
private function addDot():void
{
for(var i:int = 0; i < 10; i++)
{
var dot:Dot = new Dot();
dot.x = Math.floor(Math.random() * stageWidth);
dot.y = Math.floor(Math.random() * stageHeight);
this.addChild(dot);
totalDots++;
trace(""+totalDots);
for(var j:int = 0; j < totalDots; j++)
{
if(numDots[j].hitTestObject(box))
{
stage.removeChild(numDots[j]);
numDots.splice(j, 1);
totalDots--;
}
}
}
}
Your problem is your nested loop. With each iteration, you add one new dot, and then loop over all of the existing ones, and remove them if it collides with the box. I don't think that's what you intended to do.
It looks like you just want to make sure the dots are not added within a certain area. In that case, keep it simple with a do while loop:
for(var i:int = 0; i < 10; i++)
{
var dot:Dot = new Dot();
this.addChild(dot);
do {
dot.x = Math.floor(Math.random() * stageWidth);
dot.y = Math.floor(Math.random() * stageHeight);
} while(dot.hitTestObject(box))
totalDots++;
trace(""+totalDots);
}
You never add any dot to your array.
You add the dot to the display list like so:
this.addChild(dot);
and you try to remove it like so:
stage.removeChild(numDots[j]);
Despite the fact that the dot is never added to the array, this couldn't have worked even if it was. That's because this is not stage. They are two different things.
You should never use stage.addChild() (check the documentation for more info on that). Just call addChild() all the time which is equivalent to this.addChild(). This ensures that you always operate on one and the same DisplayObjectContainer
For what it's worth, you can avoid the trial loop altogether by calculating a random value with the proper interval (the difference between the include and exclude areas) and deriving x and y coordinates from that.
The following code (written in a language I do not know, apologies if the syntax is faulty) has two cases. The if case handles the situation where the dot will appear left or right of the exclusion box, and the range of x values is restricted to being left or right of that box. The else case is where the dot will appear above or below the box, and the x values are not restricted.
var interval:int = stageWidth * stageHeight - box.w * box.h;
var cut:int = interval - (stageWidth - box.w) * box.h;
for (var i:int = 0; i < 10; i++) {
var r:int = Math.floor(Math.random() * interval);
var x:int;
var y:int;
if (r >= cut) {
r -= cut;
y = r / (stageWidth - box.w);
x = r - y * (stageWidth - box.w);
y += box.y;
if (x >= box.x) x += box.w;
} else {
y = r / stageWidth;
x = r - y * stageWidth;
if (y >= box.y) y += box.h;
}
var dot:Dot = new Dot();
dot.x = x;
dot.y = y;
this.addChild(dot);
totalDots++;
trace(""+totalDots);
}
This assumes that box is entirely within stageWidth,stageHeight.
Also notable is that it allows the dots to overlap; same as the original code.
For more complex shapes you can set box to the largest rectangle fully enclosed by that shape so as to avoid many but not all retry cases. This can be helpful for large shapes and many dots. Or there are variations which might manage a perfect fit to another shape (eg., an ellipse).

Detecting and using collision detection between two different arrays to remove instances of them

I'm currently creating a small flash game using ActionScript and after receiving help for another issue I had on here, I've encountered another one when moving onto a different part of it.
This is the code I currently have:
var asteroidSpeed = 5;
var soundExplosion:Sound = new ExplosionSound();
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKDown);
var newLaser:Array = new Array();
function onKDown(e:KeyboardEvent):void {
keys[e.keyCode] = true;
if (e.keyCode == 32) {
/*laser.x = player.x + player.width/2 - laser.width/2;
laser.y = player.y;
addChild(laser);*/
for (var count=0; count < 4; count++) {
newLaser[count] = new shipLaser();
newLaser[count].x = player.x + player.width/2 - newLaser.width/2;
newLaser[count].y = player.y;
addChild(newLaser[count]);
}
}
}
var spawnTimer:Timer = new Timer(3000); //timer will tick every 3 seconds
spawnTimer.addEventListener(TimerEvent.TIMER, spawn, false, 0, true); //let's run the spawn function every timer tick
spawnTimer.start();
var spawnPoints:Array = [0,100,200,300,400,500,550]; //your list of spawn x locations
var spawnAmount:int = 4; //how many asteroids to have on the screen at once (you could increase this over time to make it more difficult)
var asteroids:Vector.<asteroidOne> = new Vector.<asteroidOne>(); //the array for your asteroids - changed to vector for possible performance and code hint improvement (basically the same as Array but every object has to be of the specified type)
spawn(); // calling it immediately
//calling this will spawn as many new asteroids as are needed to reach the given amount
function spawn(e:Event = null):void {
if(asteroids.length >= spawnAmount) return; //let's not bother running any of the code below if no new asteroids are needed
spawnPoints.sort(randomizeArray); //lets randomize the spwanPoints
var spawnIndex:int = 0;
var a:asteroidOne; //var to hold the asteroid every loop
while (asteroids.length < spawnAmount) {
a = new asteroidOne();
a.x = spawnPoints[spawnIndex];
spawnIndex++; //incriment the spawn index
if (spawnIndex >= spawnPoints.length) spawnIndex = 0; //if the index is out of range of the amount of items in the array, go back to the start
a.y = -50;
asteroids.push(a); //add it to the array/vector
addChild(a); //add it to the display
}
}
player.addEventListener(Event.ENTER_FRAME, obstacleMove);
function obstacleMove(evt:Event):void {
for (var i:int = 0; i < asteroids.length;i++) {
asteroids[i].y += asteroidSpeed;
if (asteroids[i].y > stage.stageHeight || asteroids[i].x > stage.stageWidth || asteroids[i].x < -asteroids[i].width || asteroids[i].y < -asteroids[i].height) {
//object is out of the bounds of the stage, let's remove it
removeChild(asteroids[i]); //remove it from the display
asteroids.splice(i, 1); //remove it from the array/vector
continue; //move on to the next iteration in the for loop
}
if (player.hitTestObject(asteroids[i])) {
trace("HIT");
removeChild(asteroids[i]);
asteroids.splice(i,1);
removeChild(player);
// will add end-game trigger here soon.
}
}
}
function randomizeArray(a:*, b:*):int {
return (Math.random() < .5 ) ? 1 : -1;
}
player.addEventListener(Event.ENTER_FRAME, laserCollision);
function laserCollision(evt:Event):void {
for (var i in newLaser) {
for (var a in asteroids) {
if (asteroids[a].hitTestObject(newLaser[i])) {
trace("BOOM!");
var soundExplosion:Sound = new ExplosionSound();
var channel1:SoundChannel = soundExplosion.play();
removeChild(newLaser[i]);
removeChild(asteroids[a]);
}
}
}
}
addEventListener(Event.ENTER_FRAME, laserEnter);
function laserEnter(event:Event):void {
for (var i in newLaser) {
newLaser[i].y -= laserSpeed;
// Moves the laser up the screen
if(newLaser[i].y == 0) {
removeChild(newLaser[i]);
}
}
}
What I want to do is when an instance from the newLaser array collides with an instance of the asteroids array, to remove both from the scene / indexes (but only the two that collided and not all of the ones on the scene).
Currently, when a laser hits an asteroid, it removes the asteroid but not the laser and one of the asteroids on the current row stops moving and then the next row of asteroids spawns but does not move down.
I get this error too:
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
at flash.display::DisplayObjectContainer/removeChild()
at _8_fla::MainTimeline/obstacleMove()
Any help would be greatly appreciated.
You error, is likely because you are running 3 separate enter frame handlers in the same scope, and removing items from arrays and display lists (that are referenced in multiple enter frame handlers). So you asteroid is removed from the display list in one, and then you try to remove it again in another.
There are also a whole lot of other issues with your code that will cause errors and undesired results. Things like for(var i in newLasers) - in that kind of loop, i will refer to the actual laser object not the index of the array. I've re-factored your code and added lots of code comments to hopefully give you an idea of where you were going wrong:
var spawnTimer:Timer = new Timer(3000); //timer will tick every 3 seconds
var spawnPoints:Array = [0, 100, 200, 300, 400, 500, 550]; //your list of spawn x locations
var spawnAmount:int = 4; //how many asteroids to have on the screen at once (you could increase this over time to make it more difficult)
var asteroidSpeed = 5;
var asteroids:Vector.<asteroidOne> = new Vector.<asteroidOne>(); //the array for your asteroids - changed to vector for possible performance and code hint improvement (basically the same as Array but every object has to be of the specified type)
var lasers:Vector.<shipLaser> = new shipLaser(); //the array of lasers
var maxLasers:int = 4; //the maximum amount lasers allowed at any given time
var soundExplosion:Sound = new ExplosionSound();
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKDown);
player.addEventListener(Event.ENTER_FRAME, gameLoop);
spawnTimer.addEventListener(TimerEvent.TIMER, spawn, false, 0, true); //let's run the spawn function every timer tick
spawnTimer.start(); //start the spawn timer
spawn(); // calling it immediately
function onKDown(e:KeyboardEvent):void{
if (e.keyCode == 32) {
//create ONE laser per button push (instead of 4 identical lasers)
if(lasers.length < maxLasers){ //if player hasn't reached the maximum amount of lasers available
var tmpLaser:shipLaser = new shipLaser();
tmpLaser.x = player.x + player.width / 2 - tmpLaser.width / 2;
tmpLaser.y = player.y;
addChild(tmpLaser);
lasers.push(tmpLaser);
}
}
}
//calling this will spawn as many new asteroids as are needed to reach the given amount
function spawn(e:Event = null):void {
if (asteroids.length >= spawnAmount)
return; //let's not bother running any of the code below if no new asteroids are needed
spawnPoints.sort(randomizeArray); //lets randomize the spwanPoints
var spawnIndex:int = 0;
var a:asteroidOne; //var to hold the asteroid every loop
while (asteroids.length < spawnAmount)
{
a = new asteroidOne();
a.x = spawnPoints[spawnIndex];
spawnIndex++; //incriment the spawn index
if (spawnIndex >= spawnPoints.length)
spawnIndex = 0; //if the index is out of range of the amount of items in the array, go back to the start
a.y = -50;
asteroids.push(a); //add it to the array/vector
addChild(a); //add it to the display
}
}
function gameLoop(e:Event):void {
//LOOP through all the asteroids, give it a label (asteroidLoop) so we can break/continue it inside other loops
asteroidLoop: for (var i:int = 0; i < asteroids.length; i++) {
//check if asteroid is out of bounds
if (asteroids[i].y > stage.stageHeight || asteroids[i].x > stage.stageWidth || asteroids[i].x < -asteroids[i].width || asteroids[i].y < -asteroids[i].height) {
//object is out of the bounds of the stage, let's remove it
removeChild(asteroids[i]); //remove it from the display
asteroids.splice(i, 1); //remove it from the array/vector
continue; //forget the rest of the code below and move on to the next iteration in the for loop since the asteroid is gone
}
//check if any lasers are colliding with this asteroid
for (var j:int = lasers.length-1; j >= 0;j--) { //iterate over all lasers backwards
if (asteroids[i].hitTestObject(lasers[j])){
trace("BOOM!");
var soundExplosion:Sound = new ExplosionSound();
var channel1:SoundChannel = soundExplosion.play();
//remove the asteroid
removeChild(asteroids[i]); //remove it from the display
asteroids.splice(i, 1); //remove it from the array/vector
//remove the laser
removeChild(lasers[j]);
lasers.splice(j, 1);
continue asteroidLoop; //break completely out of this inner for-loop (lasers) since the asteroid in the outer loop was removed, and move on to the next asteroid
}
}
//check if this asteroid collides with the player
if (player.hitTestObject(asteroids[i])){
trace("HIT");
//remove the asteroid
removeChild(asteroids[i]); //remove it from the display
asteroids.splice(i, 1); //remove it from the array/vector
removeChild(player);
spawnTimer.stop(); //stop spawning new asteroids
//will add end-game trigger here soon.
break; //break completely out of the asteroid loop if it's game over (as there's no point in checking the rest of the asteroids)
}
//we've made it this far, so let's just move this asteroid
asteroids[i].y += asteroidSpeed;
}
for (var i:int = lasers.length-1; i >= 0;i--) { //iterate backwards
lasers[i].y -= laserSpeed;
// Moves the laser up the screen
if (lasers[i].y <= 0){ //make this less than or equal to 0
removeChild(lasers[i]); //remove the laser from the display list
lasers.splice(i, 1); //remove the laser from the array
}
}
}
function randomizeArray(a:*, b:*):int {
return (Math.random() < .5) ? 1 : -1;
}

Assigning instance names to array Objects: ActionScript 3.0

I'll just start by saying that I am a bit new to programming, and I apologize if this is a stupid question.
I have a timer running in my application that at every interval, creates an a new instance of a MovieClip called blueBall.Here is my code:
var randomX:Number = Math.random() * 350;
var newBlue:mc_BlueBall = new mc_BlueBall ;
newBlue.x = randomX;
newBlue.y = -20;
for(var i:Number = 0;i < blueArray.length; i++)
{
newBlue.name = "newBlue" + i;
}
blueArray.push(newBlue);
addChild(newBlue);
}
var randomX:Number = Math.random() * 350;
var newBlue:mc_BlueBall = new mc_BlueBall ;
newBlue.x = randomX;
newBlue.y = -20;
for(var i:Number = 0;i < blueArray.length; i++)
{
newBlue.name = "newBlue" + i;
}
blueArray.push(newBlue);
addChild(newBlue);
}
My question is: How do I make it so that each newly created object in the array has it's own hitTestObject Event? I want to make it so that if the if the user's icon touches one of the newBlue objects, that newBlue object with be removed, and the score will go up a point.
Thanks!
this is my first time answering a question here but I hope I can help! Assuming you have a timer for your main game loop, you should try something like this once per frame:
//For each blue ball in the array
for(var i:int = 0; i < blueArray.length; i++) {
//If it touches the player
if(blueArray[i].hitTestObject(thePlayerMC)) {
//Increment the score
score++;
//Remove from stage and array
removeChild(blueArray[i]);
blueArray.splice(i, 1); //<-At index i, remove 1 element
//Decrement i since we just pulled it out of the array and don't want to skip the next array item
i--;
}
}
This is sort of the quick and dirty solution, but highly effective and commonly used.

How to compare 2 arrays?

I have two arrays, namely combo and truecombo. The user fills the combo with MovieClips by clicking on various buttons on the stage, truecombo is the correct combination.
At any given point (enterFrame) Flash is checking whether the two are the same, if yes, then do some stuff. For the time being this is my code (altered several times, like with Typecasting the indices, adding .parent at the end of combo[o] etc. 2 things will happen, either one or the other.
Either the statement will not be satisfied, at which point the adding and chopping of the combo array will continue, or the condition will be instantly met when combo.length = 6. Check my code.
UPDATE: I have a dropbox file with my current code. Click this for FLA link and here is the SWF link stripped down as always for ease and security.
/*stage.*/addEventListener(Event.ENTER_FRAME, checkthis);
function checkthis(e:Event)
{
for(var o:int=0;o<= combo.length; o++)
{
if((combo[o] == truecombo[o]) && (combo.length==truecombo.length))
{
equal=true;
}
}
if (equal==true)
{
stage.removeEventListener(Event.ENTER_FRAME, checkthis);
endSeq();
}
}
function endSeq():void
{
bravo.play();
for (var i:int = 0; i < combo.length; i++)
{
var element:DisplayObject = combo[i];
element.parent.removeChild(element);
}
firebb.gotoAndPlay(2);
windbb.gotoAndPlay(2);
spiritbb.gotoAndPlay(2);
earthbb.gotoAndPlay(2);
}
This is how I push my new elements to the combo array.
function add(element:DisplayObject)
{
twist.gotoAndPlay(2);
element.width = WIDTH;
element.height = HEIGHT;
if (this.combo.length >= MAX_ELEMENTS)
{
removeChild(this.combo.shift());
}
this.combo.push(element as DisplayObject);
this.addChild(element);
this.reorder();
}
function reorder()
{
for (var i:int = 0; i < combo.length; i++)
{
var element:DisplayObject = combo[i];
element.x = OFFSET_X + (i * SEP_X);
element.y = OFFSET_Y;
}
}
And this is how I have my truecombo and its contents created.
var fireb:firebtn = new firebtn();
var spiritb:spiritbtn = new spiritbtn();
var earthb:earthbtn = new earthbtn();
var windb:windbtn = new windbtn();
var combo:Array=new Array();
const truecombo:Array = [fireb,windb,spiritb,windb,earthb,fireb];
Sorry for the lack of comments, I'd guess it's pretty self-explanatory. Thanks in advance.
I believe combo[o] & truecombo[o] are two instances of the same class & you want them to be matched. If that is the case you may consider :
getQualifiedClassName(combo[o]) == getQualifiedClassName(truecombo[o])
To match the way you did, you must ensure the objects lying inside truecombo be referring to the same ones on stage & not new instances.
EDIT:
It seems you do not break the loop when the match is a success. Use this instead :
function checkthis(e:Event)
{
for(var o:int=0;o<= combo.length; o++)
if((combo[o] == truecombo[o]) && (combo.length==truecombo.length)) {
equal=true;
break;
}
if (equal) {
stage.removeEventListener(Event.ENTER_FRAME, checkthis);
endSeq();
}
}
Here's a really simple loop:
var equal:Boolean=true
if(combo.length == truecombo.length) {
for(var i:int=0; i<combo.length; i++) {
if(combo[i] != truecombo[i]) {
equal=false;
break;
}
}
} else {
equal=false;
}
if(equal) {
//do whatever
}
This assumes both are equal, until we find out otherwise. So if the lengths are different, they are not equal. If the ith element is different, they are not equal.
In the end, you check if your flag equal is true and do whatever you want to do.

Resources