Clearing tiles in tile based as3 game - actionscript-3

I been searching for a way to handle this for hours but have found nothing. In my BuildMap function, I instantiate new MovieClips (Tile) with the instance name cell. Their frame is based on my 2d array (protoMap). The problem is that the cells are instantiated in the function. I don't know how to access them outside of it. I want to removeChild(cell) but the only way I know how is within the function that it's instantiated in.
public function BuildMap()
{
for (var i:int=0; i < mapHeight; i++)
{
for (var u:int=0; u < mapWidth; u++)
{
var cell:MovieClip = new Tile();
cell.gotoAndStop(protoMap[i][u]+1);
cell.x = tileSide*u;
cell.y = tileSide*i;
addChild(cell);
var currCellLabel:String = cell.currentFrameLabel;
mapLabels[i].push(currCellLabel);
}
}
}
I want a function called ClearMap() that loops through again and does removeChild(cell). I thought about doing a clearTiles:Boolean and in BuildMap() do
if(clearTiles == false)
{
build the map;
}else{loop again and removeChild(cell)}
but that didn't work... so then I tried to pass cell as an argument to BuildMap() but when I tried to remove it, it wasn't an object of the caller... or something like that. I was also thinking to put cell into its own array, but I don't want to waste memory unnecessarily. Any solutions for the noob?

