As you well know in as3 we have a getBounds() method which returns the exact dimension and coordinates of the movieclip in the DisplayObject container we want.
Fact is that these data are calculated based on the graphics in their state in the MC at the frame it is while getBounds() is called.
What I want is the REAL bounds rectangle, that is the larger rectangle that the WHOLE animated movieclip will take in its container.
I thought of two ways:
1 - a flash built-in method that I don't know
2 - going through every frame always getting the bounds and finally returning the biggest (but what if it's a long animation? should I wait for it to play completely before I can get what I want?)
I hope I've been clear. If you need examples, let me know!
You can iterate through each frame without having to wait for the animation to play:
Let's say your clip is called bob:
var lifetimeBounds:Rectangle = new Rectangle();
bob.gotoAndStop(1);
for(var i:int=1;i<=bob.totalFrames;i++){
lifetimeBounds.width = Math.max(lifetimeBounds.width, bob.width);
lifetimeBounds.height = Math.max(lifetimeBounds.height, bob.height);
lifetimeBounds.x = Math.min(lifetimeBounds.x, bob.x);
lifetimeBounds.y = Math.min(lifetimeBounds.y, bob.y);
bob.nextFrame();
}
bob.gotoAndStop(1); //reset bob back to the beginning
It's more CPU taxing (so I'd recommend not using it if the above works for your situation), but you could also use getBounds() in the example above and compare the returned rectangle against the lifetimeBounds rectangle:
var tempRect:Rectangle;
var lifetimeBounds:Rectangle = new Rectangle();
bob.gotoAndStop(1);
for(var i:int=1;i<=bob.totalFrames;i++){
tmpRect = bob.getBounds(this);
lifetimeBounds.width = Math.max(lifetimeBounds.width, tempRect.width);
lifetimeBounds.height = Math.max(lifetimeBounds.height, tempRect.height);
lifetimeBounds.x = Math.min(lifetimeBounds.x, tempRect.x);
lifetimeBounds.y = Math.min(lifetimeBounds.y, tempRect.y);
bob.nextFrame();
}
I had this issue when converting animations to bitmapData frames, as I wanted all the resulting frames to be a uniform size and match the largest frame dimensions.
I basically had to loop through the animation 1 frame at a time and compare the bounding box to the current largest dimensions. I too thought it was a less than an ideal solution, but it worked.
So #2 is your best bet, as there is no flash built in method that provides what you seek.
Related
I am trying to get the derived position and scaling of a 3D Sprite and set them to a 2D Sprite.
I have managed to do the first part like this:
var p:Point = sprite3d.local3DToGlobal(new Vector3D(0,0,0));
billboard.x = p.x;
billboard.y = p.y;
But I can't get the scaling part correctly. I am trying this:
var mat:Matrix3D = sprite3d.transform.getRelativeMatrix3D(stage); // get derived matrix(?)
var scaleV:Vector3D = mat.decompose()[2]; // get scaling vector from derived matrix
var scale:Number = scaleV.length;
billboard.scaleX = scale;
billboard.scaleY = scale;
...but the result is apparently wrong.
PS. One might ask what I am trying to achieve. I am trying to create "billboard" 3D sprites, i.e. sprites which are affected by all 3D transformations except rotations, thus they always face the "camera".
The documentation says that you get the vector correctly, but its coefficients don't seem to be added together to form a single length value. Try first an unscaled sprite, and check if you're receiving a sqrt(3) value as its length. If yes, then you should use 0th element of the vector as X scale, and 1th as Y scale. I'm not sure what to do with 2nd element (in this case it'll be a Z scale, either divide both scales by it, or multiply by it). Hope that helped.
Here is the code to make an MC the exact size in even proportions of whatever your choice. I choose 777 for this example.
my_mc.height = 777; // Can be anything you want.
my_mc.scaleX = my_mc.scaleY; /// This makes it the same proportions.
Now the questions is how can I tween this?
I'm afraid you have to tween both x and y scale values.
Other scenarios can be tricky.
Another alternative would be to tween an arbitary property of the movieclip, and letting a frame-based function (called on ENTER_FRAME event) that reads this variable and updates both scale values.
OR, making a custom movieclip class and let its internals handle a custom scaleXY property.
function tweenThis(newHeight:uint):void{
var oldHeight:uint=my_mc.height;
var difValue:Number=newHeight/oldHeight;
var newWidth:uint=my_mc.width*difValue;
new Gtween(my_mc,1,{width:newWidth,height:newHeight});
}
I have an application where user have to draw a line on the canvas without overlapping it. Is there a way to test the overlapping? I have googled already but found result with circles and rectangle overlapping. My case is different. Here user will draw lines on canvas without overlap the line itself. May be I am missing something so any guidance is appreciated. Thanks
I take it you mean the user draws a line with some sort of pen tool, using the mouse.
Here's what I would do:
First, hold the path of the line drawn in a BitmapData object.
var lineBitmapData:BitmapData = new BitmapData(display.width,display.height,true,0x00000000);
This creates a transparent bitmap object with the user's line on it.
On each frame (or timer event, if you use timer) do the following:
1.capture the current mouse position and put it into a Point object.
var currentMousePosition:Point = new Point(mouse.x,mouse.y);
you will also need a point representing the upper-left corner of your bitmapData.
var pt1:Point = new Point(1,1);
2.perform collision detection between the current mouse position and the lineBitmapData
var result:Boolean = lineBitmapData.hitTest(pt1, 0xFF, currentMousePosition);
the second parameter in the hitTest method is the threshhold value. Basically, this needs to be set to the minimum alpha value that you want to count as a hit.
3.check the result of the hitTest. If it's false, this means what the user is about to draw this frame does not intersect what was already drawn. In this case, you add the bit that was drawn during the last frame to the lineBitmapData.
If the hitTest returns true, however, that means the user is about to make his line intersect, so your program needs to stop the drawing (or whatever behavior you want).
if(result){
myPenTool.stopDrawing();}else{
var drawnLastFrame:BitmapData = myPenTool.drawSingleFrameLine();
lineBitmapData.draw(drawnLastFrame);}
4.Update what the user sees on the screen with the new lineBitmapData
To anybody who may care to help:
I am looking to create an animation (perhaps frame by frame) that corresponds with the coordinates of an object. Specifically, I want to have a draggable object's coordinates (locked to the x-axis) indicate where the playhead of a specific movie clip should be.
In other words, let's say that I have a 100px wide stage and I want each px location of an object on that stage to correspond to a particular frame of a movieclip.
In concept, I feel that it should be as easy as loading an objects coordinates into a variable, then passing that variable on with a simple math equation, adjusting it for movieclip length... but right about then my brain gets fried.
Finding out how to lock a draggable object to the x-axis has been pretty easy, but from there I'm stumped. I'm not particularly well versed in AS3 but I do like to think I understand the concepts.
Thank you in advance.
Try the following:
import flash.events.Event;
//the min (left-most) coord your draggable mc can be dragged
var minX:int=0;
//the max (right-most) coord your draggable mc can be dragged
var maxX:int=100;
var frameTo:uint;
//enterframe listener to check drag_mc x position continuously
addEventListener(Event.ENTER_FRAME, enterframe_handler);
function enterframe_handler(e:Event):void
{
//drag_mc is your draggable movieclip, anim_mc is the animation
//drag_mc.x should always be between minX and maxX: (minX <= drag_mc.x <= maxX)
//(drag_mc.x/(maxX - minX) gives us the "percentage" (from 0 to 1)
//multiply by the animation's total frames lenght,
// and add 1 (because frame numbers begin at 1)
frameTo = 1 + Math.floor((drag_mc.x/(maxX - minX))* anim_mc.totalFrames);
//set animation to target frame!
anim_mc.gotoAndStop(frameTo);
}
I am making an achtung die kurve-like game in AS3.0. So far I've done the movements of the 4 different players, and it works alright.
I am now to make collision detection, in order to test if a 'worm'-so to speak, is colliding with eachother or its own tail.
As I understand it, if I use hitTestObject(); it will use the registration area of the whole object, which would be a huge problem, seeing since this registration makes a 4-sided registration that contains all of the object. So if this is used, it will 'collide' just by entering this rectangle instead of hitting the actual worm. Is this correctly understood?
I've been looking through different methods of collision detection, and can't seem to find an optimal one for my project.
My thought were to check if the 'worms' are drawing their new sprites on a white background. if they aren't, then it must have hit something.
You can see how I used my code here: code in .as format linked to an .fla file
Sorry for my ill-formulated question, hope it makes somewhat sense.
Any help is greatly appreciated!!
Best regards - Jesper
Try this function if you want a Pixel Perfect Collision Detection with efficient CPU usage:
trace("Collided: " + (areaOfCollision(mc1, mc2) != null));
trace("Where: " + areaOfCollision(mc1, mc2));
function areaOfCollision(object1:DisplayObject, object2:DisplayObject, tolerance:int = 255):Rectangle {
if (object1.hitTestObject(object2)) {
var limits1:Rectangle = object1.getBounds(object1.parent);
var limits2:Rectangle = object2.getBounds(object2.parent);
var limits:Rectangle = limits1.intersection(limits2);
limits.x = Math.floor(limits.x);
limits.y = Math.floor(limits.y);
limits.width = Math.ceil(limits.width);
limits.height = Math.ceil(limits.height);
if (limits.width < 1 || limits.height < 1) return null;
var image:BitmapData = new BitmapData(limits.width, limits.height, false);
var matrix:Matrix = object1.transform.concatenatedMatrix;
matrix.translate(-limits.left, -limits.top);
image.draw(object1, matrix, new ColorTransform(1, 1, 1, 1, 255, -255, -255, tolerance));
matrix = object2.transform.concatenatedMatrix;
matrix.translate(-limits.left, -limits.top);
image.draw(object2, matrix, new ColorTransform(1, 1, 1, 1, 255, 255, 255, tolerance), BlendMode.DIFFERENCE);
var intersection:Rectangle = image.getColorBoundsRect(0xFFFFFFFF, 0xFF00FFFF);
if (intersection.width == 0) return null;
intersection.offset(limits.left, limits.top);
return intersection;
}
return null;
}
After a successful preliminary hitTestObject(), this function backgroundly takes a snapshot from the shapes of both objects painted with different colors each, then overlays them intersecting the colors on a new one, returning the Rectangle of the resulting shape. So cool.
To learn more about Pixel Perfect Collision Detection you can google Collision Detection followed by one of these names: "The ActionScript Man", "Troy Gilbert", "Boulevart (wim)", "Grant Skinner (gSkinner)" or "Senocular". Those guys are awesome AS3 references by the way.
The problem you discribe is a very common problem for collission detection because the object has a set width and height and therefor defines a rectangle as the object.
There is a solution however to make a colission detection system on pixel level I have found this on the official site and this made me able to make collission detection for bitmaps on pixel level.
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7d55.html
hope it helps you out in the same way.
Looking at the screenshots of that game, I think the best model would be to describe each worm as a chain of circles. Then divide the world/level in a grid with cells somewhat larger than the circle radii.
The collision check would then be:
clear grid
place each circle into the 1 or more grid cells it falls in
iterate over all cells, for each cell:
for each pair of circles (partially) in this cell, check if they intersect. If they do; collision.
Note that this may result in more than 1 collision occurrence between circle A and B, so you'd also need to check that to avoid duplicates.
Step 1 and 2 can be optimized by not clearing the grid, and instead of step 2, updating each circle's cell after it moves. If you size your cells like 5x the size of a circle, a circle can stay in the same cell for a few frames, avoiding excessive add/remove operations.
I'm doing something similar in a project of mine right now, except with space ships! My grid cells are currently 256x256 (too big for your project I think) and my units have radii of about 20.