push objects that equal true, into an array - actionscript-3

I have an enemy hitting a bunch of tiles, and through a loop, I check all the tiles if the enemy is hitting them. I trace them and some will tell me if it's true or false. I want to be able to get the enemy to randomly choose from those that are true, and go on those tiles. It traces all the tiles that are in contact with the enemy, problem is, I'm not sure how I can get those tiles that are registering as true, into its own array, then have the enemy move randomly into those tiles.
for (var j:int = 0; j < tileset.length; j++){
trace(tileset[j].currentFrameLabel, tileset[j].hitTestObject(enemy));
if (tileset[j].hitTestObject(enemy) && !tileset[j].hitTestObject(player)){
options.push(Boolean(true));
}
EDIT: here's my timer function, where every 5 second, I want the enemy to move to an available tile. Although you can't see tileset, it is an array that is equal to the movieclip that is a tile, which is in a for loop itself. So basically, tileset is 49 movie clips of tile. I have those available tiles pushed into another array which is options. Then I make a var called enemyPick which would be the counter. That's how far I am.
function timerenemy (event:TimerEvent) {
var options:Array = [];
for (var j:int = 0; j < tileset.length; j++){
if (tileset[j].hitTestObject(enemy) && ! tileset[j].tileMiddle.hitTestObject(player)) {
//trace(tileset[j].currentFrameLabel, tileset[j].hitTestObject(enemy));
tileset[j].outline.gotoAndStop("attack");
options.push(tileset[j]);
}
if (options.length > 0){
var enemyPick:int = Math.floor(Math.random()*options.length)
}
}
trace(enemyPick, options);
}

First, create an array who's length is the maximum number of acceptable tiles. For instance, in a straight line of tiles, only two can possibly be available at a time. Now, each time you need to have the enemy move tiles, initialize a counter to 0. Then, loop through each tile, and 'ask' if it is touching the enemy. If it its, record it in array[counter], and increment counter. After the loop, pick a random number from 0 to (counter) and use array[random number] as the tile that the enemy moves to.

Related

Actionscript 3.0 Splice/RemoveChild issue

I have rocks and enemy ninjas moving off the screen. The ninjas are supposed to disappear after being hit by a shuriken once, however they are taking two hits to disappear, although the shurikens disappear every time fine. The code is nearly identical for the shurikens and enemy ninjas, however the ninjas don't seem to work properly. I also get the occasional ninja getting stuck somewhere on the screen and shurikens pass through it.
//If a rock moves off the screen, it is spliced.
if (rock[j].x <= -301){
removeChild(rock[j]);
rock.splice(j, 1);
rockNum--;
}
}
for (var q=shurikens.length - 1; q >= 0; q--){
for (var w=enemy.length - 1; w >= 0; w--){
//If the shurikens hit the ninjas.
if ((shurikens[q]).hitTestObject(enemy[w])){
removeChild(enemy[w]);
enemy.splice(w, 1);
ninjaNum--;
removeChild(shurikens[q]);
shurikens.splice(q, 1);
break;
}
}
}
}
How to fix your code, and give you some performance tips:
This code is very "bug friendly": you are modifying the "length" property of array within a for/in loop that relied on the same property, this is really not a wise thing to do.
The way I would do it:
// Create an array where to store colliding ninjas and shurikens
var depleted:Array = [];
// Create local references to make code more readable and fast (querying arrays all the time is slow).
var ninja:DisplayObject;
var shuriken:DisplayObject;
// Loop in shurikens (no need to check if shurikens have been depleted, since you query only once each of them
for (var q:int=shurikens.length - 1; q >= 0; q--){
shuriken = shurikens[q]; // Assign a shuriken to our variable so you avoid to call further shurikens[q];
for (var w:int=enemy.length - 1; w >= 0; w--){ // Loop in ninjas
ninja = enemy[w]; // Assign ninja
//If the shurikens hit the ninjas. (only if ninjas have not yet been removed)
// This is the core of our improvement, before calling hitTest that is slow, you first check that ninjas have not already been killed
if (depleted.indexOf(ninja) == -1 &&
shuriken.hitTestObject(ninja)){
// It's a hit with a live ninja. I just add both objects to depleted list.
depleted.push(ninja);
depleted.push(shuriken);
break; // Breaking the loop, makes sure shuriken cannot hit 2 ninjas
}
}
}
// Then, you loop in the list of killed ninjas and depleted shurikens, and remove them from arrays and display list
for each (var depletedObj:DisplayObject in depleted) {
// First remove object from the relevant array
if (shurikens.indexOf(depletedObj) != -1) shurikens.splice(shurikens.indexOf(depletedObj), 1); // If it was in the shurikens array remove from there
else if (enemy.indexOf(depletedObj) != -1) enemy.splice(enemy.indexOf(depletedObj), 1); // If it was in the ninjas array remove from there
// The do all necessary stuff to remove object from DisplayList (end eventually add it to an object pooling list)
removeChild(depletedObj);
}
ninjaNum = enemy.length; // Update number of ninjas
Another hint, in case you run this for loops on each frame, if you place ninjas and shurikens in 2 different DisplayObjectContainer, you can first hitTest the 2 large containers, and once they collide, you can run the loops to check fine collisions. Also, in numeric for/in loops, declare variables always as :int. Typing variable makes it faster to access than an untyped variable. You can of course improve this code to make it faster, i.e.: adding a "alive = true" property to shurikens and ninjas, so you do not need to query a third array, etc.

