Movieclip -> BitmapData and origin - actionscript-3

I'm trying to draw a MovieClip to a BitmapData so that it looks exactly the same as if I had done addChild(movieclip).
I can't seem to figure out what magic formula I need to use to keep the animation in place around it's origin.
This is the relevant bit of code:
private function update(e:Event):void
{
var bounds:Rectangle = movieClip.getBounds(movieClip);
bitmapData = new BitmapData(maxMCwidth, maxMCheight, true, 0x0);
bitmapData.draw(movieClip, new Matrix(1, 0, 0, 1, maxMCwidth - (bounds.x + bounds.width), maxMCheight - (bounds.y + bounds.height)));
bitmap.bitmapData = bitmapData;
}
Full code here: http://pastebin.com/KyU5FPeJ
And this is what the result looks like:
http://www.swfcabin.com/open/1339173476
The top animation is done using addChild(mc). The bottom animation is done using bitmapData.draw. Notice how the sword on the right doesn't move horizontally in the bottom version. Almost as if the whole animation was getting right-aligned.
Here's also a link to my flashdevelop project with the full code in case anybody wants to play around with it: https://www.dropbox.com/s/gvt4scknqdian2a/bitmapdatadrawing.zip
I just want both versions to look the same. What do I need to change to my bitmapData drawing code to make this happen?

Ok I figured it out!
The cause of the problem was that I'm not calculating maxMCwidth or maxMCheight correctly. I was going through the movieclip looking for the widest/highest single frame. BUT that doesn't take into account that the position of bounds across frames changes. To get the actual maximum width/height across the whole animation, I used this code:
maxBounds = new Rectangle();
for (var i:uint = 0; i < movieClip.totalFrames; i++)
{
var tempBounds:Rectangle = movieClip.getBounds(movieClip);
maxBounds = maxBounds.union(tempBounds);
movieClip.nextFrame();
}
Then this allowed me to fix the translation matrix:
bitmapData.draw(movieClip, new Matrix(1, 0, 0, 1, -maxBounds.x, -maxBounds.y));
And like this it works for every animation I've tested.

