AS3 removing dynamically created child movieclips - actionscript-3

I'm fairly new to AS3. Anyways, I'm try to remove a dynamically created child movieclip when clicked on. When a dirt block is clicked on, which is a child movieclip of 'world' I want to remove it.
I've tried various ways of removing it using removeChild. I've also tried moving the function inside/outside of the for loop that creates the movieclips.
var blockCount:Number = 0;
var blockArray:Array = [];
var world:MovieClip = new World();
world.x = 50;
world.y = 50;
world.name = "world";
addChild(world);
for(var i:Number=1;i<=100;i++){
blockCount++;
var tempGrassBlock:MovieClip = new GrassBlock();
tempGrassBlock.x = i*16;
tempGrassBlock.y = 256;
tempGrassBlock.name = "b"+blockCount;
world.addChild(tempGrassBlock);
tempGrassBlock.addEventListener(MouseEvent.CLICK, removeBlock);
function removeBlock(event:Event){
world.removeChild(getChildByName(event.target.name));
}
}
Thanks for the help.

Try this
function removeBlock(event:Event){
world.removeChild(event.currentTarget as DisplayObject);
}

No function definition should be inside a for. I changed that in your code and rewrited a little below:
var blockCount:Number = 0;
var blockArray:Array = [];
var world:MovieClip = new World();
world.x = 50;
world.y = 50;
world.name = "world";
addChild(world);
for(var i:Number=1;i<=100;i++){
blockCount++;
var tempGrassBlock:MovieClip = new GrassBlock();
tempGrassBlock.x = i*16;
tempGrassBlock.y = 256;
tempGrassBlock.name = "b"+blockCount;
world.addChild(tempGrassBlock);
tempGrassBlock.addEventListener(MouseEvent.CLICK, removeBlock);
}
function removeBlock(event:MouseEvent){
trace("Is click really working? This target name is " + event.currentTarget.name);
world.removeChild(event.currentTarget));
}

Related

Multiple sprites added in loop, but only last sprite clickable

I have multiple bitmap images added into sprites(each image added into 1 sprite) in a loop, then all the sprites added to 1 _contentHolder(Sprite) then that is added to a viewport.
What the problem is, the multiple sprites that are added inside the loop, everything displays with no problem but only the last sprite added is clickable. None of the sprite added before it is clickable. Wondering what the problem is, they are not overlapping and when i hover the mouse over the top of all the sprites, it turns into the mouse clicker but it just won't click.
Thanks for your time!
My code:
function onImageLoaded(e:Event):void {
loadedArray.push(e.target.content as Bitmap);
for(var i:int = 0; i < loadedArray.length; i++){
var currentY1:int = 200;
var image: Sprite= new Sprite;
e.currentTarget.loader.content.height =200;
e.currentTarget.loader.content.y += currentY1;
image.mouseChildren = true; // ignore children mouseEvents
image.mouseEnabled = true; // enable mouse on the object - normally set to true by default
image.useHandCursor = true; // add hand cursor on mouse over
image.buttonMode = true;
image.addChild(loadedArray[i]);
_contentHolder.addChild(image);
}
newArray.push(image);
var viewport:Viewport = new Viewport();
viewport.y = 0;
viewport.addChild(_contentHolder);
var scroller:TouchScroller = new TouchScroller();
scroller.width = 300;
scroller.height = 265;
scroller.x = 10;
scroller.y = 100;
scroller.viewport = viewport;
addChild(scroller);
image.addEventListener(MouseEvent.CLICK, gotoscene);
}
loadImage();
Edit:
function gotoscene(e: MouseEvent):void{
var index:Number;
index = newArray.indexOf(e.target);
trace(index);
blackBox.graphics.beginFill(0x000000);
blackBox.graphics.drawRect( -1, -1, stage.width, stage.height);
blackBox.alpha = 0.7;
addChild(blackBox);
var originalBitmap : BitmapData = loadedArray[index].bitmapData;
var duplicate:Bitmap = new Bitmap(originalBitmap);
duplicate.width = stage.width;
_contentHolder1.addChild(duplicate);
// Use counter here to only add _contentHolder1 once
//Assuming that `samedata` is a class member (I can't see the rest of your code)
addChild(_contentHolder1);
}
Edit2:
private var image:Array = new Array;
//In the For loop
image[i] = new Sprite();
image[i].addChild(loadedArray[i]);
image[i].addEventListener(MouseEvent.CLICK, gotoscene);
function gotoscene(e:MouseEvent):void{
index = image.indexOf(e.target);
trace(index);
}
You should move image.addEventListener(MouseEvent.CLICK, gotoscene); statement into the loop where you add child sprites. Once you do, the listener will be added to all of the sprites, not just the last one that's currently stored in image variable, and is the only one that responds to your clicks.
for(var i:int = 0; i < loadedArray.length; i++){
var currentY1:int = 200;
var image: Sprite= new Sprite;
e.currentTarget.loader.content.height =200;
e.currentTarget.loader.content.y += currentY1;
image.mouseChildren = true; // ignore children mouseEvents
image.mouseEnabled = true; // enable mouse on the object - normally set to true by default
image.useHandCursor = true; // add hand cursor on mouse over
image.buttonMode = true;
image.addEventListener(MouseEvent.CLICK, gotoscene); // <-- THIS
image.addChild(loadedArray[i]);
_contentHolder.addChild(image);
}
And for all that is holy, learn to indent your code, so that you will be able to visually find the start and end of your loops and see if a certain statement is within the loop or not.
I did work for a few years in AS3 and this was a weird an usual problem. I used to solve it with a function that adds the event to each clip:
function someFunction():void {
for (...) {
var image:Sprite = new Sprite();
addSceneListener(image);
}
}
function addSceneListener(mc:Sprite):void {
mc.addEventListener(MouseEvent.CLICK, gogoscene);
}

