adsense



[JAVA/slick2d] 2D 슈팅게임 위치즈 스크램블 제작강좌 -step 7-

step 7. 충돌의 기술

다음 작업은
- 총알과 적 캐릭터의 충돌
- 적 캐릭터의 공격
- 플레이어 캐릭터와 적 캐릭터/적 총알의 충돌

주로 충돌 관련된 영역이네요.
우선 총알과 적 캐릭터의 충돌을 구현하겠습니다.

기존 요시카 스크램블에서는 점과 점 사이의 거리로 판단하는-소위 원 충돌을 사용했는데, 이번에는 Slick2D에 있는 Shape를 기본으로 한 박스 충돌을 사용해 보겠습니다.

기존 박스충돌과 달리 박스(Rectangle)의 스케일링(확대축소)과 회전을 지원하며, 확축회전된 박스간의 충돌, 원과 원 또는 원과 박스의 충돌도 지원합니다.
※Shape는 박스와 원 외에도 폴리곤 형상의 지정/충돌도 사용 가능합니다.


※잠깐 그 전에, Player클래스에서 총알을 생성할 때 총알에 "BULLET" 이라는 name을,
GameScene 클래스에서 적 캐릭터를 생성할 때 적 캐릭터 오브젝트에 "NEUROI1"이라는 name을 각각 지정하도록 합니다.



우선 GameObject에 Shape rect; 를 추가합니다.


그리고 여기 억세스하기 위한 setRect, getRect 함수를 만듭니다.

rect는 수동으로 지정할 수도 있는데,
Bullet의 경우 이미지가 회전한다던가 하는 변화가 있고,
Neuroi1의 경우 이미지의 일부를 클리핑해서 보여주므로 양쪽 모두 render에서 리얼타임으로 rect를 갱신해 줄 것입니다.
(rect 정보는 현재 위치 좌표를 포함해야 하므로, 반드시 갱신해줘야 합니다. update에서건 render에서건)

Bullet의 render를 보면


버퍼용 Image를 하나 마련해, 회전이나 크기 변경은 이 버퍼에 해 주고,
rect의 초기값을 최초에 읽은 img 기준으로,
그리고 버퍼 이미지에 적용한 것과 같은 스케일과 각도로 변형된 형상(Trans Shape)을 얻어냅니다.


스케일과 회전각이 적용된 Shape를 얻어오기 위해 몇 가지 단계가 필요합니다.


createScaleTransform으로 Shape의 크기를 변경하고,
createRotateTransform으로 Shape를 회전시키는데,

크기가 변경된 시점에서 크기의 변경은 중심점이 아닌 좌상단을 기준으로 하므로 중심점에 맞추기 위해 회전하기 전에 createTranslateTransform 으로 중심점에 맞추는 과정이 들어갑니다.
createRotateTransform의 경우, 각도는 degree가 아닌 radian을 사용하고, 추가로 회전의 중심점을 지정해줄 수 있는데 지정하지 않으면 역시 0,0을 중심으로 회전합니다.

확축회전 Shape를 얻어올 때 마다 위 과정을 타야 하므로, 간단히 하나의 함수로 통합한 것이 BasicScene의 getTransShape라는 함수입니다.


비슷하게 Neuroi1 클래스에서도 render에 rect를 확보합니다.

네우로이 또는 총알의 render에 있는
g.setColor(Color.red);
g.draw(rect);
부분의 주석을 해제해주면 붉은색 사각형으로 실제 반영된 Rectangle을 확인할 수 있습니다. 아직 Player에게는 rect를 부여하지 않았습니다.


이제 충돌 영역을 확보했으니, 총알이 적에 맞았는지 판단하겠습니다.


Bullet 클래스에 checkHit라는 함수를 만듭니다. 이 함수는 update에서 화면 벗어남을 체크하기 전에 부릅니다.



적 캐릭터를 생성할 때 지정한 이름인 "NEUROI1"으로 getChildrenByName을 구하면 현재 화면상의 GameObject 중에서 적 캐릭터만 얻어올 수 있습니다.

위 배열의 루프를 돌려서 자신(총알)의 rect와 적 캐릭터의 rect가 intersects 했는가 보면 됩니다.
intersects는 Shape가 기본 제공하는 함수로 회전 상태의 사각형이나 혹은 폴리곤 등 다른 형태의 Shape의 충돌도 얻어올 수 있습니다.

intersects했다면,
- 적에게 총알의 파괴력(hp)만큼 '너는 얼마만큼의 데미지를 입었다'고 통보해주고
- 적에게 데미지를 입힌만큼을 총알의 hp에서 제외하고 총알의 hp가 남지 않았다면 총알을 소멸합니다.

총알에 hp를 설정하지 않았으니,
Bullet 클래스에 int hp; 정의하고 생성자에서 hp = 1; 해 줍니다.

그리고 Neuroi1 클래스에 얼마만한 데미지를 입었는지 통보를 받을 함수로 setHit(int damage)를 만듭니다.
이걸 intersects했을 때 불러줍니다. temp.setHit(hp) 같은 식이 되겠네요.

setHit에서는 damage만큼 적 캐릭터의 hp를 줄이고, 0 이하가 되면 소멸 요청.
그리고 (damage - 적의 hp) 값을 리턴해줍니다.
Bullet에서는 setHit의 리턴값으로 hp를 갱신하고, hp가 0 이하면 소멸 요청을 합니다.


checkHit에서 주의할 것은,
slick2d 엔진에서는 render보다 update가 먼저 불리는 것 같습니다.
render에서 rect값을 얻기 때문에 checkHit에서는 필히 총알의 recr와 적 캐릭터의 rect가 null인지 여부를 확인해야 합니다.


이제 게임을 진행해 보겠습니다.
(최종 소스는 효과를 확인하기 위해 총알의 발사 간격 등을 조금씩 조정하였습니다)



적의 hp가 2, 총알의 hp(=damage)를 1로 설정했으므로,
총알과 적이 충돌하면 총알이 소멸하고, 적도 두 발 맞으면 소멸합니다.

아직 명중/파괴 시의 파티클 이펙트가 없기 때문에 조금 심심해 보입니다.



핑백

덧글

댓글 입력 영역


Books

Geek라이프

메가 드라이브 퍼펙트 카탈로그
마에다 히로유키 저/조기현 역

미소녀 일러스트 테크닉
B-은하, pen스케, 카와이 저/정유진 역

핵심강좌! Cocos2d-x
이재환 저

피규어의 교과서 레진 키트 & 도색 입문 편
후지타 시게토시 저/김정규 역
예스24 | 애드온2
일본서적 전문사이트 NEPIC