Ok I can't get your project to run since you didn't supply a main mxml.
However, I think I found your issue.
I don't think getBounds is returning the values you think they are.
I can't look into the skeleton swc but I have a feeling there is on object off the top or bottom of the screen in it and the way you coded it would cause it to shrink down to make it fit.
getBounds is probably adding that to the height.
bitmapData = new BitmapData(movieClip.width, movieClip.height, true, 0x0);
bitmapData.draw(movieClip, new Matrix(1, 0, 0, 1, movieClip.width/2, movieClip.height/2 ));
bitmap = new Bitmap(bitmapData);
bitmap.x = 70;
bitmap.y = 200;
Remember untested code here. :(

Related

Canvas Animation Not Rendering

I'm new to the canvas tag and am playing around with some animation. Basically, I'm trying to setup a "ground" section composed of multiple images (similar to an 8bit side scroller game like Mario Brothers). The ground will be composed of multiple images, which I've built a constructor function to load these and tile them across the bottom.
function Ground(context,ImageName,ImgX,ImgY,ImgW,ImgH){
this.width=ImgW;
this.height=ImgH;
this.x=ImgX;
this.y=ImgY;
img=new Image();
img.onload=function(){context.drawImage(img,ImgX,ImgY,ImgW,ImgH);};
img.src='images/'+ImageName;};
This seems to work out just fine. I've then setup the rest of the animation, including a basic setup for Key Left/Right events, like so:
window.onload=function(){
var canvas=document.getElementById('canvas'),
context=canvas.getContext('2d'),
Grounds=[],
ImgX=-150; // INITIAL STARTING X FOR FIRST GROUND TILE
// INSERT GROUND ELEMENTS
for(var i=0,l=8; i<l; i++){
var ImgX+=150;
Grounds[i]=new Ground(context,"ground.png",ImgX,650,150,150);
};
// ASSIGN LEFT/RIGHT KEYS
window.addEventListener('keyup',function(event){
switch(event.keyCode){
case 37:
for(var i=0,l=Grounds.length; i<l; i++){
Grounds[i].x+=10;
};
break;
case 39:break;
};
});
// ANIMATION LOOP
(function drawFrame(){
window.mozRequestAnimationFrame(drawFrame,canvas);
context.clearRect(0, 0, canvas.width, canvas.height);
}());
};
I know exactly what my problem is, but don't know how to solve it. The animation loop is clearing the canvas every frame, but not redrawing the updated position (if any) when the user presses the left arrow key.
I'm missing the redraw part here and I'm not exactly sure how to handle this or if I'm approaching this entirely wrong. Any help is very appreciated! Thanks!
First of all you're incrementing the property x of the ground tiles but that property is not even used anywhere in your code. Modify your code so that the onload event of those image objects draws the image according to their own x property so changes to it will actually affect what is drawn. Also add the image object as a property of the Ground object so you can access it later on from outside.
Your approach is really not so good but if you want to do it without going back to 0 do it as follows:
function Ground(context,ImageName,ImgX,ImgY,ImgW,ImgH){
this.width=ImgW;
this.height=ImgH;
this.x=ImgX;
this.y=ImgY;
var self = this; // Add local reference to this Ground instance
this.img=new Image(); // img is now a property too
this.img.onload=function(){context.drawImage(this, self.x, self.y,self.width,self.height);};
this.img.src='images/'+ImageName;};
Ok so now you can change the property x of the ground tiles and call the draw function of it again (which is the onload event).
Grounds[i].x+=10;
Grounds[i].img.dispatchEvent(new Event("load"));
Please note that you should really make the updates of all the values first and then all the draw calls separately.
Can you not just add a draw method? You usually so something like this:
init -> update -> clear, redraw -> update -> clear, redraw -> ...
// ANIMATION LOOP
(function drawFrame(){
window.mozRequestAnimationFrame(drawFrame,canvas);
context.clearRect(0, 0, canvas.width, canvas.height);
contect.drawImage(...);
}());

action script 3 - is it possible to trigger click event only when mouse is clicked on the image part?

I have a problem and I have potential solution. But I wanted to confirm if there is an easy and simple way to solve my problem.
App type:
Isometric Game
Problem statement:
I am loading images in my flash app and have mouse events attached to them.
The images I load are prop images like vehicles, trees, buildings etc., and all of them are transparent.
Example: Red ball asset (please ignore the yellow background which I applied to describe the problem)
If I click on the actual image area (colored in red), then every thing works perfect
I don't want to trigger mouseevent when I click on empty image part (or transparent area, which I have shown in yellow color)
There is one way I know by creating masks in flash. I don't want to do it unless that is the final option left because I load image assets instead of flash assets and I don't want to create a new mask asset for all the assets
There is another method I was going to adopt by using getPixel method of Bitmap. Which is discussed here.
But there is another problem with this method.
I might be able to ignore the click event when I click on the empty part of the asset but if there is some other asset is behind the image in the same location, then I need to process the click event for the occluded image.
Well, thinking of solution to this problem takes me to the getObjectsUnderPoint where I can scan the occluded assets
Well, what you proposed as a solution is 100% valid. Just move the logic of determining what game object is clicked outside of that object.
Listen for MOUSE_DOWN/MOUSE_UP events at container which contains your game objects.
Catch an event
Check if the game object which is the target of this event is transparent at this point using BitmapData.getPixel32
If it is use getObjectsUnderPoint to find out all other game objects at this point
Find in a loop the first object which is not transparent at this point
Now you got the actual object which is hit.
One interesting solution is to use Sprite objects with the individual non-transparent pixels burnt onto them.
Suppose this is your Loader "complete" handler:
private function loaderCompleteHandler(event:Event):void
{
// Loader is not our child, we use a Sprite instead (below).
var loader:Loader = Loader(event.target);
var sprite:Sprite = new Sprite();
addChild(sprite);
var w:Number = loader.content.width;
var h:Number = loader.content.height;
// Use transparent bitmap.
var bitmapData:BitmapData = new BitmapData(w, h, true, 0);
bitmapData.draw(loader.content);
// Now burn the image onto the Sprite object, ignoring
// the transparent pixels.
for (var xPos:int = 0; xPos < w; xPos++) {
for (var yPos:int = 0; yPos < h; yPos++) {
var pixel32:uint = bitmapData.getPixel32(xPos, yPos);
var alpha:int = pixel32 >>> 24;
if (alpha != 0) {
sprite.graphics.beginFill(pixel32 & 0xFFFFFF, alpha / 0xFF);
sprite.graphics.drawRect(xPos, yPos, 1, 1);
sprite.graphics.endFill();
}
}
}
}
Essentially you want "empty" pixels that aren't clickable, and fully transparent pixels aren't quite the same thing. With this solution you get empty pixels.
Only problem is that this might be slow. Give it a shot.

Use bitmapData.hitTest on two bitmapData with centered registration point

I've spent all the day on this, it's time to ask for your help :)
I'm trying to do collision detection of two display objects, both have centered registration point.
On my stage I have fixed elements that when added to stage are pushed in an Array called "zoneUsed". All the displayObject in my project have the registration point in the center.
My goal is to click on the stage, and check if in the clicking coords I could create a circle. My plan was to create a Sprite for the new object, cycle on the zoneUsed array, and check if the new sprite have enough space to live.
Here my code so far:
private function checkSpaceForNewMarker (markerToCheck:Sprite):Boolean {
var isPossible:Boolean = true;
var bmdataToCheck:BitmapData = new BitmapData (markerToCheck.width, markerToCheck.height, true, 0);
var m:Matrix = new Matrix ();
m.tx = markerToCheck.width/2;
m.ty = markerToCheck.height/2;
bmdataToCheck.draw (markerToCheck, m);
for (var i:int = 0; i<zoneUsed.length; i++) {
trace ("*** CHECKING ****");
var bmddataOnTheTable:BitmapData = new BitmapData (zoneUsed[i].width, zoneUsed[i].height, true, 0);
var tableMatrix:Matrix = new Matrix ();
tableMatrix.tx = zoneUsed[i].width/2;
tableMatrix.ty = zoneUsed[i].height/2;
bmddataOnTheTable.draw(zoneUsed[i], tableMatrix);
if (bmdataToCheck.hitTest(new Point(markerToCheck.x, markerToCheck.y), 255, bmddataOnTheTable, new Point (zoneUsed[i].x, zoneUsed[i].y), 255)) {
trace ("COLLISION");
isPossible = false;
} else {
trace ("NO COLLISION");
isPossible = true;
}
}
return isPossible;
}
....But right now the results are weird. Depending on the zones, my traces work or not. What am I doing wrong?
The problem is , you are drawing 1/4 (quarter) part of every object.
BitmapData is not like a Shape, Sprite, MovieClip, and it crops all the pixels, when the drawing bounds is out of the bounds of (0,0,bitmapdata.width, bitmapdata.height) rectangle.
Just remove this lines:
m.tx = markerToCheck.width/2;
m.ty = markerToCheck.height/2;
and also
tableMatrix.tx = zoneUsed[i].width/2;
tableMatrix.ty = zoneUsed[i].height/2;
You don't need this translations.
Also your code may be cause for memory leak. You are creating bitmapdata, but do not dispose it. The garbage collector will not release the memory you have allocated.You must release memory explicitly. Call bitmapdata.dispose() every time you have no need of that bitmapdata.
I'm not sure that the origin of the bitmap has anything to do with the test itself. The very nature of the test would seem to imply that the hittest is based on the RGBA value of the two supplied bitmaps. Anyway rather than picking apart your own implementation I'll just refer you to a tutorial by Mike Chambers (adobe platform evangelist). http://www.mikechambers.com/blog/2009/06/24/using-bitmapdata-hittest-for-collision-detection/
Also for more flash tutorials check out www.gotoandlearn.com.