Create a class instead of using functions.
The point of a class is to isolate concerns.
In your case, you want to deal with all those tiles. (create them according to protoMap and be able to delete them all.
Extending a DisplayObjectContainer such as Sprite, will allow you to add all the tiles to the map object, then you can add and remove the map easily.
Your BuildMap function becomes the constructor.
Here's some non working pseudo code that illustrates the idea:
package
{
public class Map extends Sprite
{
public function Map (data, width, height)
{
for (var i:int=0; i < height; i++)
{
for (var u:int=0; u < width; u++)
{
var cell:MovieClip = new Tile();
cell.gotoAndStop(data[i][u]+1);
cell.x = tileSide*u;
cell.y = tileSide*i;
addChild(cell);
var currCellLabel:String = cell.currentFrameLabel;
mapLabels[i].push(currCellLabel);
}
}
}
public function clear():void
{
removeChildren();
}
}
}
The whole map is contained in that class. You'd have to add the labels, but you didn't specify what they are or what they do, so I left them out.
You can use it like so:
var awesomeMap:Map = new Map(protoMap, mapWidth, mapHeight);
addChild(awesomeMap);
//later
awesomeMap.clear();
//or
removeChild(awesomeMap);
I'd like to point out that building tile maps with MovieClips this way is a bad idea. MovieClips are horrible for performance, because they carry the overhead of their timeline.
Removing all children is very wasteful, too.
So if you have performance problems, try reusing objects and/or employing a technique called "blitting"
See this article: http://www.adobe.com/devnet/flash/articles/blitting_mc.html

Related

Why am I unable to access the methods of an object via is ObjectContainer

first of all, i'm not a native english speaker but, still, i'll try my best to be understandable and as clear as possible.
So, in my programming class, I need to make a Tile based game (like zelda, for exemple) with animate cc (flash). On a map, I want to make a dance floor with tiles that changes on the rhythm of a music. these tiles are movieclip with two frame, one white and one red.
This is how the tiles are generated:
private function createGrid(): void {
grid = new MovieClip();
addChild(grid);
for (var r: int = 0; r < nbRow; r++) {
for (var c: int = 0; c < nbCol; c++) {
var t: Tiles = new Tiles();
t.x = t.width * c;
t.y = t.height * r;
grid.addChild(t);
}
}
grid.x = 15; //center the grid on x
grid.y = 35; //center the grid on y
}
This is the Tiles Class :
package {
import flash.display.MovieClip;
import flash.events.*;
public class Tiles extends MovieClip {
private var rand:int;
public function Tiles() {
// constructor code
getTiles();
}
public function getTiles():void {
random();
setColor();
}
private function random() : void{
rand = Math.floor(Math.random()*100)+1;
}
private function setColor() : void{
if(rand<=30){
gotoAndStop(8); //red frame
}else{
gotoAndStop(7); //white frame
}
}
}
}
createGrid() place the tiles as soon as the map is placed on the stage and stock every tiles in the MovieClip grid. Now, I want the tiles to change randomly between red and white on the beat of a streamed music (and keep the ratio of 30% red tiles and 70% white tiles)
var s: Sound = new Sound();
var sc: SoundChannel;
s.load(new URLRequest("GameSong_mixdown.mp3"));
sc = s.play(0, 1000);
I know i need the leftpeek properties of my soundchannel to achieve that but,for now, I do my test with a button that trigger this function:
private function setTiles(e: Event): void {
// loop through all child element of a movieclip
for (var i: int = 0; i < grid.numChildren; i++) {
grid.getChildAt(i).getTiles();
}
}
Right now, the problem is : I'm unable to acces my Tiles method. I did a trace on grid,getChildAt(i), and saw all instances of my tiles in the console. So, i know for sure that every instances of my tiles are stored in grid. But, I don't know why, grid.getChildAt(i).getTiles(); doesn't work (and every other method from Tiles). The error message is: Call to a possibly udefined method getTiles through a reference with static type flash.display:DisplayObject
Does someone know what i'm doing wrong ?
ps: I translated all my class name, var name, etc from french to
english to make the code clearer.
Your mistake is that getChildAt(...) method has a return type of DisplayObject which is neither dynamic (will not let you access random properties) nor it have DisplayObject.getTiles() method.
All you need is to tell the program that this object is actually of Tiles class:
private function setTiles(e:Event):void
{
// loop through all child element of a movieclip
for (var i: int = 0; i < grid.numChildren; i++)
{
// Cast display objects to Tiles class.
var aTiles:Tiles = grid.getChildAt(i) as Tiles;
// Call the method.
aTiles.getTiles();
}
}

How to give differents directions of bullets on one array?

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.

Creating a vector array of movie clips AS3

I have several movie clips on the stage of my main .fla named btn1-btn7 which will act as buttons. I have a class file named Functions.as where an event listener is created when a button is clicked. onButtonClicked is just going to a frame on the timeline.
obj.addEventListener(MouseEvent.CLICK, onButtonClicked);
I would like the ability to set the buttonMode, visibility, etc. of all of the buttons simultaneously. I have been looking into this for a few hours and am not able to find any solutions. I am now looking into adding them to a vector (which is a new concept for me), but I am not sure how to go about executing this properly. This is what I have so far.
public var buttons:Vector.<MovieClip > = new Vector.<MovieClip > ();
function addButtons()
{
buttons.push(btn1,btn2,btn3,btn4,btn5,btn6,btn7);
for (var i:int; i<buttons.length; i++)
{
trace(buttons[i].name);
}
}
How would I go about, for example, adding the event listener to all of the objects? I will also be setting the buttonMode to true, and making them all invisible simultaneously. I don't even know if it's possible to accomplish this. Thank you in advance for any suggestions.
I'm going to asume that you use timeline code, and have instances of the buttons already placed on the stage. So, first, create the vector:
var _btns:Vector.<MovieClip> = new Vector.<MovieClip>;
_btns.push(btn1,btn2,btn43....) //add all the buttons
Than, you can init the properties of all the buttons:
var _mc:MovieClip;//helper var
for(var i:int=0,i<_btns.length;i++)
{
_mc = _btns[i];
_mc.visible = false;
_mc.buttonMode = true;
_mc.addEventListener(MouseEvent.CLICK, onClick);
}
Then, the event handler:
function onClick(e:MouseEvent):void
{
for(var i:int=0,i<_btns.length;i++)//reset all the buttons
{
_btns[i].visible = false;
}
_mc = MovieClip(e.eventTarget);
_mc.visible = true; //make visible the clicked one
}
You just need to do what you are doing with the .name property in your example code. You need to loop thru every single button in your array (or vector, if you prefer). Here is an example how to set the property of buttonMode:
function setButtonMode(b:Boolean):void {
for(var i:int=0; i<buttons.length; i++) {
var btn:MovieClip = buttons[i]; //store the current reference in a var for faster access
btn.buttonMode = b;
btn.mouseChildren = !b;
}
}

counting the number of instances of a particular class on stage in as3

Does some one know how to get the number of instances of a particular class (in my case Ball.as) currently on the stage.
Note: I want a solution to not include the use of numChildren and then looping through all children, as I want the number of Balls every enterFrame and looping like that might just make my code more heavier.
So any suggestions?
You can use a static variable within the class to keep track, but that you means you have to correctly keep track at this variable at all times, or it could end up with the wrong count.
public static var count:int = 0;
public Ball() {
addEventListener(Event.ADDED, onAdded);
addEventListener(Event.REMOVED, onRemoved);
}
private function onAdded(event:Event):void {
removeEventListener(Event.ADDED, onAddeed);
Ball.count++;
}
private function onRemoved(event:Event):void{
removeEventListener(Event.REMOVED, onRemoved);
Ball.count--;
}
and then whenever you want to count them:
trace(Ball.count);
a) Count them
var balls:uint = 0;
for(var i:uint=0,l:uint = numChildren; i<l;i++)
if(getChildAt(i) is Ball) balls++;
b) Keep track of them
var balls:uint = 0;
function addBall():DisplayObject {
balls++;
return addChild(new Ball());
}
function removeBall(ball:Ball):DisplayObject {
balls--;
return removeChild(ball);
}
c) Isolate them
var ballContainer:Sprite = new Sprite();
addChild(ballContainer);
...
ballContainer.addChild(new Ball());
var balls:uint = ballContainer.numChildren;
I don't think there would be a way to get around doing the loop.
When I have an Actionscript project of any size I will create a static Manager class that will handle all app wide variables. You could create an Array in there and add/remove from that when a new instance of ball is created.

Add multiple movieclips, not replacing the old ones