How to Adjust Volume in an Audio loop?

How would someone change sound levels of a music playing in a loop? For example, I'm making a game and at a certain frame I want the music level (music.wav) to be decreased to half of its volume.
How could someone do this in AS3?
You are using the word "loop" in a confusing way. In programming, a loop usually refers to one of the "for" loops that looks like this:
for (var i:int = 0; i < 10; i++)
{
//do stuff 10 times
}
I surmise that this is not what you mean by loop, but rather that you would like a MovieClip or the main timeline to decrease the volume of a Sound object at the end of n frames. Or do you just mean the music itself is looping? Hopefully you see the value of asking well written questions. That being said..
Mind you, I haven't tried this, but according to my reference book (ActionScript 3.0 Cookbook by Lott, Schall & Peters) you need to use a SoundTransform object which specifies the volume at which you want the sound to be set. Try this:
var _sound:Sound = new Sound(music.wav); // creates a Sound object which has no internal volume control
var channel:SoundChannel = _sound.play(); // creates a SoundChannel which has a soundTransform property
var transform:SoundTransform = new SoundTransform(); // SoundTransform objects have a property called "volume". This is what you need to change volume.
Now in your loop (or on the frame event that you are using) do this:
transform.volume *= 0.9; // or whatever factor you want to have it decrease
//transform.volume /= 1.1; // or this if you prefer.
channel.soundTransform = transform; //
So anytime you want the volume to decrease by this incremental amount, run this bit of code. Of course, you need to make sure that any variables you set are accessible in the code that is referencing them. One way that comes to mind to do this is with a function.
private function soundDiminish(st:SoundTransform, c:SoundChannel, factor:Number = 0.9):void
{
st.volume *= factor;
c.soundTransform = st;
}
Now, whenever you want to diminish the volume just call the soundDiminish function.
Maybe your frame event looks like this:
function onLoadFrame(fe:Event):void
{
soundDiminish(transform, channel); // 3rd parameter optional
}
If you only want this function to be called every 20 frames then:
function onLoadFrame(fe:Event):void
{
// this is a counter that will count up each time this frame event happens
frameCount ++;
if (frameCount >= 20)
{
soundDiminish(transform, channel); // 3rd parameter optional
frameCount = 0; // reset the frame counter
}
}

How to check collision for multiple symbols

I was wondering if it was possible to check for collision by the instance name and not separate mc names. I have about 150-200 objects (dots for pacman game) that I need to check collision for and want to do it efficently. Thanks!
If you have instance named dots, and a player, you could do something like this:
//a var to hold each loop iteration's dot for convenience
var tmpDot:DisplayObject;
//loop 200 times from 1 - 200
for(var i:int=1;i<= 200;i++){
//getChildByName gets an instance from a string, in this case dot plus i (i is the current iteration number)
tmpDot = getChildByName("dot" + i);
//check if the dot exists and is hitting the player
if(tmpDot && tmpDot.hitTestObject(player)){
//hit a dot, do something here like remove the dot
removeChild(tmpDot);
//increment points etc.
//if there's no possibility of the player hitting more than one dot at a time, then for efficiency you should break out of this loop
break;
}
}
Now, as mentioned in the comments on your question, giving 200 dots an instance name is tedious. An easier way, would be to take your dot MovieClip in your library, go to it's properties, and export it for actionscript (let's say you gave it a class name of Dot). Then what you could do, is at the start of a level find all the dot objects that you have on your timeline (no instance names needed) and add them to an array:
//DO THIS ONLY WHEN THE LEVEL STARTS
//create a vector/array to store all your dots for better speed
var allDots:Vector.<Dot> = new Vector.<Dot>();
//iterate over all the children of this timeline frame
for(var i:int=0;i<numChildren;i++){
//if the item is a Dot, add it to the array
if(getChildAt(i) is Dot){
allDots.push(getChildAt(i) as Dot);
}
}
Now, you can do the hit tests like this:
//YOU PROBABLY WANT TO DO THIS EITHER EVERY FRAME, OR WHENEVER THE PLAYER MOVES
//flag to see if all dots are eaten
var allEaten:Boolean = true;
var tmpDot:Dot;
for(var i:int=0;i<allDots.length;i++){
tmpDot = allDots[i];
//.... same as the top code example at this point
if(tmpDot && tmpDot.hitTestObject(player)){
removeChild(tmpDot);
//do anything else you need to do when a dot is eaten
//if we've already determined that we haven't eaten all the dots, then break the loop
if(!allEaten) break;
}
//if a dot has a parent, then they haven't been all eaten
if(tmpDot.parent){
allEaten = false;
}
}

