게임메이커 스튜디오 2 시작하기 03 - 충돌 판정

우주선의 공격과 충돌 판정을 코딩합니다.

2022-06-11

gamedevgamemakerstudio2gmscollision

참고 문헌: 게임메이커 스튜디오 공식 동영상 My First Game - Intro to GameMaker - Space Rocks (Part 3) 바로가기

충돌 마스크 설정

오브젝트의 충돌 판정을 내리기 위해서는 충돌을 감지할 수 있는 기준이 있어야 합니다. 스프라이트의 '충돌 마스크'가 충돌을 감지하는 역할을 합니다. 오브젝트의 '충돌 마스크'는 '스프라이트와 동일'로 설정되어 있을 겁니다. 그건 내버려두고, 스프라이트를 선택하세요. 여기서는 spr_rocket입니다.

스프라이트의 '충돌 마스크' 패널을 보면, '모드'와 '종류'가 있습니다. '모드'는 일단 '자동'으로 두세요. '종류'에서 마스크 종류를 바꿀 수 있습니다. 웬만하면 '직사각형'을 설정하는 편이 낫습니다. 컴퓨터의 계산 시간이 빠르기 때문입니다. 그 외에 '타원', '마름모', '정밀하게' 등을 보면 '(느림)'이라는 말이 붙어 있습니다. 계산 시간이 느려 게임 실행에 부담을 줄 수 있습니다.

그런데 '직사각형'으로 설정하면 너무 충돌 가능성이 높아져 플레이어의 불만을 살 수 있습니다. '부딪히지도 않았는데 폭발한다'라고 생각할 수 있겠죠. 그러니 충돌 마스크를 편집해서 충돌 마스크를 작게 만들겠습니다. '모드'를 '설명서'로 바꾸세요. 그러면 충돌 마스크 크기를 직접 바꿀 수 있습니다. 마우스로 사각형 꼭지점을 끌어서 원하는 만큼 마스크 영역을 설정하세요. 플레이어가 공평하게 느낄 만큼 충분히 여유를 줍니다.

'모드' 이름이 '설명서'인 것은 번역 오류로 보입니다. 영어 버전에서는 'Manual'로 되어 있습니다. 자동이 아니라 수동 설정이라는 거죠.

충돌 코드 오브젝트 선택

충돌 코드를 어느 오브젝트에 작성할 것인지도 선택해야 합니다. 나의 우주선에 작성할 것인가, 아니면 소행성에 작성할 것인가? 어느 쪽에 작성해도 충돌 판정을 내릴 수 있습니다. 둘 중 하나에만 작성하면 됩니다.

만약 소행성에 작성한다면, 소행성이 10개, 20개씩 생성되었을 때 해당 오브젝트가 전부 충돌 코드를 실행할 것입니다. 그러면 기기에 부담이 올 수 있겠죠. 따라서 나의 우주선 한 곳에만 충돌 코드를 작성합니다. 그러면 훨씬 효율적입니다.

충돌 코드 작성

우주선 오브젝트에서 '이벤트 추가'를 합니다. '스텝'에서 충돌 코드를 직접 작성해도 되지만, 게임메이커 스튜디오에서는 충돌 이벤트를 별도로 지원합니다. '충돌 > 오브젝트 > obj_asteroid'를 선택하세요. 'obj_asteroid'는 저의 소행성 오브젝트 이름입니다. 즉, 충돌 대상 오브젝트 이름을 선택하는 거죠. 여기서 우주선 자신을 선택하지 않도록 주의하세요.

instance_destroy()

instance_destroy()는 자기 자신을 없애는 메소드입니다. 이제 우주선이 소행성과 충돌하면 순삭될 거예요.

총알 생성

우선 총알 스프라이트와 오브젝트를 생성합니다. 총알은 스프라이트를 직접 그리는 편이 간편할 거예요. 총알의 충돌 마스크와 원점까지 확인하세요.

총알 코드 작성

총알은 우주선의 위치를 반드시 알아야 합니다. 우주선의 위치에서 생성되어야 하기 때문이죠. 그래서 우주선 오브젝트에서 총알 코드를 작성합니다.

우주선 오브젝트의 '스텝'에서 스페이스키를 누르면 총알이 생성되도록 합니다. 방향도 우주선의 image_angle과 동일하게 해 줍니다.

if (keyboard_check_pressed(vk_space)) {
    var bullet_id = instance_create_layer(x, y, "Instances", obj_bullet)
    bullet_id.direction = image_angle
}

우선 키보드 함수입니다. 앞서 썼던 keyboard_check()를 써도 되지만, 그러면 총알이 줄줄이 나가게 됩니다. 즉, 프레임마다 총알이 생성되어 마치 레이저처럼 총알이 나가게 됩니다. 만약 레이저 광선 효과를 원한다면 keyboard_check()를 쓰면 됩니다. 하지만 여기서는 keyboard_check_pressed()를 썼습니다. 그러면 해당 키보드가 눌러진(pressed) 순간에만 총알이 나갑니다. 즉, 키보드가 눌러진 순간의 프레임에서만 총알이 생성됩니다. 그러니 해당 키를 꾹 누르고 있어도 한 발만 나가고 끝입니다. 연발을 쏘고 싶다면 키를 반복해서 두드려야 합니다. 인자로 스페이스키 vk_space를 넣어 줍니다.

instance_create_layer() 메소드는 다른 오브젝트를 생성하는 메소드입니다. 여기서는 총알을 '인스턴스 레이어'로 생성합니다. 스튜디오 화면 왼쪽을 보면 '레이어' 패널과 '인스턴스 레이어 등록 정보' 패널이 보일 겁니다. 지금은 우주선과 소행성이 인스턴스 레이어로 등록되어 있습니다. 마찬가지로 총알도 인스턴스 레이어로 등록하는 것입니다.

