타지않는 놀이터

[고도4] Part.1 - 5 물리엔진 활용하기 본문

강좌/Godot 4 - Part.1

[고도4] Part.1 - 5 물리엔진 활용하기

김메가 2023. 12. 21. 22:33

지난 시간 구현한 플레이어 캐릭터는 발판과 벽을 무시하고 통과한다. 이번 시간에는 물리 관련 노드를 활용하여 캐릭터와 발판 사이의 충돌을 구현해보자.

 

stage_1씬에 CharacterBody2D라는 노드를 생성해주자. Sprite2D를 생성할 때와 방법은 같다. CharacterBody2D를 생성했으면 씬 독에서 Player노드를 CharacterBody2D노드에 드래그 하자.

 

그러면 Player노드가 CharacterBody2D의 하위로 들어가게 된다. 이렇게 다른 노드의 하위로 들어간 노드를 [자식 노드](Child Node)라고 칭한다. 반대로 [자식 노드]를 가진 상위 노드는 [부모 노드](Parent Node)라고 부른다. Player노드는 CharacterBody2D노드의 [자식 노드]이며 반대로 CharacterBody2D노드는 Player노드에 대하여 [부모 노드]라는 관계인 것이다.

 

자식 노드인 Player노드의 이름을 ‘Sprite’, 부모 노드인 CharacterBody2D노드의 이름을 ‘Player’로 변경해주자. 이제 Sprite노드(원래 이름이 Player였던 노드)는 부모 노드에 붙어서 그래픽을 표시하는 역할만 할 것이고 실제 이동하는 역할은 그 부모 노드가 할 것이기 때문에 Player라는 이름은 부모인 CharacterBody2D노드가 가지는 것이 더 적절하기 때문이다. 지금부터는 바뀐 이름대로 부모 쪽을 Player노드, 자식 쪽을 Sprite노드라고 부를 것이니 혼동하지 않도록 유의하자.

 

Sprite노드를 선택하고 인스펙터를 살펴보면 여러 속성 중에서 Transform이라는 그룹이 보일 것이다. 클릭해서 펼쳐보자.

 

Transform이란 간단히 설명하자면 2D노드(Sprite2D타입이 아니더라도 모든 2D관련 노드는 이 Transform을 가지고 있다.)의 공간적 정보(위치, 각도, 크기)이다. 각 속성의 의미는 다음과 같다.

-      position : 위치

-      rotation : 각도

-      scale : 크기

-      skew : 기울임 (이건 거의 쓸 일 없다.)

우리가 2D스크린에서 Sprite2D노드를 이동하고, 회전시키고, 늘렸다 줄였다 하는 모든 조작은 사실 이 속성들을 조절하는 것이다. 그런데, Transform으로 묶이는 속성의 값들은 모두 [부모 노드]를 기준으로 삼는다. 예를 들어 position[부모 노드로부터의 상대적인 위치]를 의미하는 것이다.

우리는 Sprite노드가 부모 노드인 Player노드의 공간적 정보와 일치하도록 그래픽을 표시하게 해야 한다. 따라서 Transform에 해당하는 속성들을 스크린샷과 같이 전부 기본 값으로 설정해주도록 하자.

 

다음은 Sprite노드에 붙어있는 스크립트를 제거하고 같은 스크립트를 Player노드에 부착하자.

 

이제 코드를 수정할 차례다. 지난번과 마찬가지로 player.gd스크립트를 열자.

 

우선 가장 첫번째 라인을 수정하자. 이 스크립트는 이제 Sprite2D가 아닌 CharacterBody2D타입의 노드(Player노드)에 부착되므로 해당 타입을 상속받아야 한다. 따라서 ‘extends Sprite2D’‘extends CharacterBody2D’로 수정하자.

 

그리고 그 밑에 ‘var speed:float = 500.0’이라는 코드를 입력해주자. 이 스크립트가 부착된 노드에 speed라는 이름을 가진 [변수]를 만들고 노드가 생성되었을 때 해당 [변수] 초기값이 500.0으로 설정된다는 의미이다. [변수](variable, 줄여서 var)란 대충 값을 담아두는 공간이라고 생각하면 된다.

 

다음으로 _process메서드의 내용을 수정해주자. 지난 강의에서 말했듯이 주석은 생략해도 되며 코드의 의미가 궁금하면 읽어보자.

 

그리고 새로운 메서드, _physics_process와 그 내용을 작성해주자.

 

