메뉴 건너뛰기

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

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
번호 제목 글쓴이 날짜 조회 수
105 그럼 질문으로... [1] 쿠로쇼우 2008-09-26 3243
104 흥크립트에 궁금한점 [4] 상상악수 2008-08-21 3835
103 [수정]이거왜이러는거죠;; [4] file 상상악수 2008-08-19 3254
102 srand에 관해서 [4] A.미스릴 2008-07-15 3868
101 COgg 질문 [3] A.미스릴 2008-06-29 3710
100 문D라이브도 더블버퍼링이 필요한가요? [3] A.미스릴 2008-06-28 4223
99 클레스들을 담은 헤더들의 혼란 [4] A.미스릴 2008-06-21 3893
98 씨언어 질문 (내일 시험 ㄷㄷ) [1] 쿠로쇼우 2008-06-17 4334
97 MFC 더블 버퍼링 질문 [2] A.미스릴 2008-06-13 6617
96 문D라이브 2008년 5월 19일 버전 [2] 혼돈 2008-06-02 3749
95 문D 질문 #2 [1] A.미스릴 2008-06-01 8797
94 문D 질문 [5] A.미스릴 2008-05-26 3847
93 #define에 대해 [1] A.미스릴 2008-05-19 4061
92 문D라이브로 더블드래곤을 만들자(11) file 똥똥배 2008-05-17 4647
91 문D라이브로 더블드래곤을 만들자(10) file 똥똥배 2008-05-17 4065
90 문D라이브로 더블드래곤을 만들자(9) 똥똥배 2008-05-17 2889
89 이상하군요. [2] 대슬 2008-05-16 2490
» 문D라이브로 더블드래곤을 만들자(8) [1] file 똥똥배 2008-05-16 2973
87 문D 라이브 질문 [5] 대슬 2008-05-15 2889
86 문D라이브로 더블드래곤을 만들자(7) file 똥똥배 2008-04-27 3480