메뉴 건너뛰기

창작에 관련된 질문이나 간단한 팁, 예제를 올리는 곳

8편 : 충돌

오랜만에 적는 김에 본격적인 것을 적어 보겠습니다.
일단 아래의 그림을 봅시다.



파란색 사각형은 공격을 당하면 대미지를 입게 되는 몸통 영역,
빨간 부분은 적의 몸통 영역이 닿이면 대미지를 입는 공격 영역 입니다.
일단 완벽하게 그림하고 일치하지 않는 것은 처리 속도를 빠르게 하기 위함이오,
어차피 발차기 동작 같은 것은 순식간이라서 잘 보이지도 않습니다.
어차피 게임이 눈속임이고 사기지요.

어쨌든 저 영역을 대충 정해서 충돌을 만들어 봅시다.

//유닛 클래스
class Unit
{
private:
     int x;                    //X좌표
     int y;                    //Y좌표
     int z;                    //Z좌표(점프한 높이)
     int delay;               //애니메이션용 변수
     int state;               //현재 상태
     bool left_punch;     //왼팔, 오른팔 펀치질 검사
     int jump_power;          //점프력
     int speed;               //속도
     RECT body;               //몸통
     RECT attack;          //공격영역

public:
     void Action();                              //상태에 따라 행동을 함
     void Control();                              //키 입력을 받아 들임


     void AI(int hero_x, int hero_y);     //컴퓨터의 행동
     int GetX();
     int GetY();

     Unit(int x, int y, int speed);
};

저 body와 attack이 공격과 몸통 영역입니다.
RECT는 사각형 구조체입니다.
(왼쪽, 위, 오른쪽, 아래)의 좌표를 가져서 사각형을 나타낼 수 있습니다.
많이 쓰는 구조체죠.
저 영역은 매번 액션이 끝난 후에 정해줍시다.

다음 코드를 CUnit::Action()의 마지막에 추가합시다.

 //몸통 재설정
 SetRect(&body, x, y, x+38, y+56);

38, 56은 걸을 때의 대충 크기입니다.
일단 배우는 거니 꼼꼼하지 않게 대충 하겠습니다.
꼼꼼하신 분은 매 걷는 동작마다 크기를 다르게 해주시든지요.

그 다음은 공격 영역.
공격 영역의 경우에는 조금 다른 것이 펀치일 때와 킥일 때의 영역이 틀리고,
평상시에는 영역이 존재하지 않아야 합니다.
이걸 어떻게 할 것인가!!

아래를 봅시다.

     //평상시
     if(state==0)
     {
          if(delay<30)jdd->DrawPicture(backbuffer, "걷기2", x, y-2, NULL);
               else if(delay<60)jdd->DrawPicture(backbuffer, "걷기1", x, y, NULL);
               else if(delay<90)jdd->DrawPicture(backbuffer, "걷기3", x, y, NULL);
               else jdd->DrawPicture(backbuffer, "걷기1", x, y, NULL);

          ++delay;
          if(delay >= 120)delay=0;

          attack.left = -999;
     }
     //펀치
     else if(state==1)
     {
          if(left_punch)jdd->DrawPicture(backbuffer, "펀치1", x-8, y-2, NULL);
               else jdd->DrawPicture(backbuffer, "펀치2", x-8, y-2, NULL);

          ++delay;
          if(delay>=10)state=0;

          SetRect(&attack, x-8, y+16, x+8, y+30);
     }
     //킥
     else if(state==2)
     {
          jdd->DrawPicture(backbuffer, "킥", x-6, y, NULL);

          ++delay;
          if(delay>=10)state=0;

          SetRect(&attack, x-6, y+30, x+18, y+44);
     }
     //점프
     else if(state==3)
     {
          jdd->DrawPicture(backbuffer, "걷기1", x, y-z, NULL);

          z+=jump_power/10;
          jump_power-=GRAVITY;
         
          //착지
          if(z <= 0)
          {
               z=0;
               state=0;
          }

          attack.left = -999;
     }

빨간 부분이 새로 추가된 부분이란 것은 당연한 것이고,
attack.left = -999는 영역이 존재하지 않는다는 암호와 같은 겁니다.
왜냐면 정상적으로 attack이란 사각형 영역의 왼쪽이 -999가 나올리가 없기 때문입니다.
그러므로 이 -999값을 '존재하지 않음'이란 의미로 정한 겁니다.
좀 더 이해하기 쉽게 만들고 싶으면 -999 대신

#define GRAVITY 9

의 밑에

#define NOT_ATTACK -999

란 매크로를 선언하고 -999대신 NOT_ATTACK을 적어줍니다.

attack.left = NOT_ATTACK;

공격하지 않는다는 것을 아까 전보다 쉽게 이해할 수 있겠죠?

이번엔 공격하는 경우를 봅시다.

SetRect(&attack, x-6, y+30, x+18, y+44);

저 공격 영역의 숫자는 아래와 같이 도트를 보면서 계산해 봅시다.
곰곰히 생각해 봅시다. 이것까지 설명해 주기는 귀찮으니까요.



이제 영역 설정은 다 됐으니 충돌 검사를 해봅시다.
아, 그전에 영역을 받아 올 수 있는 GetBodyRect와 같은 것이 필요하겠죠.

 RECT GetBodyRect();
 RECT GetAttackRect();
 void Damage();

