Flash CS4 + ActionScript 3.0으로 게임 수학/물리 구현 공부 계속.

3-1: 사각형끼리의 충돌판정.

파란 박스와 빨간 박스는 각각 마우스로 드래그가 가능하고 충돌하면 gotoAndStop()을 이용해 아픈 표정으로 바뀐다.

충돌체크는 일단 각 박스의 상하좌우값을 구하고 그 값들을 이용해 충돌여부를 판단한다.

14/12/10 수

소스(화면에 미리 'boxBm' 'boxRm'이란 이름의 파란색, 빨간색 박스 무비클립을 만들어두었다.):

var boxB = boxBm;
var boxR = boxRm;
var hit:Boolean=false;

function init(){
	boxB.gotoAndStop(1);//박스 일반 표정으로
	boxR.gotoAndStop(1);
	boxB.addEventListener(MouseEvent.MOUSE_DOWN,mDown);
	boxB.addEventListener(MouseEvent.MOUSE_UP,mUp);
	boxR.addEventListener(MouseEvent.MOUSE_DOWN,mDown);
	boxR.addEventListener(MouseEvent.MOUSE_UP,mUp);
	stage.addEventListener(Event.ENTER_FRAME,loop);
}
function mDown(e:MouseEvent){
	//선택한 박스 맨 위로 보내기.
	e.currentTarget.parent.setChildIndex(e.currentTarget, 
				e.currentTarget.parent.numChildren-1);
	e.currentTarget.startDrag();//박스 마우스 따라다니기
}
function mUp(e:MouseEvent){
	e.currentTarget.stopDrag();//박스 마우스 따라다니기 중지
}
function loop(e:Event){
	checkHit(boxB, boxR); //두 박스 충돌체크
	if(hit){ //두 박스 충돌시 처리
		boxB.gotoAndStop(2);//박스 놀란 표정으로
		boxR.gotoAndStop(2);
	}else{
		boxB.gotoAndStop(1);//박스 일반 표정으로
		boxR.gotoAndStop(1);
	}
}
function checkHit(aBox, bBox){ //두 박스 충돌체크
	boxSet4corner(aBox);//박스의 상하좌우값 구하기
	boxSet4corner(bBox);
	if((aBox.right > bBox.left)&&(aBox.left < bBox.right)){
		if((aBox.down > bBox.top)&&(aBox.top < bBox.down)){
			hit = true;
		}else{ hit=false; }
	}else{ hit=false; }
}
function boxSet4corner(mov){//박스의 상하좌우값 구하기
	mov.left = mov.x;
	mov.right = mov.x + mov.width;
	mov.top = mov.y;
	mov.down = mov.y + mov.height;
}
init();

 


3-2: 원형끼리의 충돌판정.

파란 원와 빨간 원은 각각 마우스로 드래그가 가능하고 충돌하면 gotoAndStop()을 이용해 아픈 표정으로 바뀐다.

충돌체크는 두 원의 중심 사이 거리를 구하고 그 값이 두 원의 반지름 합 보다 작으면 충돌로 처리한다. 원래는 거리의 제곱근 값과 반지름 합을 비교해야 하는데 아래 코드에선 거리의 제곱과 반지름 합의 제곱과의 비교로 바뀌어 있다. 이유는 컴의 경우 제곱근 계산에 시간이 더 많이 걸리기에 동일한 결과를 얻을 수 있되 보다 빠른 제곱간 비교로 바꾼 것.

소스(화면에 미리 'cirBm' 'cirRm'이란 이름의 파란색, 빨간색 원 무비클립을 만들어두었다.):

var cirB = cirBm;
var cirR = cirRm;
var hit:Boolean=false;
var dx,dy; //거리 차 저장용 변수
var distSqr; //거리 차의 제곱근 저장용
var ar; //반지름의 합 저장용

function init(){
	cirB.r = cirB.width/2;//원들의 반지름
	cirR.r = cirR.width/2;
	cirB.gotoAndStop(1);//원 일반 표정으로
	cirR.gotoAndStop(1);
	cirB.addEventListener(MouseEvent.MOUSE_DOWN,mDown);
	cirB.addEventListener(MouseEvent.MOUSE_UP,mUp);
	cirR.addEventListener(MouseEvent.MOUSE_DOWN,mDown);
	cirR.addEventListener(MouseEvent.MOUSE_UP,mUp);
	stage.addEventListener(Event.ENTER_FRAME,loop);
}
function mDown(e:MouseEvent){
	//선택한 원 맨 위로 보내기.
	e.currentTarget.parent.setChildIndex(e.currentTarget, 
				e.currentTarget.parent.numChildren-1);
	e.currentTarget.startDrag();//원 마우스 따라다니기
}
function mUp(e:MouseEvent){
	e.currentTarget.stopDrag();//원 마우스 따라다니기 중지
}
function loop(e:Event){
	checkHitCircle(cirB, cirR); //두 원 충돌체크
	if(hit){ //두 원 충돌시 처리
		cirB.gotoAndStop(2);//원 놀란 표정으로
		cirR.gotoAndStop(2);
	}else{
		cirB.gotoAndStop(1);//원 일반 표정으로
		cirR.gotoAndStop(1);
	}
}
function checkHitCircle(aCir, bCir){ //두 원 충돌체크
	dx = aCir.x - bCir.x; //두 원 중심간 거리 구하기
	dy = aCir.y - bCir.y;
	distSqr = dx * dx + dy * dy;
	ar = aCir.r + bCir.r; //두 원의 반지름 합
	//두 원의 중심간 거리가 반지름 합 보다 작으면 충돌
	if(distSqr < ar * ar){
		hit = true;
	}else{ hit = false; }
}
init();