How can I access the width of a JPG loaded inside a movieclip in AS3?

i'm trying to reproduce the bookshelf similar the one in this site: http://www.callis.com.br/
The books are PNG files that comes in several different sizes.
What I'm trying to accomplish is, after loading in several different movieclips (book0, book1, book2, ...) read their widths, calculate the gap necessary to push it, and then arrange the x value of each book.
Every time I try to access the width value, it says it's 0.
Here's a portion of my code, that's trying to do the trick.
Thanks in advance.
for (j=0; j < dados.livro.length(); j++){
if(j<8){
targetLivro = MovieClip(MovieClip(root).livros.getChildByName("livro"+(j)));
}else{
targetLivro = MovieClip(MovieClip(root).getChildByName("livro"+(j)));
}
preparaLivro(targetLivro,dados.livro[j].imagem.text(),dados.livro[j].link.text());
//larguraTotalLivrosSup += targetLivro.width;
//trace(larguraTotalLivrosSup);
trace("livro"+j+": ");
trace("título:"+dados.livro[j].titulo.text());
trace("imagem:"+dados.livro[j].imagem.text());
trace("link:"+dados.livro[j].link.text());
}
//organiza a primeira estante
//
/*
for (i=0; i<largurasArray.length; i++){
trace(largurasArray[i]);
}*/
var offset:Number = 20;
for (j=0; j<livros.numChildren; j++){
//targetLivro = MovieClip(livros).getChildByName("livro"+(j));
trace(livros.getChildAt(j).width);
}
function preparaLivro(livro:MovieClip, capa:String, url:String){
var capaLoader:Loader = new Loader();
var capaImageRequest:URLRequest = new URLRequest(capa);
var capaBitmapData:BitmapData;
var capaBitmap:Bitmap;
var capaLargura:Number = 0;
capaLoader.load(capaImageRequest);
capaLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void{
capaBitmapData = new BitmapData(e.target.content.width, e.target.content.height);
capaBitmapData.draw(capaLoader);
capaBitmap = new Bitmap;
capaBitmap.bitmapData = capaBitmapData;
livro.y -= e.target.content.height;
trace(e.target.content.width, e.target.content.height);
livro.buttonMode = true;
livro.addChild(capaBitmap);
livro.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void{
navigateToURL(new URLRequest(url), "_blank");
});
//trace(e.target.content.width);
});
}
Instantiate bitmap from LoaderInfo(event.target).content like:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("http://www.google.com/images/srpr/logo4w.png"));
function onComplete(event:Event):void
{
var bitmapData:BitmapData =
Bitmap(LoaderInfo(event.target).content).bitmapData;
}
Tracing bitmapData.width and bitmapData.height outputs:
width: 550, height: 190
Otherwise, if you want width / height from the loader, access loader's contentLoaderInfo:
var width:Number = loader.contentLoaderInfo.width;
var height:Number = loader.contentLoaderInfo.height;

How to create a series of class instances in a for loop, as3

In my library I have a bunch of classes named tip1, tip2, tip3, tip4...and so on. Is it possible to create one instance of each on the stage using a for loop? I tried this but it didn't seem to work.
var tips:int = 12;
for(var i:int = 1; i<=tips; i++){
var tipName:String = "tip"+i
var tip:MovieClip = new tipName();
tip.name = "tip" + i
tip.x = stage.width;
tip.y = 0;
addChild(tip);
}
Any help would be appreciated. Thanks!
You were missing the "getDefinitionByName" part.
// Up top
import flash.utils.getDefinitionByName;
// Down below
var tips:int = 12;
for (var i:int = 1; i < tips; ++i ) {
var myClass:Class = getDefinitionByName('tip' + i) as Class;
var tip:Object = new myClass();
tip.name = "tip" + i;
....
}
Instead of
var tip:MovieClip = new tipName();
Try (written from memory)
var clazz:Class = getDefinitionByName(tipName) as Class;
var tip:MovieClip = new clazz();
Also, you generally want to use stage.stageWidth instead of stage.width, since the latter will return the stage bounding box width (which might not be the same as the area the swf file covers).