How can I load a Papervision/Flex application (SWF) as a material on a Papervision plane?

I am trying to build a portfolio application similar to the used by Whitevoid. I am using Flex 4 and Papervision3D 2. I have everything working except for one issue. When I try to load an external SWF as a material on one of the planes, I can see any native Flex or Flash components in their correct positions, but the papervision objects are not being rendered properly. It looks like the viewport is not being set in the nested swf. I have posted my code for loading the swf below.
private function loadMovie(path:String=""):void
{
loader = new Loader();
request = new URLRequest(path);
loader.contentLoaderInfo.addEventListener(Event.INIT, addMaterial);
loader.load(request);
}
private function addMaterial(e:Event):void
{
movie = new MovieClip();
movie.addChild(e.target.content);
var width:Number = 0;
var height:Number = 0;
width = loader.contentLoaderInfo.width;
height = loader.contentLoaderInfo.height;
//calculate the aspect ratio of the swf
var matAR:Number = width/height;
if (matAR > aspectRatio)
{
plane.scaleY = aspectRatio / matAR;
}
else if (matAR < aspectRatio)
{
plane.scaleX = matAR / aspectRatio;
}
var mat:MovieMaterial = new MovieMaterial(movie, false, true, false, new Rectangle(0, 0, width, height));
mat.interactive = true;
mat.smooth = true;
plane.material = mat;
}
Below I have posted two pictures. The first is a shot of the application running by itself. The second is the application as a MovieMaterial on a Plane. You can see how the button created as a spark object in the mxml stays in the correct position, but papervision sphere (which is rotating) is in the wrong location. Is there something I am missing here?
Man. I haven't seen that site in a while. Still one of the cooler PV projects...
What do you mean by:
I cannot properly see the scene rendered in Papervision
You say you can see the components in their appropriate positions, as in: you have a plane with what looks like the intended file loading up? But I'm guessing that you can't interact with it.
As far as I know, and I've spent a reasonable amount of time trying to make something similar work, the MovieMaterial (which I assume you're using) draws a Bitmap of whatever contents exist in your MovieClip, and if you set it to animated=true, then it will render out a series of bitmaps - equating animation. What it's not doing, is displaying an actual MovieClip (or SWF) on the plane. So you may see your components, but this is how:
MovieMaterial.as line 137
// ______________________________________________________________________ CREATE BITMAP
/**
*
* #param asset
* #return
*/
protected function createBitmapFromSprite( asset:DisplayObject ):BitmapData
{
// Set the new movie reference
movie = asset;
// initialize the bitmap since it's new
initBitmap( movie );
// Draw
drawBitmap();
// Call super.createBitmap to centralize the bitmap specific code.
// Here only MovieClip specific code, all bitmap code (maxUVs, AUTO_MIP_MAP, correctBitmap) in BitmapMaterial.
bitmap = super.createBitmap( bitmap );
return bitmap;
}
Note in the WhiteVoid you never actually interact with a movie until it "lands" = he's very likely swapping in a Movie on top of the bitmap textured plane.
The part that you are interacting with is probably another plane that holds the "button" that simply becomes visible on mouseover.
I think PV1.0 had access to real swfs as a material but this changed in 2.0. Sadly. Hopefully Molehill will.
cheers

