Drawing BitmapData to Sprite still using bitmapdata itself? - actionscript-3

I'm calling images from library to be use. But because I will be constantly add and remove the images, so I tried to copy the bitmapdata to a sprite and reuse the sprite.
private function loadImage():void {
for (var i:int = 1; i < typeAmount + 1; i++) {
SlotClass = Main.queue.getLoader('main_uiMC').getClass('slot' + i + 'bmp') as Class;
bmpDSlot = new SlotClass() as BitmapData;
bmpDSlotV.push(bmpDSlot)
}
}
private function bitmaping():void {
for (var i:int = 1; i < typeAmount + 1; i++) {
slotS = new Sprite()
slotS.graphics.beginBitmapFill(bmpDSlotV[i - 1], new Matrix, false, true)
slotS.graphics.drawRect(0, 0, bmpDSlotV[i - 1].width, bmpDSlotV[i - 1].height);
slotS.graphics.endFill();
bmpV.push(slotS)
}
Every time I duplicate the sprite, flashdevelop's profiler showed that the bitmapdata is being added as well. Even when I use removeChild to remove the Sprite, the memory usage won't decrease.
Is there a way to better copy the content of the bitmapdata, and can be completely remove when I remove the sprite?
*i will still be using the image, just that on that particular round i would like to remove the sprite that has the image.

You should use a single BitmapData object, create numerous Bitmap objects and operate these.
private function bitmaping():void {
for (var i:int = 1; i < typeAmount + 1; i++) {
slotS = new Bitmap(bmpDSlotV[i-1]);
bmpV.push(slotS);
}
Creating another Bitmap like this does not add another BitmapData, it uses existing one as reference.

Well your choice is to reuse the BitmapData. And because you need multiple instances of the same image, you need to create new one for each object that you would like to add. And this would be a Bitmap with the old BitmapData:
var originalBMD:BitmapData = instantiateItSomehow();
var first:Bitmap = new Bitmap(originalBMD);
var second:Bitmap = new Bitmap(originalBMD);
This way you would reuse the BitmapData and I think the memory will increase only because of the Bitmap objects, but the data itself should not be duplicated.

Related

Clearing tiles in tile based as3 game

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

Loading images does not load them into memory

I am loading a batch of 150 HD images into my app - it is basically a 3D view of an object. Once I load the image files using Loader instances I store the loaders' first child's bitmapdata in a Vector. When all of the loaded, I want to begin to "rotate" the object = meaning I am simply swapping the images. I take the Vector where I have the bitmapdatas and draw them onto a canvas bitmapdata one after the other. No science there, it all works as intended.
The problem is that once all the images are loaded and stored in a vector and BEFORE they are drawn to the canvas, they are not in the memory. That means that the first rotation of my 3D object (-> all 150 images drawn) is really slow. After the first rotation there is no problem and all is fluid. My question is: is there a way to force the images to get loaded into the memory without drawing them onto the stage? I expected that they would simply get loaded to memory once they are loaded to the app (Wrong!).
I tried to use addChild() instead of drawing them to a canvas bitmap, same result. I don't think the code is necessary but just in case:
private var _loaders:Vector.<Loader>;
private static const NAME:String = "img_00";
private static const MIN:uint = 0;
private static const MAX:uint = 150;
private var _loaded:uint = 0;
private var _currentFrameIndex:uint = 0;
private var _canvas:Bitmap;
private var _bitmaps:Vector.<BitmapData>;
private var _destPoint:Point;
public function loadImages():void {
var s:String;
for(var i:int=MIN; i<=MAX; i++) {
if(i < 10) s = "00" + i;
else if(i < 100) s = "0" + i;
else s = i.toString();
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadHandler);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadHandler);
loader.load(new URLRequest("images/JPEG/"+ NAME + s + ".jpg"));
_loaders.push(loader);
}
}
private function loadHandler(e:Event):void {
_loaded++;
if(_loaded > (MAX - MIN)) {
_bitmaps = new Vector.<BitmapData>(_loaders.length);
for(var i:int=0; i<_loaders.length; i++) {
var loader:Loader = _loaders[i];
_bitmaps[i] = Bitmap(loader.getChildAt(0)).bitmapData;
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadHandler);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, loadHandler);
loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, loadHandler);
}
setFrame(0);
dispatchEvent(new Event(LOAD_COMPLETE));
}
}
public function setFrame(frame:uint):void {
if(frame >= 0 && frame < _bitmaps.length) {
_currentFrameIndex = frame;
var bmpData:BitmapData = _bitmaps[_currentFrameIndex];
_canvas.bitmapData.copyPixels(bmpData, bmpData.rect, _destPoint);
}
}
"Not in the memory" means that the images are loaded, but not yet decoded, and this decode is done on the fly, and this takes the time you observe as slowness. You can attempt to "virtually" rotate the image by having a bitmap that's not yet added to stage to be the reference to each of the bitmapDatas of your vector. Make a progress bar that shows how much of the vector has already been decoded, and once this happens, display the bitmap and give the user smooth rotation.
addEventListener(Event.ENTER_FRAME,prerender);
var b:Bitmap=new Bitmap();
/* optional
b.x=stage.stageWidth;
b.y=stage.stageHeight;
addChild(b);
*/
var vi:int=0;
var sh:Shape=new Shape();
sh.graphics.lineStyle(4,0,1); // a simple progress bar
sh.graphics.moveTo(0,0);
sh.graphics.lineTo(100,0);
sh.scaleX=0;
sh.x=stage.stageWidth/2-50; // centered by X
sh.y=stage.stageHeight/2;
addChild(sh);
function prerender(e:Event):void {
if (vi==_bitmaps.length) {
// finished prerender
removeEventListener(Event.ENTER_FRAME, prerender);
removeChild(sh);
// removeChild(b); if optional enabled
setFrame(0);
return;
}
b.bitmapData=_bitmaps[vi];
vi++;
}
Also, it's always better to assign the bitmapData property to a Bitmap object if you don't plan to have that bitmapdata changed. So, instead of your _canvas.bitmapData.copyPixels(bmpData, bmpData.rect, _destPoint); you just do _canvas.bitmapData = bmpData; and it'll work.
UPDATE: Your issue might as well nail to the last point, that is assigning instead of copying. If your destPoint is something else than (0,0), you just make another Bitmap object on top of your _canvas with desired offset, and assign bitmapdatas in there. I have remembered that when I first made multiple animated objects based on a single Vector.<BitmapData> like yours, and tried doing copyPixels(), my animations were jittering and not displaying proper frames, but once I did _bitmap.bitmapData=_bitmaps[currentFrame] everything went as smooth as it should be.