저 3개를 Unit 클래스에 추가합니다.
메인에서 불러야 하니 public 영역에 넣어야 한다는 것을 주의하십시오.
Damage()는 대미지를 입었을 때 처리인데 미리 넣겠습니다.

이제 아래에는 선언한 함수들을 정의해야 겠죠?

RECT Unit::GetBodyRect()
{
     return body;
}

RECT Unit::GetAttackRect()
{
     return attack;
}

void Unit::Damage()
{
     x-=100;
}

별거 없습니다.
GetBodyRect 에서는 몸통 영역을 돌려주고,
GetAttackRect 에서는 공격 영역을 돌려줍니다.
Damage 영역에서는 공격을 받았을 때의 처리인데
지금은 hp도 없으니 그냥 왼쪽으로 100픽셀 이동하게 했습니다.

마지막으로 메인 루프에 충돌 처리를 넣어봅시다.

//메인 실행
while(!GetKey(vkey_esc))
{
     if(!ProcessMessage())break;

     jdd->DrawPicture(backbuffer, "배경", 0, 0, NULL);

     hero.Control();
     enemy.AI(hero.GetX(), hero.GetY());

     hero.Action();
     enemy.Action();

     //충돌 검사
     RECT attack = hero.GetAttackRect();
     RECT body = enemy.GetBodyRect();

     if(attack.left != -999)     //공격영역이 존재할 때
     {
          if(attack.right >= body.left && attack.left <= body.right && attack.bottom >= body.top && attack.top <= body.bottom)
          {
               enemy.Damage();                                  
          }
     }

     jdd->Render();
}

일단 지금은 적을 때리는 것만 처리하겠습니다.
attack이란 RECT 구조체에 주인공의 공격영역을 GetAttackRect로 받아오고,
body란 RECT 구조체에 적의 몸통영역을 GetBodyRect로 받아옵니다.

그 다음은 공격영역의 존재여부 검사.
attack.left가 -999란 소리는 존재하지 않는다는 소리이니 영역을 검사하지 않습니다.
만약 공격영역이 존재한다면 두 사각형이 겹치는지 아래의 검사식으로 검사합니다.

attack.right >= body.left && attack.left <= body.right && attack.bottom >= body.top && attack.top <= body.bottom

원리는 아래의 그림과 같습니다.



일단 가로로 겹치는지 검사입니다.
사각형과 사각형의 평행 위치 관계는 3가지가 있을 겁니다.
attack이 body보다 왼쪽에 있는 경우 : attack.right < body.left
attack이 body보다 오른쪽에 있는 경우 : attack.left > body.right

마지막 경우가 둘이 겹치는 경우가 되겠죠?
즉, 위의 두 경우에 속하지 않을 때입니다.

attack.right < body.left의 반대는 attack.right >= body.left
attack.left > body.right의 반대는 attack.left <= body.right
물론 두 조건을 모두 충족해야 하니 && 입니다.

수직선상의 겹치는 관계도 원리가 똑같습니다.
그래서 최종적으로 나오는 검사식이

attack.right >= body.left && attack.left <= body.right && attack.bottom >= body.top && attack.top <= body.bottom

가 되는 겁니다.


실행해보면 맞을 때마다 밀려나는 적을 볼 수 있습니다.
이번 편은 약간 어려웠을 수도 있으니 main.cpp를 첨부하겠습니다.
못 따라오신 분들은 이것을 보면서 따라하길 바랍니다.

main.cpp

대슬

2008.05.16
20:47:18
(*.132.163.161)
헉, 이런 기능이 있었군요. 예전에 만들 때는 이런 것도 모르고 만들었었네..
List of Articles
번호 제목 글쓴이 날짜 조회 수sort
105 흥크립트 기본 바꾸기. [3] 장펭돌 2008-02-03 1855
104 혼돈형에게 질문! [3] 장펭돌 2007-11-28 1851
103 흥크립트 AVI 재생.. [2] 장펭돌 2008-02-14 1851
102 반복문 예제 [4] file 대슬 2007-12-01 1833
101 흥크립트 AVI 지원에서... [2] file 장펭돌 2008-02-02 1823
100 마우서 포인트 바꾸기 질문 다시 ㄱ- [2] 장펭돌 2008-02-05 1807
99 클릭 문제.... ㄱ- [1] 장펭돌 2008-01-31 1801
98 [re] 흥크립트 질문.. [2] 똥똥배 2008-02-07 1797
97 흥크립트에서 반복문 실행하기 (덤추가) [9] 대슬 2007-12-01 1790
96 [re] 흥크립트 질문 똥똥배 2008-02-01 1782
95 흥크립트 다루다 보니 알아낸게 있는데... [4] 네모상자 2008-01-18 1765
94 흥크립트 버그발견 [7] 네모상자 2008-01-22 1754
93 흥크립트배경 어떤식으로 넣어야됨? [2] 세균맨 2007-12-10 1751
92 마우스 포인터 바꾸기가 안되네요... +질문2 장펭돌 2008-02-04 1748
91 흥크립트 질문.. [3] 궤레브 2008-02-07 1747
90 조건분기할 때 [5] 흑곰 2008-02-12 1745
89 액션알피지 원거리 무기 질문 [8] 아리포 2006-01-18 1741
88 갑자기 급질문.. 궤레브 2008-02-08 1737
87 흥크립트 질문 [1] 장펭돌 2008-02-01 1733
86 똥똥배님께 질문 [6] 흑곰 2008-01-09 1730