So, in short, my problem is this. I am using a variable which is a movieclip loaded from an external swf. I want to "spawn" multiple instances of the movieclip that all react to the same code, so for example if I say var1.x = 100, they all are at 100x. But my problem is when I run addChild(var1) multiple times(I'm not actually typing in addChild(var1) over and over, I just have it set to add them at random times), the new child just replaces the old one, instead of making multiple movieclips. Should I do something like
var var1:MovieClip
var var2:MovieClip = new var1 ?(which doesnt work for me btw, gives me errors)
Oh, heres the code, and also, I am pretty new to as3 fyi, still don't even know how arrays work, which was my second guess to the problem.
var zombieExt:MovieClip;
var ldr2:Loader = new Loader();
ldr2.contentLoaderInfo.addEventListener(Event.COMPLETE, swfLoaded2);
ldr2.load(new URLRequest("ZombieSource.swf"));
function swfLoaded2(event:Event):void
{
zombieExt = MovieClip(ldr2.contentLoaderInfo.content);
ldr2.contentLoaderInfo.removeEventListener(Event.COMPLETE, swfLoaded2);
//zombieExt.addEventListener(Event.ENTER_FRAME, moveZombie)
zombieExt.addEventListener(Event.ENTER_FRAME,rotate2);
function rotate2 (event:Event)
{
var the2X:int = playerExt.x - zombieExt.x;
var the2Y:int = (playerExt.y - zombieExt.y) * 1;
var angle = Math.atan(the2Y/the2X)/(Math.PI/180);
if (the2X<0) {
angle += 180;
}
if (the2X>=0 && the2Y<0) {
angle += 360;
}
//angletext.text = angle;
zombieExt.rotation = (angle*1) + 90;
}
playerExt.addEventListener(Event.ENTER_FRAME,spawn1);
function spawn1 (event:Event)
{
if(playerExt.y < 417)
{
var someNum:Number = Math.round(Math.random()*20);
if(someNum == 20)
{
addChild(zombieExt)
zombieExt.x = Math.round(Math.random()*100)
zombieExt.y = Math.round(Math.random()*100)
}
}
}
}
addChild() does not create new instances. It is used to add an already created instance to the display list. If you call addChild() multiple times on the same instance then you are just readding itself.
Also each instance is unique, you can not globally change the x position of an instance by changing another one of them. What you would do is as Henry suggests and add each new instance of a MovieClip into an array, then whenever you change something you can loop through the array and apply the changes to each instance.
You can not go var2:MovieClip = new var1 either since var1 is an instance and not a class.
Here's a different method of receiving loaded MovieClips, which i use when i need many copies of the item.
in the swf you are loading, give the target movieclip a linkage name in the library, for this example i will use "foo"
private var loadedSwfClass:Class
private var newZombie:MovieClip;
private var zombieArray:Array = new Array();
function swfLoaded2(event:Event):void
{
loadedSwfClass = event.target.applicationDomain.getDefinition("foo");
for(var n:int = 0; n<100; n++){
newZombie = new loadedSwfClass()
zombieArray.push(newZombie);
addChild(newZombie);
}
}
as per this tutorial
http://darylteo.com/blog/2007/11/16/abstracting-assets-from-actionscript-in-as30-asset-libraries/
although the comments say that
var dClip:MovieClip = this;
var new_mc = new dClip.constructor();
this.addChild(new_mc);
will also work.
It sounds like you might be accessing the same instance some how in your code. It would be helpful to see your code to figure this one out.
If I wanted to load in one swf files and add a MovieClip multiple times I would place it in the library of that SWF file. And then instantiate it and store it into an object pool or a hash or some list.
// after the library were finished loading
var list:Array = [];
for(var i:int=0; i<10; i++) {
var myCreation:MySpecialThing = new MySpecialThing();
addChild(myCreation);
list.push(myCreation);
}
where my library would contain a linkage to the class MySpecialThing.
Calling addChild(var1) multiple times on the same parent doesn't have any effect (unless you have added another child to the same parent in between, in which case it will change the child index and bring var1 to the top). If you call it on different parents, it will just change the parent of var1, doesn't duplicate. Call addChild(new MovieClassName()) at random times instead to add new copies of it. Use an array as suggested here to access them later.
Wow, thanks there henry, just using an array did exactly what I needed, and made things alot simpler.
when you load in using a loader you only get 1 instance, however you can do some funky reflection to determine what class type the given loader.content is, and then instantiate them using that. For Example:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loader_completeHandler);
loader.load(new URLRequest("ZombieSource.swf"));
var classType:Class;
function loader_completeHandler(evt:Event):void
{
var loadInfo:LoaderInfo = (evt.target as LoaderInfo);
var loadedInstance:DisplayObject = loadInfo.content;
// getQualifiedClassName() is a top-level function, like trace()
var nameStr:String = getQualifiedClassName(loadedInstance);
if( loadInfo.applicationDomain.hasDefinition(nameStr) )
{
classType = loadInfo.applicationDomain.getDefinition(nameStr) as Class;
init();
}
else
{
//could not extract the class
}
}
function init():void
{
// to make a new instance of the ZombieMovie object, you create it
// directly from the classType variable
var i:int = 0;
while(i < 10)
{
var newZombie:DisplayObject = new classType();
// your code here
newZombie.x = stage.stageWidth * Math.random();
newZombie.x = stage.stageHeight * Math.random();
i++;
}
}
Any problems let me know, hope this helps.