3-2a: 원형과 사각형 간의 충돌판정.

파란 원와 빨간 사각형은 각각 마우스로 드래그가 가능하고 충돌하면 gotoAndStop()을 이용해 아픈 표정으로 바뀐다.

충돌체크는
1. 판정할 사각형의 상하좌우로 각각 원의 반지름 만큼 확장시킨 사각형 안에 원의 중심좌표가 포함되면 충돌가능성이 있다.
2. 1의 조건을 만족해도 원의 중심좌표가 원래 사각형의 밖, 확장된 사각형들의 각 모퉁이에 있고 또 원이 사각형의 가장 가가운 정점을 내부에 포함하지 않을 경우 충돌하지 않았다.
위 2단계로 나눠 체크한다.

소스(화면에 미리 'cirBm' 'boxRm'이란 이름의 파란색 원, 빨간색 사각형 무비클립을 만들어두었다.):

var cirB = cirBm; //파란 원
var boxR = boxRm; //빨간 사각형
var hit:Boolean=false;
var dx,dy; //거리 차 저장용 변수

function init(){
	cirB.r = cirB.width/2;//파란 원의 반지름
	cirB.gotoAndStop(1);//원 일반 표정으로
	boxR.gotoAndStop(1);
	cirB.addEventListener(MouseEvent.MOUSE_DOWN,mDown);
	cirB.addEventListener(MouseEvent.MOUSE_UP,mUp);
	boxR.addEventListener(MouseEvent.MOUSE_DOWN,mDown);
	boxR.addEventListener(MouseEvent.MOUSE_UP,mUp);
	stage.addEventListener(Event.ENTER_FRAME,loop);
}
function mDown(e:MouseEvent){
	//선택한 원 맨 위로 보내기.
	e.currentTarget.parent.setChildIndex(e.currentTarget, 
				e.currentTarget.parent.numChildren-1);
	e.currentTarget.startDrag();//원 마우스 따라다니기
}
function mUp(e:MouseEvent){
	e.currentTarget.stopDrag();//원 마우스 따라다니기 중지
}
function loop(e:Event){
	checkHitCB(cirB, boxR);//원과 박스간 충돌체크
	if(hit){ //두 원 충돌시 처리
		cirB.gotoAndStop(2);//원 놀란 표정으로
		boxR.gotoAndStop(2);
	}else{
		cirB.gotoAndStop(1);//원 일반 표정으로
		boxR.gotoAndStop(1);
	}
}
function checkHitCB(aCir, bBox){ //원과 박스간 충돌체크
	hit = false;
	boxSet4corner(bBox);//박스의 상하좌우값 구하기
	//큰 장방형 체크
	if((aCir.x > bBox.left-aCir.r)&&(aCir.x < bBox.right+aCir.r)&&
		(aCir.y > bBox.top-aCir.r)&&(aCir.y < bBox.down+aCir.r)){
		hit = true;
		var ar = aCir.r; //글자수 줄이기 위해 변수 채용
		//왼쪽 끝 체크
		if(aCir.x < bBox.left){
			//좌측상단 모서리와 원 중심간 거리와 
			//원의 반지름과의 크기를 비교하여
			//거리가 반지름보다 짧으면 충돌
			if(aCir.y < bBox.top){
				if(distSqr(bBox.left, bBox.top, 
				   aCir.x, aCir.y) >= ar*ar){
					hit = false;
				}
			}else{
				//좌측하단 모서리 체크
				if(aCir.y > bBox.down){
					if(distSqr(bBox.left, bBox.down, 
					   aCir.x, aCir.y) >= ar*ar){
						hit = false;
					}
				}
			}
		}else{
			//오른쪽 끝 체크
			if(aCir.x > bBox.right){
				//우측 상단 모서리 체크
				if(aCir.y < bBox.top){
					if(distSqr(bBox.right, bBox.top, 
					   aCir.x, aCir.y) >= ar*ar){
						hit = false;
					}
				}else{
					//좌측 하단 모서리 체크
					if(aCir.y > bBox.down){
						if(distSqr(bBox.right, bBox.down, 
						   aCir.x, aCir.y) >= ar*ar){
							hit = false;
						}
					}
				}
			}
		}
	}
}
function boxSet4corner(mov){//박스의 상하좌우값 구하기
	mov.left = mov.x;
	mov.right = mov.x + mov.width;
	mov.top = mov.y;
	mov.down = mov.y + mov.height;
}
function distSqr(x1, y1, x2, y2){//거리의 제곱 계산
	dx = x2 - x1;
	dy = y2 - y1;
	return dx * dx + dy * dy;
}
init();

* 위의 충돌 체크 예제들은 순수한 수학적인 알고리즘을 통한 결과들인 탓에 다른 어떤 프로그래밍 언어들에도 적용이 가능한 방법들이긴 하다만 제법 길고 복잡한데 사실 ActionScript 3.0에는 보다 간단하고 일반적인 충돌체크 관련 해결책이 몇 개 존재한다.

1. 두개의 Object 사이의 충돌: Sprite1.hitTestObject(Sprite2)
2. Object와 점 사이의 충돌: Sprite1.hitTestPoint(x, y, shapeFlag(true/false)) -shapeFlag를 true로 설정하면 경계박스가 아닌 시각적인 그래픽 부분과의 충돌을 체크한다.
3. 두개의 BitmapData 사이의 충돌: bitmapData1.hitTest(Point, alpha,bitmapData2, Point, alpha)

사각형끼리의 충돌은 hitTestObject()를 이용하면 되고 원과 원, 원과 사각형, 혹은 더 복잡한 어떤 도형들간의 충돌은 3번째 hitTest()를 이용하면 된다.

관련내용은 14/7/21((월)의 일기 참고.