메뉴 건너뛰기

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

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 날짜 최근 수정일
185 질문요.C++ [7] 질문자01 2729   2007-05-10 2008-04-03 05:32
도대체 파라미터가 뭔지 모르겠어요.  
184 [re] 혼돈님께 질문 혼돈 2048   2007-12-25 2008-03-17 04:37
옛날에 뼈대를 그려놓고 일단 게임을 만들자는 주의였는데 나중에 완성하고 나면 고치기도 귀찮고, 그 그래픽에 맞춰 만든 거라서 그냥 그대로 쓰게 되는 경우가 많았음. 그래서 그 다음부터는 처음부터 그래픽 소스를 제대로 준비한 뒤 시작하는 식으로 했습...  
183 Bootstrap4 container class가 적용된 div의 양 옆에 설정하지 않은 margin이 생김 노루발 10   2024-02-07 2024-02-07 14:25
문제: <div class="container"> TEST </div> 위와 같은 페이지를 브라우저에서 렌더링 시 div의 양 옆에 설정하지 않은 빈 margin이 생김. 해결: <div class="container-fluid"> TEST </div> margin을 0으로 주고 width를 100%로 주고 등등 별걸 다 해봤는데 ...  
182 express.js 세션 적용 후 리다이렉트 시 세션 적용이 제대로 안 되는 문제점 노루발 4   2024-02-07 2024-02-07 14:23
문제: logout.js router.get('/', (req, res) => { req.session.destroy(); res.redirect('/login'); }); login.js if (result[0] !== undefined) { // 로그인에 성공하였으므로 세션을 할당 req.session.uid = result[0].uid; req.session.us...  
181 illegal character 방지 [3] 노루발 22   2023-07-17 2023-07-19 16:14
문제점: 클라이언트 <-> 서버 통신을 하면서 다음과 같이 메세지를 주고받기로 함 패킷종류|파라미터1|파라미터2 예) MOTDREQ -> 서버에 MOTD를 요청 (파라미터 없음) MOTD|공지사항입니다 -> MOTD는 "공지사항입니다" 임 (파라미터 1개) MSG|김덕배|안녕하세...  
180 Lua-love2d TCP 통신 [1] 노루발 25   2023-07-14 2023-07-22 16:19
서버: Lua 클라이언트: Love2d(Lua) 서버 구동에는 luasocket 라이브러리가 필요하며, luarocks로 설치할 수 있음. 별도 패키지 관리자가 있는 리눅스 시스템에서는 apt-get install lua-socket 등의 패키지 관리자 명령어로 설치 가능하며 Windows에서 구동시...  
179 PHP로 웹게임 만드는 영상 [1] 노루발 530   2021-06-25 2022-01-28 03:40
Simple PHP Strategy Game - YouTube  
178 certbot을 이용한 HTTPS 인증서 발급 및 적용 노루발 20   2021-01-12 2021-01-12 16:57
snap 설치 및 업데이트 sudo snap install core; sudo snap refresh core certbot 설치 sudo snap install --classic certbot 심볼릭 링크 생성 sudo ln -s /snap/bin/certbot /usr/bin/certbot nginx에 맞춰 자동 설정 sudo certbot --nginx 알아서 다 해주기...  
177 Love2d 게임 안드로이드로 패키징하기 노루발 49   2021-01-11 2021-02-21 01:45
http://hondoom.com/zbxe/index.php?mid=study&document_srl=797993 버전이 바뀌면서 빌드 방법이 바뀌었기에 다시 정리한다. 1. Android studio 설치 https://developer.android.com/studio/index.html SDK 플랫폼 - Android 11.0 [API 30] SDK 버전 - An...  
176 자동화된 Lua 스크립트의 문서화 - LDoc 노루발 43243   2021-01-11 2021-01-11 11:53
다운로드 https://github.com/lunarmodules/LDoc penlight 설치가 필요 luarocks install penlight 프로젝트가 있는 폴더에서 아래의 명령행을 실행 lua /path/to/ldoc/ldoc.lua $* https://stevedonovan.github.io/ldoc/manual/doc.md.html 문서 코멘트라는걸...  
175 리캡챠 적용 [1] 노루발 11   2021-01-08 2021-01-11 12:15
XE 회원가입 시 구글 리캡챠 인증 추가하기 : 네이버 블로그 (naver.com)  
174 RPG Maker MV 로컬라이징 방법 file 똥똥배 932   2015-10-27 2015-10-27 05:51
 
173 김프로 이미지 맵 만들기 노루발 393   2015-11-11 2015-11-11 08:05
https://docs.gimp.org/en/plug-in-imagemap.html  
172 [번역] gamedev레딧의 Getting Started 문서 번역 [5] priling 1893   2014-12-26 2018-07-24 10:33
처음인 분들을 위한 '게임만들기' 가이드이 글은 [레딧 게임개발 커뮤니티의 /u/LordNed님의 포스팅]을 베이스로 작성한 것입니다. 이 글의 목적은 게임을 만들고 싶어하는 분들이 어떻게 시작할 수 있을지에 대해 명확한 가이드라인을 보여드리는 것입니다.  ...  
171 Love2d 안드로이드 게임 패키징하기 [3] 노루발 551   2014-12-15 2021-01-11 12:11
이 문서는 개발 환경이 갖추어져 있는 상태이고, 빌드를 무사히 마친 뒤라고 가정합니다. 또한 이 문서는 https://bitbucket.org/MartinFelis/love-android-sdl2/wiki/Game%20Packaging#markdown-header-how-to-package-the-apk-with-your-own-love-090-game ...  
170 Love2d 안드로이드 빌드하기 노루발 575   2014-12-15 2014-12-15 00:59
Love2d의 안드로이드 포트는 알파 단계입니다. 차차 개선되어 나가긴 하겠지만 아직 불안정한 부분이 많으며, 일어날 수 있는 오작동과 그로 인한 결과는 일절 책임지지 않습니다.   이 문서는 Windows 사용자 기준입니다. Linux나 Mac을 사용할 정도의 내공...  
169 Love2d 여러 플랫폼으로 빌드 자동화 노루발 476   2014-11-12 2014-11-12 17:35
https://github.com/MisterDA/love-release/blob/master/README.md http://www.ambience.sk/lua-love2d-game-distribution/  
168 구글 인앱 구매 Soomla로 구현해본 후 팁 똥똥배 577   2014-09-20 2014-09-20 18:45
1. 일단 알파 테스트라도 앱을 출시시켜야 인앱이 작동한다. 그리고 APK 업로드 후에 바로 인앱이 적용되지 않는다. 구글에 게시되는 데, 시간이 걸리므로 인앱 등록 - APK 등록을 마친 후 다음날부터 구현하는 게 깔끔하다. 이걸로 모르고 하루종일 왜 안되나...  
167 Love2d 게임 중간에 광고 표시 [1] 노루발 378   2015-11-12 2015-11-17 00:16
http://love2d.org/forums/viewtopic.php?f=11&t=81224  
166 cocos2d-x 2.2.2 문자열 출력 버그 [2] 똥똥배 602   2014-06-10 2014-08-23 22:35
cocos2d-x 2.2.2, 맥에서만 나타는 문제다. 윈도우, 안드로이드에서는 아무 이상 없다. 줄 수가 꽤 긴 문자열을 출력할 경우, 문자가 전부 다 나오지 않고 잘린다. 잘리는 기준은 줄이다. 문자 수가 어떻든 간에 줄 수에 따라서 잘려버린다. 아마 문자열의 줄 ...