convert masked movieclip into bitmap and save it on server

i have a
dsgnArea----> a movieclip
dsgnArea is masked by dsgnAreaMask, which inturn is a movieclip
dsgnArea.mask=dsgnAreaMask;
the width,height and position of dsgnAreaMask and dsgnArea are same.
i dynamically added multiple movieclips and labels to dsgnArea;
like..
dsgnArea.addChild(movieClip1);
dsgnArea.addChild(movieClip2);
dsgnArea.addChild(label1);
dsgnArea.addChild(label2); and so on...
these movieclips (movieClip1,movieClip2,......) and labels(label1,label2,....) positions can be altered in runtime..
but as i masked the dsgnArea with dsgnAreaMask, only a portion of the added movieClips and labels are visible...
So, my problem is to grab that visible portion in dsgnArea into a bitmap,like a screenshot of that particular dsgnArea, and save it in my server.
please help me out in this problem.
Say s is the DisplayObject object you want to capture and m is the mask applied on it.
var maskRect:Rectangle = m.getRect(s);
var matrix:Matrix = new Matrix(1, 0, 0, 1, -maskRect.x, -maskRect.y);
var w:Number = Math.min(s.width, maskRect.right) - maskRect.x;
var h:Number = Math.min(s.height, maskRect.bottom) - maskRect.y;
var bd:BitmapData = new BitmapData(w, h);
bd.draw(s, matrix);
Does that work?
The BitmapData draw method is what you are looking for. You can use it's clipRect param to define what you would like to draw (the masked parts).
Quasimondo did a handy little method to do this (take a snapshot of the whole displayObject), it's available here: http://www.quasimondo.com/archives/000670.php
I don't know if it works with masked content though.
if it doesn't, the trick would be to translate the whole content by the size of the mask
var bounds:Rectangle = dsgnAreaMask.getBounds( dsgnAreaMask );
instead of using the content of the clip
var bounds:Rectangle = clip.getBounds( clip );
as far as saving file to server is concerned, the question was asked (answered?) here AS3 Save Media File to server