issue with addChild's X and Y value

I have an Enemy class that deals with my monster moving and attacking. Within that class, I have another class called enemyMagic, which is a blank movieclip that serves as a masterclass to different movieclips that I will make.
So in the enemyMagic class, I add a movieclip called attack1
public var attack1:Attack1 = new Attack1;
public function EnemyMagic() {
////////////CREATE THE TIMER//////////
masterEnemyAttackTimer.addEventListener(TimerEvent.TIMER, mastertimer);
////////////ATTACKS/////////
//TIER 1//
addChild(attack1);
}
And in the enemy class, I add the enemyMagic when the enemy is attacking a certain position.
for (var k:int = 0; k < Main.tileset.length; k++)
{
if (! Main.tileset[k].tileMiddle.hitTestObject(this.enemyVisionPoint))
{
if (Main.tileset[k].tileHP !== 0)
{
attackoptions.push(Main.tileset[k]);
}
if (Main.tileset[k].tileMiddle.hitTestObject(Main.player.visionPoint))
{
addChild(enemymagic);
Main.tileset[k].outline.gotoAndStop("attack");
this.enemymagic.x = (Main.tileset[k].x);
this.enemymagic.y = (Main.tileset[k].y);
trace(enemymagic.x, enemymagic.y, Main.tileset[k].x, Main.tileset[k].y);
For some reason, the enemymagic is tracing the exact same number as the tile's x and y, but it isn't adding it on the tile. It adds it way off the screen. I think it might be because it starts on the enemy's x and y and then calculates?
So my question is how can I get the enemymagic movie clip to get exactly on the position of the tile?
You can do two things. First, when you do a plain addChild() the base coordinate system of the child is the one of its parent, which is your Enemy instance, which is of course at somewhere nonzero. And then you assign it the coordinates of Main.tileset[k] which has a different parent (most likely instance of Main). This creates the distance you speak of. So, in order to locate your magic over the exact tile, either use this.globalToLocal(Main.tileset[k].localToGlobal(PZERO)) where PZERO is a new Point() constant (or write new Point() instead of PZERO, but this will create another empty Point object and will quickly escalate), or do an addChild() directly to the tile you are attacking with unaltered coordinates.

How to add a movieclip to the stage then make it move down?

I am making a game where i want to spawn zombies then make them move down the screen. I also want to have multiple on the screen at once. I have tried multiple ways now but none of them have worked.
Here is my code
if ((zombie == 1)||(zombie == 3)||(zombie == 5)||(zombie == 7))
{
var Z = new Z;
Z.x = 403.25;
Z.y = -86.9;
Z.rotation = 90;
addChild(Z)
zombie += 1;
}
//Functions
function startzombie(event)
{
trace("start zombies")
zombie = 1;
addEventListener(Event.ENTER_FRAME,zombiemove)
}
function zombiemove(event:Event)
{
Z.y += 1;
}
Z is the zombie
You need an Array that will hold references to your zombie instances, instead of a single variable of class Zombie, whatever it is name. Then, in the part of code that moves zombies, you iterate through that array and advance all zombies in there. Also you need to check if some zombies are already defunct so that you need to remove them from screen and from array, and act accordingly.
var za:Array; // initialize elsewhere
....
function addZombie():void {
var z:Zombie=new Zombie();
z.x=Math.random()*600; // position that zombie
z.y=-20; // somewhere
addChild(z);
za.push(z);
}
function advanceZombies():void {
for (var i:int=za.length-1;i>=0;i--) za[i].walkABit();
}
You call a method in Zombie class specially designed for moving the zombie. They can be say stunned or slowed so that this move might not occur, and it's of no deal to your main class to control each and every zombie's alterable behavior, this is what's named "black box concept". You say "move", they move if they are able.