How to pass a variable into an Event.COMPLETE function?

I am running a loop to pull thumbs into a containing movieclip from an xml list. What I want to do is have the thumb's parent movieclip fade in after they are done loading, but I can't figure out how to reference the parent once it's loaded.
My code(which currently doesn't work the way I want it):
var vsThumb:articleBox;
var currentarticleX:Number = 0;
var articleLinkURL:String;
var articleImageURL:String;
var articleText:String;
var vsThumbLoader:Loader;
var next_x:Number;
next_x = 9;
var thumbAlphaTween:Tween;
var articlevsThumb:Array = new Array();
function loadarticleHeadlines():void
{
for (var i:int = 0; i < egarticleXml.articlelist.articleitem.length(); i++)
{
vsThumb = new articleBox();
vsThumb.alpha = 0;
vsThumbLoader = new Loader();
vsThumbLoader.load(new URLRequest(egarticleXml.articlelist.articleitem[i].articlethumbnail));
articleListContainter.addChild(vsThumb);
vsThumb.articleImage.addChild(vsThumbLoader);
vsThumb.articleTitle.text = egarticleXml.articlelist.articleitem[i].articletitle;
titleAutosize(vsThumb.articleTitle);
vsThumb.x = next_x;
next_x += 260;
articlevsThumb[i] = vsThumb;
vsThumbLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, showBox);
vsThumb.clickBtn.buttonMode = true;
}
function showBox(event:Event):void
{
thumbAlphaTween = new Tween(articlevsThumb[i],"alpha",None.easeNone,0,1,.25,true);
}
}
So how do I refer back to the loader's parent so I can fade in the whole movieclip? Can I pass a variable into the showBox function?
Don't use nested functions. They tend to make things more complicated.
i will always have the end value (articleitem.length()-1) in all of the event handlers you create, because its scope is the outer function, loadarticleHeadlines (it will increase by 1 on every iteration). That's probably why your code doesn't work.
The event will be fired on the loaderInfo of your loader, so you can find the loader's parent by using event.target.loader.parent:
function loadarticleHeadlines() : void
{
for (var i:int = 0; i < egarticleXml.articlelist.articleitem.length(); i++)
{
vsThumb = new articleBox();
vsThumb.alpha = 0;
vsThumbLoader = new Loader();
vsThumbLoader.load(new URLRequest(egarticleXml.articlelist.articleitem[i].articlethumbnail));
articleListContainter.addChild(vsThumb);
vsThumb.articleImage.addChild(vsThumbLoader);
vsThumb.articleTitle.text = egarticleXml.articlelist.articleitem[i].articletitle;
titleAutosize(vsThumb.articleTitle);
vsThumb.x = next_x;
next_x += 260;
articlevsThumb[i] = vsThumb;
vsThumbLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, showBox);
vsThumb.clickBtn.buttonMode = true;
}
}
function showBox(event:Event):void
{
thumbAlphaTween = new Tween(event.target.loader.parent,"alpha",None.easeNone,0,1,.25,true);
}
You don't need to pass a variable to your showBox, use the target property of the Event to retrieve the Loader:
function showBox(event:Event):void
{
var li:LoaderInfo=LoaderInfo(event.target);
// be nice remove your listener when your are done
li.removeEventListener(Event.COMPLETE, showBox);
var ldr:Loader=li.loader; // here is your loader
// do whatever you want with loader
thumbAlphaTween = new Tween(articlevsThumb[i],"alpha",None.easeNone,0,1,.25,true);
}

Closure problem? - passing current value of a variable