instance_create_layer() 메소드의 인자를 보겠습니다. 처음 2개는 x, y입니다. 이것은 우주선 오브젝트의 위치, x좌표와 y좌표를 나타냅니다. (룸 화면의 x좌표는 왼쪽부터 0에서 시작하고, y좌표는 위에서부터 0에서 시작합니다.) 세 번째 인자 Instances는 화면 왼쪽 위 '레이어' 패널에 보이는 'Instances'를 가리킵니다. 생성하고자 하는 총알 레이어의 이름이 'Background'가 아니라 'Instances'라는 뜻입니다. 네 번째 인자는 생성하고자 하는 오브젝트 이름입니다. 여기서는 obj_bullet입니다.

이렇게 instance_create_layer() 메소드로 총알 오브젝트를 생성하면, 해당 오브젝트의 id를 리턴값으로 받게 됩니다. 여기서는 bullet_id라는 변수를 새로 만들어 리턴값을 저장했습니다. 그러면 이 bullet_id 변수를 재사용해 총알 오브젝트에 필요한 속성 또는 메서드를 지정 또는 실행할 수 있습니다.

bullet_id라는 로컬 변수를 생성하기 위해 var 오퍼레이터를 사용했습니다. 로컬 변수는 해당 이벤트에서만 사용된 후 메모리에서 삭제됩니다. 게임 전체의 메모리 절약을 위해 로컬 변수를 사용합니다.

bullet_id는 총알 오브젝트를 가리킵니다. 그래서 direction 속성을 여기에 붙이면 총알 오브젝트의 방향 속성을 설정할 수 있습니다. 총알 오브젝트의 방향이 우주선의 image_angle과 동일한 값이 되도록 할당했습니다.

총알을 생성하고 방향을 설정했지만, 속도가 없습니다. 그래서 총알은 아직 움직이지 않습니다. 총알 속도를 여기서 지정해도 되지만, 웬만하면 총알 오브젝트 자체에서 지정하는 편이 좋습니다. 그래서 총알 오브젝트 obj_bullet에서 '이벤트 추가 > 생성'으로 갑니다. 총알이 생성된 순간부터 속도를 가지게 할 겁니다.

speed = 5

이제 총알이 우주선 앞 방향으로 나아갑니다.

총알에 맞은 소행성 코드 작성

총알 오브젝트에서 충돌 대상인 소행성의 코드를 작성합니다. obj_bullet에서 '이벤트 추가 > 충돌 > 오브젝트 > obj_asteroid'를 선택합니다.

instance_destroy() // 일단 총알 오브젝트 자신이 사라집니다

with (other) {
    instance_destroy() // 소행성 오브젝트도 사라집니다
    
    if (sprite_index == spr_asteroid_256) { // 그런데 가장 큰 소행성은 둘로 쪼개질 겁니다
        repeat(2) { // 더 작은 소행성 생성을 2번 반복합니다 (즉 반쪽짜리 2개 생성)
            var asteroid_128_id = instance_create_layer(x, y, "Instances", obj_asteroid) // 한 단계 더 작은 소행성을 생성하고
            asteroid_128_id.sprite_index = spr_asteroid_128 // 여기서 생성한 소행성은 생김새(크기)를 강제로 지정합니다
        }
    } else if (sprite_index == spr_asteroid_128) { // 중간 크기의 소행성도 가장 작은 크기의 소행성 2개로 쪼개집니다
        repeat(2) {
            var asteroid_64_id = instance_create_layer(x, y, "Instances", obj_asteroid)
            asteroid_64_id.sprite_index = spr_asteroid_64
        }
    } 

with (other) {} 구문은 충돌 대상 오브젝트의 스크립트를 작성하는 구문입니다. 여기서 other는 총알과 총돌한 소행성 오브젝트를 가리킵니다.

with (other) {} 내부의 첫 줄에서 instance_destroy()로 소행성을 사라지게 합니다.

이제 소행성 크기를 판별하여, 가장 큰 소행성은 중간 크기의 소행성 2개로 나뉘고, 중간 크기의 소행성은 가장 작은 소행성 2개로 나뉩니다. 가장 작은 크기의 소행성은 이미 사라졌으며 더 이상의 생성은 없습니다.

sprite_index를 이용해 현재 충돌한 소행성 오브젝트의 생김새(크기)를 판별합니다.

repeat(2) {} 구문은 {} 안의 코드를 2회 반복합니다. 더 작은 소행성을 2개 생성하기 위해, 동일한 생성 코드를 2회 반복합니다.

repeat(2) {} 구문 내부의 2번째 줄은 소행성의 크기를 강제로 지정하는 코드입니다. 우리는 소행성 오브젝트가 생성될 때 크기가 무작위로 결정되도록 했기 때문에, 첫번째 줄에서 어떤 크기의 소행성이 생성될지 알 수 없습니다. 따라서 2번째 줄에서 새로 생성된 소행성 오브젝트의 sprite_index 속성을 이용해 소행성의 생김새(크기)를 덮어씌워 버립니다.

화면 밖으로 날아간 총알 없애기

총알 오브젝트가 빗나가 화면 밖으로 날아가면, 화면 밖에서 계속 이동하며 메모리를 소진합니다. 그러므로 화면 밖에 나간 총알 오브젝트를 없애는 코드를 작성해야 합니다.

obj_bullet에서 '이벤트 추가 > 기타 > 룸 밖으로 나감'을 클릭합니다. 여기에 스스로를 없애는 코드를 작성합니다.

instance_destroy()

이제 총알 오브젝트는 화면 밖으로 나가면 자동으로 소멸될 것입니다.