3
votes

I wrote a collision detection class that works off using BitmapData.hitTest. Basically, every update, the .draw method is called and then two BitmapData's are hitTest'ed against each other to see if there is a collision.

It works pretty well, however if I start rotating the source MovieClip I use for the BitmapData, the transformations don't get registered in the draw method. So what I did was pass through a Matrix in the draw method. That works. But here's the actual issue. If I output the BitmapData to a Bitmap on the stage, I see that Bitmap is, in fact, rotating, but now it's exceeding the bounds (width and height) of the BitmapData. Which is cropping / clipping the image of the source MovieClip.

My first guess would be to rotate the BitmapData, but rotation doesn't seem possible. There's just no properties that help you pull that off. Is there another way?

UPDATE: Basically, when the Bitmap clone of the MovieClip rotates outside the positive co-ordinate space, it doesn't get drawn. It rotates outside the bounds of the BitmapData set width and height. I can multiply the bounds by 2, and then center the bitmap inside the set bounds, but then the origin point is never fixed and the object is always being moved around. Works pretty well, but makes aligning the object to any specific point impossible.

Here's my isolated test code. It requires you to have a MovieClip on the stage named "iTest". I used a fairly vertical arrow shape (higher than it is wide) so I could keep track of rotations visually and help accentuate the clipping problem:

var rotateTimer:Timer = new Timer(10);

function rotateObject_init():void
{
 rotateTimer.addEventListener(TimerEvent.TIMER, rotateObject);
 rotateTimer.start();
}

function rotateObject_stop():void
{
 rotateTimer.removeEventListener(TimerEvent.TIMER, rotateObject);
 rotateTimer.stop();
}

function rotateObject(_event:TimerEvent):void
{
 // Deletes the previous bitmap
 if (this.getChildByName("iTestClone") != null)
 {
  removeChild(this.getChildByName("iTestClone"));

  testClone = null;
  testData = null;
 }

 iTest.rotation += 1;

 // Capture source BitmapData
 var testData:BitmapData = new BitmapData(iTest.width, iTest.height, false, 0xFFFFFF); 

 // Capture source transformations
 var testMatrix:Matrix = new Matrix();
 testMatrix.scale(iTest.scaleX, iTest.scaleY);
 testMatrix.rotate( iTest.rotation * (Math.PI / 180) );
 testMatrix.translate(0, 0);

 // Draw source with matrix
 testData.draw(iTest, testMatrix);

 // Output Bitmap
 var testClone:Bitmap = new Bitmap(testData);
 testClone.name = "iTestClone";
 testClone.transform = testMatrix.tra
 addChild(testClone);

 // Always makes sure source is visible
 this.swapChildren(iTest, testClone);
}

rotateObject_init();
3
Perhaps you could use the CDK (code.google.com/p/collisiondetectionkit) and avoid this problem altogether?Cameron
I'd rather use my own code, that way I can learn from the experience. Also, I really don't like working with other peoples code, people have such different ways of thinking and I find it's best to stick with your own workflow. I appreciate the link though.Daniel Carvalho
Sweet, do share. I also might have a fix that I might be able to try tonight. Basically, you do a precalucation that entails rotating the target object 360 degree's, and then capture it's max with and height during the process. Use that as your BitmapData size, and then use a technique somewhat similar as you've done, but perhaps simpler because we would now have the width and height, and work out a fixed top left position for the Bitmap, that of course, fits within the bounds of the BitmapData. Inspired by an tutorial on 8BitRocket.Daniel Carvalho

3 Answers

4
votes

Good question!

I have tried to make a BitmapData clone of a square rotated by 10 degrees. To get the rotation into the bitmap data I used the cloned object's matrix. The problem, as you mention is the offset you get because of the rotation pivot.

Here's how mine looks:

bounds

To get around that I though that if I get the difference between the with of the BitmapData and the right most point, that will give me the offset for x, and doing the similar operation for y, would get me in the right place.

Almost, I looks better (just one or two pixels seem harmed ):

bounds 2

Ok, I feel I got :)

offset

That offset is for x, but he misleading thing is, it is defined by the height of the bounding rectangle, not the width. Try this:

var box:Sprite = new Sprite();
box.graphics.lineStyle(0.1);
box.graphics.drawRect(0,0,150,100);
box.rotation = 10;
//get the size of the object rotated
var bounds:Rectangle = box.getBounds(this);
//make a bitmap data
var extra:int = 2;//cheating the trimmed pixels :)
var bitmapData:BitmapData = new BitmapData(bounds.width+extra,bounds.height+extra,false, 0x00009900);
//get the bottom left point
var bl:Point = getBottomLeft(box,bitmapData);
//get the matrix
var m:Matrix = box.transform.matrix;
//offset if
m.tx += Math.abs(bl.x);
//draw, using the offset, rotated matrix
bitmapData.draw(box,m);

var bitmap:Bitmap = new Bitmap(bitmapData);
bitmap.x = bitmap.y = 100;
addChild(bitmap);

function getBottomLeft(displayObject:DisplayObject,data:BitmapData):Point{
    var radius:int = displayObject.getBounds(displayObject).height;
    var angle:Number = displayObject.rotation * Math.PI / 180;
    var angle90:Number = angle + Math.PI * .5;
    return new Point(Math.cos(angle90) * radius, Math.sin(angle90) * radius);
}

You don't need a green background in the BitmapData constructor, that's for debugging. Also, you only need the the bottom left x position, therefore you need to return a Number instead of a Point.

so the slightly shorter version would be:

var box:Sprite = new Sprite();
box.graphics.lineStyle(0.1);
box.graphics.drawRect(0,0,150,100);
box.rotation = 10;

var bounds:Rectangle = box.getBounds(this);
var extra:int = 2;//cheating the trimmed pixels :)
var bitmapData:BitmapData = new BitmapData(bounds.width+extra,bounds.height+extra,false, 0x00009900);

var m:Matrix = box.transform.matrix;
m.tx += Math.abs(getLeftmost(box,bitmapData));
bitmapData.draw(box,m);

var bitmap:Bitmap = new Bitmap(bitmapData);
bitmap.x = bitmap.y = 100;
addChild(bitmap);

function getLeftmost(displayObject:DisplayObject,data:BitmapData):Number{
    var radius:int = displayObject.getBounds(displayObject).height;
    var angle90:Number = displayObject.rotation * Math.PI / 180 + Math.PI * .5;
    return Math.cos(angle90) * radius;
}

result:

offset 2

HTH,George

Note to self: be carefull what you wish for ^_^

0
votes

You say that the bounds of the bitmapData are causing the clipping, but you are setting the bounds to be the width and height of the object. This suggests that the width and height returned are actually incorrect (i.e. not taking into account the rotation).

Perhaps try using getBounds() to find the actual width and height of iTest:

// Capture source BitmapData
var bounds:Rectangle = iTest.getBounds(iTest);
var testData:BitmapData = new BitmapData(bounds.width, bounds.height, false, 0xFFFFFF); 

I'm not entirely sure if this will work though -- I haven't tested any of this :-)

0
votes

Just use the rotation property:

THENAMEOFMovieClip.rotation=number;

number being the quantity of degrees you want to rotate.