메뉴 건너뛰기

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

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 추천 수 날짜 최근 수정일
165 COgg 질문 [3] A.미스릴 3710   2008-06-29 2013-11-23 08:43
밑에 대슬님 질문 보니까 멤버변수를 등록할때 동적할당을 하는게 씌여 있는데 그냥 Cogg m_ogg_oggplayer; 이런식으로 그냥 일반변수로 등록을 하고 사용할순 없나요? 동적할당을 굳이 써야하는지 ㅡ.ㅡ;; (개인적으로 4바이트 추가와 잘못되면 메모리 누수가...  
164 문D라이브로 더블드래곤을 만들자(2) [6] file 똥똥배 3611   2008-04-18 2013-11-23 08:43
 
163 C++ 질문 2 [3] A.미스릴 3565   2008-12-22 2008-12-23 18:47
바이너리(2진수) 저장 아시죠? 그런데 포인터 변수를 만들어서 동적 할당을 했을때 그 포인터 변수도 포함되어 저장되면 정상적으로 저장이 안 되는 건가요? 왜 그러냐면 제가 저장 방식을 모든 데이터를 하나의 클래스 속에 넣어서 그 1개의 클래스 인스턴스...  
162 문D라이브로 더블드래곤을 만들자(7) file 똥똥배 3480   2008-04-27 2008-04-28 04:22
 
161 lua와 C의 연동에서 상수(define이나 enum) 값처리 똥똥배 3448   2011-05-25 2011-05-25 23:34
이 문제의 해결에 4가지 방법이 나왔다. 물론 환경이나 상황에 따라 더 많은 해결방법이 나올 수 있고, 어떤 것이 답인지는 그때 그때 마다 다른다. 일단 상황부터 정리. 스프라이터를 처리하는 툴에서는 스프라이터 애니메이션을 숫자가 아닌 특정 코멘트를 ...  
160 흥크립트 질문. 글자에 관해서 [1] 에리 3430   2009-03-21 2009-03-21 09:30
예를 들어 글자입력으로 ㅇㅇㅇㅊ ㄱㄴㄱㅇ ㅅㅌ ㅋㄴㅇ ㄴㄴㅇ 대충 이런 글자들을 입력했다 치고 분기 명령어로 분기를 만들 수 있나요?  
159 문D라이브로 더블드래곤을 만들자(3) file 똥똥배 3430   2008-04-18 2008-04-18 18:27
 
158 [수정]이거왜이러는거죠;; [4] file 상상악수 3254   2008-08-19 2008-08-21 03:24
 
157 그럼 질문으로... [1] 쿠로쇼우 3243   2008-09-26 2009-01-07 22:05
c++에서 텍스트파일을 입,출력 하는것은 어느정도 알겠습니다. 문자라든가, 띄어쓰기, <┘ 세고, 출력하는것도 어느정도 알겠고요,, 근데 한줄 단위로 입력하고, 한줄 단위로 출력하는건 어떻해야 하나요? 오늘 아침 질문(??) 올린거에서 텍스트 파일 입력한거 ...  
156 문D라이브로 더블드래곤을 만들자(4) [2] file 똥똥배 3213   2008-04-20 2008-04-21 07:20
 
155 OgreOde 사용기 똥똥배 3184   2008-03-25 2008-03-25 21:37
OgreSDK 버전 1.4.7 OgreOde 버전 0.95(아마도) 사실 오우거 엔진 쓴지도 얼마 안 되고 물리엔진은 처음 만져봤습니다. 처음에 Ogre Wiki에 나온대로 따라서 만들었는데 crateCube.mesh와 plane.mesh 때문에 에러가 나서 바닥은 직접 만들고 crateCube 대신 오...  
154 문D라이브로 더블드래곤을 만들자(6) [2] file 똥똥배 3114   2008-04-23 2008-04-25 05:01
 
153 문D라이브로 더블드래곤을 만들자(5) [6] file 똥똥배 3072   2008-04-21 2008-04-24 03:23
 
152 C++ 데이터의 바이트 용량 임의로 정의할수 없나영 [1] A.미스릴 3059   2008-04-21 2008-04-21 07:18
int는 4바이트로 정해져 있는데 약간의 수만 있으면 되는 수도 있는데 괜히 많은 숫자를 사용해서 메모리를 많이 사용하는 건 아닐지 ㅡㅡ; 3바이트라던지 4비트라던지... 데이터의 바이트 사용량을 임의로 바꿀수 없나요  
151 똥똥배의 게임대회 이야기(3) [1] 혼돈 3007   2007-02-08 2011-02-11 12:53
3. 아마추어다운 게임이란 무엇인가? 아마추어는 자유롭습니다. 자신이 원한다면 무엇이든 표현할 수 있습니다. (심하게 비윤리적이라면 문제가 되겟지만) 하지만 프로는 그렇지 못합니다. 일단 상업성 때문에 '돈이 되는' 게임을 만들어야 하고, 수많은 사람...  
» 문D라이브로 더블드래곤을 만들자(8) [1] file 똥똥배 2973   2008-05-16 2009-01-07 22:05
8편 : 충돌 오랜만에 적는 김에 본격적인 것을 적어 보겠습니다. 일단 아래의 그림을 봅시다. 파란색 사각형은 공격을 당하면 대미지를 입게 되는 몸통 영역, 빨간 부분은 적의 몸통 영역이 닿이면 대미지를 입는 공격 영역 입니다. 일단 완벽하게 그림하고 일...  
149 똥똥배의 게임대회 이야기(1) 혼돈 2953   2007-02-06 2008-03-17 04:37
지금부터 제 이야기를 잘 들어 두시는 게 수상의 열쇠가 될지도 모릅니다. 제가 끝판 보스도 아니고, 공지 내놓고 마감일까지 기다리는 것은 지루하니 이런 저런 이야기를 들려 드리도록 하죠. 1. 지금 여러분이 해야 할 일 지금 개학인데 왜 하필 시작하냐는 ...  
148 문D 라이브 질문 [5] 대슬 2889   2008-05-15 2008-05-16 06:13
1. 음악이나 사운드는 어떻게 불러와서 출력하나요. 배웠었는데 까먹었음. 2. 그림을 원하는 각도로 자유롭게 회전시켜서 출력하는 기능은 없나요?  
147 문D라이브로 더블드래곤을 만들자(9) 똥똥배 2889   2008-05-17 2008-05-17 03:07
9편 : 체계화된 동작 지금까지 열심히 이 강의를 따라오신 분들이라면 지금 게임의 여러 버그가 산재해 있음을 깨달으셨을겁니다. 그걸 왜 그냥 내버려 뒀나면... 귀찮아서~ ~는 훼이크고 일단 가르치는 주제에서 벗어나면 집중력이 떨어지고 어느 정도 이해력...  
146 똥똥배의 게임대회 이야기(4) [1] 혼돈 2881   2007-02-10 2011-02-11 12:52
4. 공모전도 커뮤니케이션 게임을 만들기 전에 먼저 생각해야 하는 것은 게임을 하는 사람들입니다. 그래서 게임회사라면 시장조사가 가장 먼저 해야 할 일입니다. 회사에서 만든 게임을 얼마나 많은 게이머들이 즐길지 알고 예상되는 수익을 계산하고 거기에 ...