I'm trying to pass the current value of a variable when an a dynamically generated navigation 'node' is clicked. This needs to just be an integer, but it always results in the last node's value.. have tried some different methods to pass the value, a custom event listener, a setter, but I suspect it's a closure problem.. help would be appreciated ;-)
function callGrid():void {
for (var i:Number = 0; i < my_total; i++) {
var gridnode_url = my_grid[i].#gridnode;
var news_category= my_grid[i].#category;
var newstitle = my_grid[i].#newstitle;
var news_content = my_grid[i]..news_content;
var news_image = my_grid[i]..news_image;
var gridnode_loader = new Loader();
container_mc.addChild(gridnode_loader);
container_mc.mouseChildren = false;
gridnode_loader.load(new URLRequest(gridnode_url));
gridnode_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, gridLoaded);
gridnode_loader.name = i;
text_container_mc = new MovieClip();
text_container_mc.x = 0;
text_container_mc.mouseEnabled = false;
var textY = text_container_mc.y = (my_gridnode_height+18)*y_counter;
addChild(text_container_mc);
var tf:TextSplash=new TextSplash(newstitle,10,0,4 );
container_mc.addChild(tf);
tf.mouseEnabled = false;
tf.height = my_gridnode_height;
text_container_mc.addChild(tf);
var text_container_mc_tween = new Tween(text_container_mc, "alpha", Strong.easeIn, 0,1,0.1, true);
gridnode_loader.x = (my_gridnode_width+5) * x_counter;
gridnode_loader.y = (my_gridnode_height+15) * y_counter;
if (x_counter+1 < columns) {
x_counter++;
} else {
x_counter = 0;
y_counter++;
}
}
}
function gridLoaded(e:Event):void {
var i:uint;
var my_gridnode:Loader = Loader(e.target.loader);
container_mc.addChild(my_gridnode);
_xmlnewstarget = my_gridnode.name;
//||||||||||||||||||||||||||||||||||||||||
//when a particular grid node is clicked I need to send the current _xmlnewstarget value to the LoadNewsContent function...
//||||||||||||| ||||||||||||||||||||||||
my_tweens[Number(my_gridnode.name)]=new Tween(my_gridnode, "alpha", Strong.easeIn, 0,1,0.1, true);
my_gridnode.contentLoaderInfo.removeEventListener(Event.COMPLETE, gridLoaded);
my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
}
function loadNewsContent(e:MouseEvent):void {
createNewsContainer();
getXMLNewsTarget();
news_category = my_grid[_xmlnewstarget].#category;
var tfnews_category:TextSplash=new TextSplash(news_category,20,16,32,false,false,0xffffff );
tfnews_category.mouseEnabled = false;
newstitle = my_grid[_xmlnewstarget].#newstitle;
var tftitle:TextSplash=new TextSplash(newstitle,20,70,24,false,false,0x333333 );
news_container_mc.addChild(tftitle);
tftitle.mouseEnabled = false;
news_content = my_grid[_xmlnewstarget]..news_content;
var tfnews_content:TextSplash=new TextSplash(news_content,20,110,20,true,true,0x333333,330);
news_container_mc.addChild(tfnews_content);
tfnews_content.mouseEnabled = false;
news_image = my_grid[_xmlnewstarget].#news_image;
loadNewsImage();
addChild(tfnews_category);
addChild(tftitle);
addChild(tfnews_content);
var news_container_mc_tween = new Tween(news_container_mc, "alpha", Strong.easeIn, 0,1,0.3, true);
news_container_mc_tween.addEventListener(Event.INIT, newsContentLoaded);
}
I'm not going to try to read your code (try to work on your formatting, even if it's just indenting), but I'll provide a simplified example:
for (var i = 0; i < my_total; i++) {
var closure = function() {
// use i here
}
}
As you say, when closure is called it will contain the last value of i (which in this case would be my_total). Do this instead:
for (var i = 0; i < my_total; i++) {
(function(i) {
var closure = function() {
// use i here
}
})(i);
}
This creates another function inside the loop which "captures" the current value of i so that your closure can refer to that value.
See also How does the (function() {})() construct work and why do people use it? for further similar examples.
Umm, as mentioned above, the code is a bit dense, but I think you might have a bit of type conversion problem between string and integers, is the "last value" always 0? try making these changes and let me know how you get on.
// replace this gridnode_loader.name = i;
gridnode_loader.name = i.toString();
// explictly type this as an int
_xmlnewstarget = parseInt(my_gridnode.name);
// replace this: my_tweens[Number(my_gridnode.name)] = new Tween(......
my_tweens[parseInt(my_gridnode.name)] = new Tween();
Oh and I think it goes without saying that you should massively refactor this code block once you've got it working.
Edit: after further study I think you need this
//replace this: my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);
var anonHandler:Function = function(e:MouseEvent):void
{
loadNewsContent(_xmlnewstarget);
};
my_gridnode.addEventListener(MouseEvent.CLICK, anonHandler);
Where your loadNewsContent has changed arguements from (e:MouseEvent) to (id:String)
Firstly, you do not need to call addChild for the same loader twice (once in callGrid) and then in (gridLoaded). Then you can try putting inside loadNewsContent: news_category = my_grid[int(e.target.name)].#category;instead of news_category = my_grid[_xmlnewstarget].#category; As _xmlnewstarget seems to be bigger scope, which is why it is getting updated every time a load operation completes.