I want to create a stacking Game. Where when you tap the screen for instance a block falls down and a new one appears where the other one originally was. Now when the User taps the screen again the same block falls down and if aligned correctly stacks on top of the first one so one and so one. Keep stacking until you miss.
I thought creating an array and pushing each new object to that array would be able to hitTest between each new one etc and have them stack on each other. I realized I don't quite understand how to go about doing this. New instances are created so I got that down. Here is my code so far:
private function engineLogic(e:Event):void
{
stackingHandler();
}
private function stackingHandler():void
{
for (var i:int = 0; i < aCatArray.length; i++)
{
var currentCat:mcCats = aCatArray[i];
//HIT TEST CATS
}
trace("NUMBER OF CATS: " + aCatArray.length);
}
private function onTap(e:MouseEvent):void
{
//Move Down
TweenLite.to(cats, 1.0, {y:(stage.stageHeight / 2) + 290, onComplete: addCats});
}
private function addCats():void
{
//Create Instance
cats = new mcCats();
//Add Objects
addChild(cats);
//Push to Array
aCatArray.push(cats);
}
I would appreciate any help from you guys. Maybe if you can push me in the right direction. Thank you in advance!
It looks like the cats variable holds the object that is currently falling?
In that case you'd do something like this:
private function stackingHandler():void
{
for (var i:int = 0; i < aCatArray.length; i++)
{
if(cats.hitTestObject(aCatArray[i])) {
// collision detected!
// kill the Tween
// set the y position of the `cats` object
// so it appears on top of the object it collided with (`aCatArray[i]`)
// (it may have moved slightly past the object before doing this check)
}
}
}
So you're looping through the array and hit testing cats against every object in the array one at a time.
It might make more sense to use a basic gravity simulation, or just linearly increasing the y value instead of using a Tween, but you didn't ask about that.
You might also want to set a flag for whether or not an object is currently falling and use that to determine whether or not to run the stackingHandler. Otherwise, you'll just be continually hit testing all the objects when nothing is moving.
This is how I was able to fix it. Creating a double for loop. Checking if they are equal to each other continue and check for hitTest:
private function stackingHandler():void
{
for (var i:int = 0; i < aCatArray.length; i++)
{
var currentCat:mcCats = aCatArray[i];
for (var j:int = 0; j < aCatArray.length; j++)
{
var newCat:mcCats = aCatArray[j];
if (currentCat == newCat) continue;
//Hit Test between Objects
if (newCat.hitTestObject(currentCat.mcHit) && newCat.bFlag == false)
{
//Stop Moving
newCat.stopMoving();
trace("HIT");
if (highScore == 0)
{
addCats();
trace("ADD CATS 1");
}else
{
TweenLite.delayedCall(0.6, addCats);
trace("ADD CATS 2");
}
//Add Points
highScore ++;
trace(highScore + " Score");
//Set Flag boolean
newCat.bFlag = true
}
}
}
}
Related
im very new on javascript, i was doing a ship gaming that you have to kill some asteroids, and when you take some differents of "objects" on the screen we expand our number of bullets. Okey, going to the point i could get 3 bullets on the screen when you take diffirents objects, but now i want to give 2 of that 3 bullets of the array different directions. When i tried, i have the problem that i give the 3 bullets the same direction, i know why but im for at least 5hrs trying to fix this and i cant.
Im programming on Flash Builder 4.7 with different classes, i ll give the code of the array who is in the main, and the bullet class so as the hero class too.
Main Array
public function evUpdateBullet():void//here execute update of my class Bullets
{
var i:int;
for(i=myBullets.length-1; i >= 0; i--)
{
if(myBullets != null) //to be ?
{
if(myBullets[i].isDestroyed) //is destroyed Bullets?
{
myBullets[i] = null;
myBullets.splice(i, 1); //deleted elements.
}else
{
myBullets[i].evUpdate();
}
}
}
}
here i push the array and create the bullet, remember myBullets is the name of the array.
public function evShoot(posX:int, posY:int):void//here create the bullet and push in the array
{
attack1 = new Bullet;
attack1.Spawn(posX, posY);
myBullets.push(attack1);
}
here i show the Hero code, where i define the position of the bullet is going to spawn on the screen.
if (isPressing_Shoot && !isDestroyed)// Here execute the event shoot without power
{
Main.instace.evShoot(model.x, model.y);
isPressing_Shoot = false;
canShoot = false;
}
evDestroyed();
}
here is the code from Bullet class
first the spawn
public function Spawn(posX:int, posY:int):void
{
isDestroyed = false;//first parameter of my bullet
model = new MCbullet;
Main.layer1.addChild(model);//painting the hero in the stage
model.x = posX;//position in the stage wiht the hero
model.y = posY;
model.tigger.visible = false;
}
then the Update
public function evUpdate():void//here conect with update general
{
if (model != null)//to be?
{
model.y -= 12;//move of my bullet
//model.x -= 12;
if (model.y <= 0 )
{
evDestroyed();
}
}
}
in this update i set the movement of y, so i can shoot vertically, but.. when i try to add an x.move, i do for the all array, so i want to know how i can give different move, for differents bullets of the same array.
Iterate through the array elements. There are a few ways to do this, but the one I'm most accustomed to using would be the for loop. It looks like this:
// loop through myBullets array to update
// each x and y position dependent on unique
// property value _xSpeed and _ySpeed.
for (var i:int = 0; i < myBullets.length; i++)
{
myBullets[i].x += myBullets[i]._xSpeed;
myBullets[i].y += myBullets[i]._ySpeed;
}
Obviously, you will need the _xSpeed and _ySpeed properties of the array elements to be set to dynamic values. You would first need to give the bullet class these properties and then set their values when you instantiate the bullets. That might look something like this:
function makeBullet():void{
var b:Bullet = new Bullet();
b.x = hero.x;
b.y = hero.y;
b._xSpeed = hero._xSpeed; // or put here whatever makes sense for assigning the right value in your application
And in your bullet class constructor, before the function but inside the class brackets, add the property:
var _xSpeed:Number = 0;
var _ySpeed:Number = 0;
Basically this is allowing each bullet to hold it's own special property that is independent of any other instance of the class.
I hope that helps.
I'm going crazy with this. I have a few bears on the stage and have listed them in an array. I want them to change their animation frame when clicked as long as they are not on the "down" animation frame to begin with. Below is the code. The problem occurs below the //check if they get hit line.
//put the bears in an array
var bearsArray: Array = new Array();
for (var i=0; i<numChildren;i++) {
if (getChildAt (i) is bear_mc) {
bearsArray.push(getChildAt(i));
}
}
//move them up and down
addEventListener(Event.ENTER_FRAME,upAndDown);
function upAndDown(event:Event){
if (Math.random() < 0.02){
var randomBear = Math.floor(Math.random() * 9);
bearsArray[randomBear].gotoAndPlay("popup");
}
}
//check if they get hit
for (var j:int = 0; j < bearsArray.length; j++){
bearsArray[j].addEventListener(MouseEvent.CLICK, hitBears);
}
function hitBears(e:MouseEvent){
for (var k: int=0; k<numChildren; k++){
if (bearsArray[k].currentFrame != "down"){
trace("clicked");
bearsArray[k].gotoAndPlay("surprised");
}
}
}
currentFrame returns an integer, not string. If you want to use string, you have to use currentFrameLabel or currentLabel.
EDIT: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/MovieClip.html#currentLabel
If you want to control only one bear, remember that you don't need to cycle through them (actually that would serve no purpose as you would be checking every one of them). The better (and correct) approach would be this:
function hitBears(e:MouseEvent){
var bear:MovieClip = e.currentTarget as MovieClip;
if(bear.currentLabel != "down") { //or currentFrameLabel, depends how your mc is set up
trace("clicked");
bear.gotoAndPlay("surprised");
}
}
I got it working by checking a hittest with the mouse instead of just checking the click. That works for some reason.
I'm sorry if it's asking for too much, but I'm too confused by now. I'm making this really simple shooter game for my nephew in AS3. It all seems to be working just fine, except for one really annoying error that keeps popping up every second or third time the game is launched.
IT is Error #1009: Cannot access a property or method of a null object reference. The problem is always with parent.removeChild(this) command in the relevant class (EnemyClass, BulletClass or MissileClass). This happens in two cases: either when checkFinishConditions method in Main is called and the EnemyClass instance needs to be deleted. So if I get the #1009 error does this mean the instance has already been deleted? The second situation is when inst.hitTestObject(enemyInstance) is checked in Main class. Does this mean the EnemyClass instance has already been deleted somehow? I'm totally lost here to be honest.
private function checkCollision():void
{
//loop through missiles
for (var i:int = 0; i < aMissileArray.length; i++) {
//get the current missile
var currentMissile:missileClass = aMissileArray[i];
//loop through enemies
for (var j:int = 0; j < aEnemyArray.length; j++) {
var thisEnemy:EnemyClass = aEnemyArray[j];
if (currentMissile.hitTestObject(thisEnemy)) {
var thisExplode:ExplosionClass = new ExplosionClass(thisEnemy.x,thisEnemy.y);
addChild(thisExplode);
currentMissile.destroyThis();
aMissileArray.splice(i,1);
thisEnemy.deleteEnemy();
aEnemyArray.splice(j, 1);
aDamageArray.splice(j, 1);
scoreValueText += 1;
j--;
i--;
}
//break;
}
}
//loop through bullets
for (var l:int = 0; l < aBulletArray.length; l++) {
//get the current missile
var currentBullet:BulletClass = aBulletArray[l];
//loop through enemies
for (var k:int = 0; k < aEnemyArray.length; k++) {
var currentEnemy:EnemyClass = aEnemyArray[k];
if (currentBullet.hitTestObject(currentEnemy)) {
currentBullet.destroyThis();
aBulletArray.splice(l, 1);
aDamageArray[k] -= 1;
l--;
if (aDamageArray[k] < 1) {
//create an explosion
var thisBulletExplode:ExplosionClass = new ExplosionClass(currentEnemy.x,currentEnemy.y);
addChild(thisBulletExplode);
currentEnemy.deleteEnemy();
aEnemyArray.splice(k, 1);
aDamageArray.splice(k, 1);
scoreValueText += 1;
k--;
}
break;
}
}
}
}
There is alot of code to look over, but one potential issue I see is here :
for each(var currentDieEnemy:EnemyClass in aEnemyArray) {
aEnemyArray.splice(0, 1);
currentDieEnemy.deleteEnemy();
}
The potential issue is that you are 'assuming' that the order of this loop is sequential based on the actual index order of the Array. You might want to stick a trace in there to verify that is actually what is happening, because I believe it's possible that they can be out of order.
See this question for details -- For-Each Loop AS3: Is the direction guaranteed?
So imagine the scenario where the 3rd item in the array is first and you splice the item at the 0 index. Now, you have an item in the array that is removed from the display list, but not from the array. So what happens when you get to that item ? Pretty much what you are describing will happen since the parent property is null, since you already removed it from the display list.
The way to fix that is to do something like this :
while (aEnemyArray.length > 0)
{
var currentDieEnemy:EnemyClass = aEnemyArray[0];
currentDieEnemy.deleteEnemy();
aEnemyArray.splice(0,1);
}
I found another issue in this block of code :
for (var j:int = 0; j < aEnemyArray.length; j++) {
var thisEnemy:EnemyClass = aEnemyArray[j];
if (currentMissile.hitTestObject(thisEnemy)) {
var thisExplode:ExplosionClass = new ExplosionClass(thisEnemy.x,thisEnemy.y);
addChild(thisExplode);
currentMissile.destroyThis();
aMissileArray.splice(i,1);
thisEnemy.deleteEnemy();
aEnemyArray.splice(j, 1); // now array composition is different.
aDamageArray.splice(j, 1);
scoreValueText += 1;
}
}
When you loop through an Array and splice an item at the loop index, you have to realize what happens to the composition of the array. Here's an example
Suppose enemy3 needs to be spliced because of a collision. Given your code, this is what happens :
Before (j == 2)
(0) enemy1
(1) enemy2
(2) enemy3
(3) enemy4
(4) enemy5
AFTER
(0) enemy1
(1) enemy2
(2) enemy4
(3) enemy5
So when the loop continues, j will now increment and equal 3.
The result is that enemy4 doesn't get evaluated as the composition of the array collapses to fill the hole created by the splice and you didn't adjust your loop index variable.
So, what you can do in that situation is simply decrement the loop index variable at the end of the block like so :
aEnemyArray.splice(j, 1); // now array composition is different.
aDamageArray.splice(j, 1);
scoreValueText += 1;
j--; // decrement loop index variable.
So while this is one solution, the main point to take away here is the change in the composition of your Array after a splice.
Yep, you are splicing your objects from arrays in a weird way. First, you are traversing the array forwards and splicing in the loop. This can lead to issues of several bullets not trigger collisions when they should, or say not move. See, if you are running your loop for bullets at index i, once you call bullets.splice(i,1); the bullet that was to be iterated next becomes at position i, then you increment i and that bullet remains unprocessed.
Next, you have nested loops, and in the inner loop you are removing an object from OUTER loop. This means once you did a removal of the outer loop's object, your inner loop is now invalidated, you have to preemptively terminate inner loop.
//loop through bullets
for (var l:int = aBulletArray.length-1; l>=0; l--) {
// first, traverse both arrays backwards
//get the current missile
var currentBullet:BulletClass = aBulletArray[l];
//loop through enemies
for (var k:int = aEnemyArray.length-1; k>=0; k--) {
var currentEnemy:EnemyClass = aEnemyArray[k];
if (currentBullet.hitTestObject(currentEnemy)) {
currentBullet.destroyThis();
aBulletArray.splice(l, 1);
aDamageArray[k] -= 1;
if (aDamageArray[k] < 1) {
//create an explosion
var thisExplode:ExplosionClass = new ExplosionClass(currentEnemy.x,currentEnemy.y);
addChild(thisExplode);
currentEnemy.deleteEnemy();
aEnemyArray.splice(k, 1);
aDamageArray.splice(k, 1);
scoreValueText += 1;
}
// and since we don't have "currentBullet" anymore, do this
break;
}
}
}
Fix all the other iterations through your arrays where you do splicing, like I did in this part of code, and you should avoid 1009 errors in these loops.
Also, you should not post your entire project code, but instead post only relevant parts, say an entire function that's reported as throwing an error, and explain which line is producing the error - it's written as number in the error's stack trace.
I have an issue here. Every five seconds the spawnEnemies is fired. It creates a movieclip, sets it position and adds it to the stage and an array. In the function checkBullet I check if the bullet hits one of the enemies. If it does, the bullet and the enemy are removed from the stage, and the enemy is spliced from the array. But an error is thrown once I hit an enemy:
TypeError: Error #1010: A term is undefined and has no properties.
If I comment out the arrayEnemies.splice(i,1) line, it works fine but then it isn't removed from the array. This is in itself not an issue, but naturally I don't like to keep unnecessary data in my array. Any help on this?
function checkBullet(event:Event) {
if(stage.contains(mcBullet)) {
for(var i:int = 0; i < arrayEnemies.length; i++) {
if(arrayEnemies[i].hitTestPoint(mcBullet.x, mcBullet.y, true)) {
stage.removeChild(mcBullet);
this.removeChild(arrayEnemies[i]);
arrayEnemies.splice(i,1);
bulletOnStage = false;
}
}
if(mcBullet.x > 800) {
stage.removeChild(mcBullet);
bulletOnStage = false;
}
}
}
function spawnEnemies(event:TimerEvent) {
var enemie:MovieClip = new box_mc();
enemie.name = "mcBox" + event.currentTarget.currentCount;
enemie.x = 850;
enemie.y = Math.floor(Math.random()*(1+155)+255);
addChild(enemie);
arrayEnemies.push(enemie);
}
function moveEnemies(event:Event) {
for(var i:int = 0; i < arrayEnemies.length; i++) {
arrayEnemies[i].x -= 5;
}
}
This will be caused by working on an array that you are interating through, you should hold a ref to the stuff you want to remove then do it after the loop.
Your problem is that if the bullet hits two enemies, you try to remove it from the stage twice. This will throw an ArgumentError.
If you need to test against all enemies (assuming multiple enemies can be hit by the same bullet), you also need to decrement i when you remove an element from your enemy array.
function checkBullet(event:Event) {
if(stage.contains(mcBullet)) {
if(mcBullet.x > 800) {
stage.removeChild(mcBullet);
bulletOnStage = false;
}
for(var i:int = 0; i < arrayEnemies.length; i++) {
if(arrayEnemies[i].hitTestPoint(mcBullet.x, mcBullet.y, true)) {
if(stage.contains(mcBullet)){
stage.removeChild(mcBullet);
}
this.removeChild(arrayEnemies[i]);
arrayEnemies.splice(i,1);
bulletOnStage = false;
i--;
}
}
}
}
Bit of an older question but thought I'd throw in my answer too for anyone that might end up here.
Like Neil said, editing an array that you're itterating through (in this case arrayEnemies) can cause concurrent update problems.
My prefered solution is to use a seperate toRemove array and remove them after the update, for example:
var enemies:Array();
//Lets assume at some point this is populated with Enemy objects
function update():void
{
var toRemove:Array = new Array();
//May want to keep and clear an array instead to avoid creating a new one each update
foreach(var enemy:Enemy in enemies)
{
enemy.update();
if(enemy.markedForRemoval())
toRemove.push(enemy);
}
foreach(var enemy:Enemy in toRemove)
enemies.splice(enemies.indexOf(enemy), 1);
}
I've got an array of sprites which I'm animating by incrementing their rotationX property. What I want is for them all to disappear once the last item in the array has come full circle. The problem is that their rotation speeds are being generated by a randomized function, so I can't just go to the end of the array to find the last one. Each time it will be a different one.
So I have an array of Sprites:
for(var i:int=0; i<arrSprites.length; i++)
{
addChild(arrSprites[i]) ;
}
Then I have my event listener:
addEventListener(Event.ENTER_FRAME, loop);
And my handler:
private function loop(e:Event):void
{
for(var i:int=0; i<arrSprites.length; i++)
{
var currentSprite:Sprite = arrSprites[i];
if(currentSprite.rotationX < 361) //this will detect the first one
//to finish but I want the last
{
currentSprite.rotationX += arrSprites[i].speed; //random speed
}
else
{
deleteTheSprites(); //removes all sprites and does other stuff
}
}
}
There's got to be an elegant way to do this. Anyone know what it is?
Thanks,
David
private function loop(e:Event):void
{
var finished : int = 0; // will count the number of sprites finished
for each (var current:Sprite in arrSprites)
{
if (current.rotationX < 361) current.rotationX += current.speed;
else if (++finished == arrSprites.length) deleteTheSprites(); // executes only if all sprites have finished
}
}