메뉴 건너뛰기

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

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 명령문 질문드립니다. [1] X-tra 2244   2008-03-26 2008-03-27 00:40
변해서 게시판 찾기 어려울것 같았는데 이상하게 한번에 찾아 버렸네요. 연구소라고 적혀 있어서요... 이번건 질문이라고 하기에는 좀 애매합니다. 예전에 흥크립트의 모든 명령어가 적혀 있던 파일을 본 기억이 납니다. 하지만 아무리 찾아도 없더군요. 블로...  
104 OgreOde 사용기 똥똥배 3184   2008-03-25 2008-03-25 21:37
OgreSDK 버전 1.4.7 OgreOde 버전 0.95(아마도) 사실 오우거 엔진 쓴지도 얼마 안 되고 물리엔진은 처음 만져봤습니다. 처음에 Ogre Wiki에 나온대로 따라서 만들었는데 crateCube.mesh와 plane.mesh 때문에 에러가 나서 바닥은 직접 만들고 crateCube 대신 오...  
103 저는 사실 이걸 잘 못해요. [3] 앟랄 2282   2008-03-26 2008-04-18 07:45
알만툴 액알. 원리가 도저히 이해가 안 갑니다. 그림넣어서 설명해주세요. 그 뭐시기 그냥 근접공격만 일단. 나중가서 원거리를 합시다.  
102 MFC 더블 버퍼링 질문 [2] A.미스릴 6603   2008-06-13 2013-11-23 08:43
void CPingpongView::OnDraw(CDC* pDC) { CPingpongDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here // 현재 창 크기와 같은 DC를 만듬 CRect rect; GetClientRect(&rect); int int_client_width = (rect.ri...  
101 임의의 점이 다각형 내부에 있는지 검사하는 함수 똥똥배 5358   2008-04-14 2008-04-14 02:21
수학도 기하학도 허접한 제가 짠 거라서 허접하지만 넓은 아량으로 봐 주십시오. struct Point { double x; double y; }; bool LineIn(Point p1, Point p2, Point p3) { double top, bottom; //위 아래 범위 결정 if(p1.y < p2.y) { top = p1.y; bottom = p2.y...  
100 문D라이브로 더블드래곤을 만들자(1) [2] file 똥똥배 4479   2008-04-16 2008-04-17 08:29
 
99 VC++ 2008 Express Edition에서 문D라이브 링크 [2] A.미스릴 13090   2008-04-17 2014-04-15 17:28
SDK도 최신버전으로 깔아주었고 도구 설정도 적절하게 했습니다 모든 셋팅이 완료되고 링크를 해보니까 아래와 같은 문장이 쭉쭉나오는군요 warning은 굉장히 많이 나오는데 실제로 컴파일을 막아버린 부분은 LINK : fatal error LNK1104: 'LIBC.lib' 파일을 ...  
98 문D라이브로 더블드래곤을 만들자(2) [6] file 똥똥배 3612   2008-04-18 2013-11-23 08:43
 
97 문D라이브로 더블드래곤을 만들자(3) file 똥똥배 3430   2008-04-18 2008-04-18 18:27
 
96 문D라이브로 더블드래곤을 만들자(4) [2] file 똥똥배 3213   2008-04-20 2008-04-21 07:20
 
95 C++ 데이터의 바이트 용량 임의로 정의할수 없나영 [1] A.미스릴 3059   2008-04-21 2008-04-21 07:18
int는 4바이트로 정해져 있는데 약간의 수만 있으면 되는 수도 있는데 괜히 많은 숫자를 사용해서 메모리를 많이 사용하는 건 아닐지 ㅡㅡ; 3바이트라던지 4비트라던지... 데이터의 바이트 사용량을 임의로 바꿀수 없나요  
94 문D라이브로 더블드래곤을 만들자(5) [6] file 똥똥배 3072   2008-04-21 2008-04-24 03:23
 
93 문D라이브로 더블드래곤을 만들자(6) [2] file 똥똥배 3114   2008-04-23 2008-04-25 05:01
 
92 문D라이브로 더블드래곤을 만들자(7) file 똥똥배 3480   2008-04-27 2008-04-28 04:22
 
91 문D라이브로 더블드래곤을 만들자(11) file 똥똥배 4647   2008-05-17 2008-05-17 20:24
 
90 #define에 대해 [1] A.미스릴 4061   2008-05-19 2008-05-19 18:57
#define을 너무 많이 쓰면 코드에 불안정화가 온다는데 사실인가여? 그러면 불안정화를 막으러면 어떻게 해야하나여 아니면 enum이나 const를 써야 하나...  
89 문D 질문 [5] A.미스릴 3847   2008-05-26 2008-06-12 18:55
1.글자, 문장을 화면상에 출력하는 방법 2.숫자를 스트링으로 형변환할떄 아스키 코드가 아니라 숫자가 옮겨가게 하는법 3.사각형, 원 등을 출력하는 방법(Rectangle 함수같은거 없나여)  
88 문D 질문 #2 [1] A.미스릴 8797   2008-06-01 2009-01-07 22:05
이거 도배하는거같은 마음이 드네여 ㅈㅅ 제가 대화창을 다뤄봤는데여 CTextDlg textdialog(50); CTextDlg * pt_textdialog(&textdialog); pt_textdialog->SetDlg(100, 100, 200, 200); pt_textdialog->SetColor(255,255,0); pt_textdialog->SetDlgBox("_d...  
87 문D라이브 2008년 5월 19일 버전 [2] 혼돈 3747   2008-06-02 2015-12-09 08:06
휴가루 웍휴2를 개발하면서 조금씩 수정 중이지만, 미스릴님의 요청이 있어서 올립니다. 대화창에 관련되서 완성되었다는 것과 흥크립트 부분이 많이 변했다는 것이 주요 변경점입니다. 대화창의 경우 기본은 대화창이 없는 상태로 MakeDlgBox를 사용하면 크기...  
86 씨언어 질문 (내일 시험 ㄷㄷ) [1] 쿠로쇼우 4332   2008-06-17 2008-06-17 22:10
1 121 12321 1234321 123454321 부탁 ㄷㄷ