_physics_process_process메서드처럼 반복적으로 작동하지만 _process와는 달리 매 프레임마다가 아니라 일정 주기마다’(기본 설정은 1/60) 작동한다(엄밀한 설명은 아니지만 현재로서는 이렇게 이해해두자.). 예를 들어 게임의 fps60일때는 _process_physics_process 둘 다 초당 60번씩 작동하겠지만 fps10으로 떨어지면 _process는 초당 10번씩만 작동할 것이다. 하지만 _physics_process메서드는 여전히 초당 60번씩 작동한다.

 

물리 연산은 실행 주기가 안정성에 큰 영향을 끼치기 때문에, 물리 연산을 수행하는 메서드는 주기가 일정한 _physics_process메서드에서 실행시키는 것이다.

 

게임을 실행시키고 좌우 방향키를 이용하여 잘 작동하는지 확인하자. 이번에 코딩한 것은 별건 아니고 기존의 이동 코드를 CharacterBody2D의 기능(충돌을 감지하는 이동)을 활용해서 이동하도록 개선한 것뿐이다. (캐릭터가 이전보다 더 빠르게 움직이는 것은 그냥 속력에 해당하는 값을 100.0에서 500.0으로 높여서 그런 것일 뿐이다.)

 

이제 CharacterBody2D가 충돌을 감지할 수 있게 충돌 영역을 설정해주자. CollisionShape2D노드를 생성해서 Player노드의 자식으로 넣어주자. CollisionShape2D라는 노드는 부모 노드가 물리 관련 노드일 때, 부모 노드의 충돌 영역의 모양을 설정할 수 있게 해준다.

 

씬 독에서 CollisionShape2D노드를 선택하고 인스펙터를 보면 shape라는 속성이 있다. 해당 속성의 <empty>라고 표시되는 빈칸을 클릭하면 원, 사각형, 캡슐 등의 모양 데이터 중에서 어떤 것을 사용할 지 선택할 수 있는데, 그 중에서 캡슐 모양(New CapsuleShape2D)를 선택하자.

 

선택한 모양이 2D 스크린에 나타난다. 충돌 영역의 모양을 적당히 설정해주되, Sprite노드와 마찬가지로 Transform 속성들은 기본값으로 설정해주자.

 

이번에는 발판, 벽에도 물리 노드를 적용해보자.

 

StaticBody2D라는 노드를 생성해서 발판 그래픽을 표시하는 노드에 자식으로 넣는다. 그리고 CollisionShape2D노드를 생성해서 StaticBody2D노드의 자식으로 넣자. (StaticBody2DCharacterBody2D와 마찬가지로 물리 관련 노드이지만 CharacterBody2D와는 달리 이동관련 기능이 없어 움직이지 않는 발판, 지형 등을 구현하는데 적합하다.)

 

생성한 StaticBody2DCollisionShape2D노드 둘 다 Transform을 기본값으로 설정해주자.

 

그리고 CollisionShape2D의 인스펙터에서 이번에는 사각형(RectangleShape2D)를 선택하자.

 

이제 발판 또는 벽 그래픽에 알맞게 충돌 영역의 모양을 설정해주자.

 

이 과정을 모든 발판과 벽에 대해서 반복해주면 된다.

 

발판과 벽의 충돌영역 설정이 끝났다면 에디터 좌상단의 Project메뉴를 통해 프로젝트 설정(Project Settings…)에 진입하자.

 

Layer Names – 2D Physics 설정에서 Layer 1“Ground”, Layer2“Player”, Layer3“Enemy”로 설정해주자.

 

프로젝트 설정 창을 닫고 Player노드의 인스펙터에서 Collision이라는 그룹을 펼쳐보자. 그 안에는 layermask라는 속성이 있을 것이다.

-      Layer : 이 물리 노드가 어떤 레이어에 해당하는지를 나타낸다.

-      Mask : 이 물리 노드가 어떤 레이어를 감지해야 하는지를 나타낸다.

방금 전 프로젝트 설정에서 우리는 1번 레이어에 Ground(발판, 벽 같은 맵 지형 요소를 의미), 2번 레이어에 Player라는 이름을 각각 지어줬다. 이를 따라서 layer2번 레이어로, mask1번 레이어로 설정해주자.

 

Player노드 뿐만 아니라 발판과 벽에 해당하는 StaticBody2D노드들도 layermask를 설정해줘야 한다. 하지만 layer의 기본값이 1번 즉 Ground레이어이므로 이미 알맞게 설정되어 있을 것이다.

 

게임을 실행시켜보자. 더 이상 플레이어 캐릭터가 발판과 벽을 통과하지 못하고 충돌하면 성공이다.