adding Bitmaps to various frames in movieClip in as3

I have a movieClip and I want attach to this movieClip bitmaps. I want attach each bitmap to different frame of movieClip. I have tried something like this, but it is not working. It is for memory game I am creating.
for(var i : int = 0; i < cardList.length; i++){
var helpVar : int = cardList[i].pictureOfCard;
cardList[i].gotoAndStop(cardList[i].pictureOfCard+2);
var bitmap : Bitmap = new Bitmap(bitMapArray[helpVar].bitmapData.clone());
cardList[i].addChild(bitmap);
cardList[i].gotoAndStop(1);
}
You could simply load all Bitmaps and only show the one, that should be visible right now. e.g.
function ShowFrame(nr:int):void{
for(i:int = 0; i<bitMapArray.length; i++){
bitMapArray[i].visible = false;
}
bitMapArray[nr].visible = true
}
My AS3 skills are rusty, so this might need some syntax correction, but it works in theory.
var i :int = 0;
processNext();
function processNext():void
{
cardList[i].addEventListener(Event.FRAME_CONSTRUCTED, onFrameConstructed );
cardList[i].gotoAndStop(cardList[i].pictureOfCard+2);
}
function onFrameConstructed( e:Event ):void
{
cardList[i].removeEventListener(Event.FRAME_CONSTRUCTED, onFrameConstructed );
var helpVar : int = cardList[i].pictureOfCard;
var bitmap : Bitmap = new Bitmap(bitMapArray[helpVar].bitmapData.clone());
cardList[i].addChild(bitmap);
if( i < cardList.length - 1 )
{
i++;
processNext();
{
else
trace("All done");
}
After some time I discover that what I was trying to achieve is probably not possible in as3. The way I solved my problem is that everytime i gotoAndStop to frame I need have there bitmap I addChild(bitmap) and everytime I gotoAndStop other frame where there should not be bitmap I removeChild(bitmap) from movieClip. (So it is very similar aproach to makeing bitmap visible and invisible)

Accessing pieces of bitmaps inside a movieclip

I have a movieclip with 10 bitmaps in it. I wanna access each of them.
myMovieClip is the movieclip containing those 10 bitmaps. I wanna access those bitmaps one by one. All 10 bitmaps are imported separately.
I tried this :
for ( var i =0 ; i< myMovieClip.numChildren ; i++)
{
trace ( myMovieClip.getChildAt(i) );
}
Problem is numChildren comes "1" only, as if it doesnot consider those 10 pieces of bitmap. Any other way, to access those bitmaps ?
thanks
V.
what do you mean by pieces of bitmaps?? Do you mean 10 different bitmap objects are children of the movieClip??
In addition, your code does have a syntax error.
var newMc:MovieClip = MovieClip();
should be:
var newMc:MovieClip = new MovieClip();
second off all, in your loop, numChildren will be always changing since you are taking the reference of a child from the myMoiveClip and moving it to the newMc object. there are two way to fix this.
either set a local variable to the value of myMovieClip.numChildren and use that value in your loop
example:
var numOfChildren:int = myMovieClip.numChildren;
for(var i:int = 0; i < numOfChildren; i++){
var newMc:MovieClip = new MovieClip();
newMc.addChild(myMovieClip.getChildAt(i));
}
this will move the bitmaps out of myMovieClip and into newMc, if you want to keep them there you can create a new bitmap inside the loop and then add the new bitmap to the newMc.
example:
for(var i:int = 0; i < myMovieClip.numChildren; i++){
var newMc:MovieClip = new MovieClip();
var b:Bitmap = new Bitmap(Bitmap(myMovieClip.getChildAt(i)).bitmapData);
newMc.addChild(b);
}

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.