Lovelyredsky

BitmapData를 이용한 정확한 충돌감지

보통 Flash에서 충돌감지를 위해서는 HitTestObject를 쓰거나 HitTestPoint를 씁니다.
하지만 이 2가지 방법 모두 완전한 것은 아닙니다.

- HitTestObject
2개의 DisplayObject를 대상으로 충돌하는지 체크합니다. 정확하게는 각 DisplayObject의 Rectangle을 기준으로 체크합니다. 따라서 기울어지지 않은 사각형들의 경우 Rectangle에서 남는 부분 없이 감싸지므로 정확하게 충돌을 감지하는 것 처럼 보이지만, 동그랗거나 불규칙한 모양의 DisplayObject의 경우 닿지 않았는데도 충돌했다고 판정을 내립니다.
더구나 child들도 같이 체크되기 때문에 투명하거나 안보이는 속성의 child도 Rectangle에 포함되어 체크됩니다.

- HitTestPoint
이건  한 점(Point)과 하나의 DisplayObject를 가지고 충돌 감지를 합니다. 한 점만 체크하기 때문에 정확하지만 마우스의 충돌을 체크하는 것 외에 사실 2개 물체의 충돌을 정확하게 감지 할 수 없습니다. 대상 point가 DisplayObject의 어느 지점의 좌표인지 알 수 없는 일이니까요.
단, HitTestPoint의 경우 ShapeFlag값이 있어서 Rectangle이 아닌 물체의 모양을 기준으로 판단합니다. 따라서 원형 물체들의 충돌감지에는 유용합니다.

이렇게 2가지 방법은 각각 사각형 또는 원형의 물체에만 유용한 단점이 있습니다. 따라서 이를 해결하는 또 다른 방법을 소개합니다.

이것은 타겟과 대상의 bitmapData를 서로 다른 단색으로 가공한 다음 Difference모드로 blend하는 것입니다. 그럼 겹치는 부분만 color를 가지고 체크할 수 있기 때문에 정확히 닿아야만 충돌로 판정할 수 있으며, 더불어 얼마나 많이 충돌했는지도 영역의 크기를 계산할 수 있습니다.
*참고자료
>Flash 8: Shape Based Collision Detection

[Source Code]

package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Transform;

public class Hit extends MovieClip
{
var unit_img:BitmapData;//unit의 bitmapData
var unit_bmp:Bitmap;
var interRect:Shape;//겹치는 영역 체크
var alpha_tolerence:uint = 120;//투명한 부분을 어느 정도 겹치는 영역으로 인식할지 정하는 인자값
//unit의 bitmapData 가공
var ctx:ColorTransform = new ColorTransform(1, 1, 1, 1, 255, -255, -255, alpha_tolerence);
//hit target의 bitmapData 가공
var diff_ctx:ColorTransform = new ColorTransform(1, 1, 1, 1, 255, 255, 255, alpha_tolerence);

public function Hit()
{
init();
}

private function init():void
{
unit_img = new BitmapData(unit.width, unit.height)
unit_bmp = new Bitmap(unit_img);
addChildAt(unit_bmp, 0);
interRect = new Shape();
addChild(interRect);
unit.buttonMode = true;
unitDrag();
unitCapture();
}

/////
private function unitDrag():void
{
unit.addEventListener(MouseEvent.MOUSE_DOWN, downHandler);
}

private function downHandler(e:MouseEvent):void
{
unit.startDrag(false);
unit.addEventListener(MouseEvent.MOUSE_UP, upHandler);
unit.removeEventListener(MouseEvent.MOUSE_DOWN, downHandler);
}

private function upHandler(e:MouseEvent):void
{
unit.stopDrag();
unit.removeEventListener(MouseEvent.MOUSE_UP, upHandler);
unitDrag();
}

private function unitCapture():void
{
unit.addEventListener(Event.ENTER_FRAME, capture_unit);
}

private function capture_unit(e:Event):void
{
//unit을 stage크기로 캡처해서 bitmapData로 만들고 colorBox를 DIFFERENCE로 blend해서
//겹치는 부분을 getColorBoundsRect로 찾아
//var img:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
var img:BitmapData = new BitmapData(unit.width, unit.height);
var mtx:Matrix = unit.transform.concatenatedMatrix;
mtx.tx -= unit.x;
mtx.ty -= unit.y;
img.draw(unit, mtx, ctx);

mtx = colorBox.transform.concatenatedMatrix;
mtx.tx -= unit.x;
mtx.ty -= unit.y;
img.draw(colorBox, mtx, diff_ctx, BlendMode.DIFFERENCE);

unit_img.copyPixels(img, new Rectangle(0,0,unit.width, unit.height), new Point());
var inter:Rectangle = img.getColorBoundsRect(0xffffffff, 0xff00ffff);
if (inter.width > 0)
{
drawingInter(inter);
} else {
interRect.graphics.clear();
}
}

private function drawingInter(rec:Rectangle):void
{
interRect.graphics.clear();
interRect.graphics.lineStyle(1, 0xff0000);
interRect.graphics.moveTo(rec.left, rec.top);
interRect.graphics.lineTo(rec.right, rec.top);
interRect.graphics.lineTo(rec.right, rec.bottom);
interRect.graphics.lineTo(rec.left, rec.bottom);
interRect.graphics.beginFill(0xffffff, 0.3);
interRect.graphics.drawRect(rec.x, rec.y, rec.width, rec.height);
interRect.graphics.endFill();
}
}
}
1 comment
  1. jin_u says: November 28, 201011:23 am

    cooool~

    Rectangle의 getColorBoundsRect를 사용해서 변화된 색상을 추출하여 여러개의 면으로 분리하는 작업을 하게 되면 “Motion Detection”이 되는거지 ^^

Submit comment