본 게시글은 'HeartBeast'의 Make an Action RPG 파트의 내용을 다룹니다.
본 강의는 연계 강의이기 때문에 꼭 이전 편들을 보면서 진행하셔야 내용을 이해하시는데 어려움이 없습니다.
더보기

2023. 05. 22

 일부 오탈자 수정

  +) 이미지 포함 수정

세 번째 파트인 히트 박스와 콜리전 레이어입니다.

저번 시간에는 애니메이션 스프라이트를 이용한 애니메이션 이펙트 제작과

시그널을 사용하여 노드를 제거하는 방식에 대해 배워보았습니다.
풀이 한 번에 제거되기 때문에 이를 해결하기 위해

이번 시간에는 히트 박스(Hitbox)를 추가해 공격을 받았을 때 풀이 제거되는 효과를 구현해보도록 하겠습니다.

 

우선 씬(Scene)을 두 개 추가해서, 히트 박스(Hitbox), 허트 박스(Hurtbox)로 만들어 줄 것입니다.

을 만드는 +버튼을 눌러서  하나를 생성하신 뒤 Other Node 버튼을 눌러주세요.

 

'area'를 검색하고 Area2D를 선택합니다.

 

또, 을 하나 더 추가해 방금처럼 Area2D를 추가합니다.

 

처음 생성한 으로 돌아가서, 이름을 'Hurtbox'으로 바꿔줍니다.

 

저장(Ctrl+S)을 누릅니다. 저장 창에서 아무 데나 우클릭을 눌러 폴더를 하나 만들어줍니다.

 

이름은 'Overlap'로 하겠습니다.

 

방금 생성한 Overlap폴더 안에다가 Save 버튼을 눌러 저장합니다.

 

나머지 하나는 이름을 'Hitbox'로 바꿉니다

 

동일하게 저장(Ctrl+S)을 눌러 저장합니다.

 

+버튼을 노드를 생성합니다.

 

'coll'를 검색하고 CollisionShape2D를 선택합니다. 그리고 Create 버튼을 눌러 생성해주세요.

 

마찬 가지로 Hurtbox 으로 넘어가서 동일하게 생성합니다.

 

이제 만들어 놓은 들을 활용해볼까요?

Grass노드에서 을 열거나 아니면, 파일 시스템(FileSystem)에서 Grass.tscn를 열어주세요.

 

Grass으로 넘어갑니다. 뷰에서 +말고 그 옆에 있는 링크 아이콘🔗을 눌러줍니다.

이 기능은 고도 엔진에서 지원하는 기능이므로, 유니티프리팹(prefab)과 조금 비슷하지만 같지는 않습니다.

둘 다 템플릿(template) 역할을 하게 되는데 고도 엔진에는 템플릿 이 되는

계속 추가로 인스턴스 시켜서 마치 프리팹처럼 활용할 수 있습니다.

 

Overlap/Hurtbox.tscn를 선택하고 Open 버튼으로 엽니다.

 

아까 생성한 이 추가되는데요, 여기서 Hurtbox노드우클릭해서 Editable Children체크니다.

 

그러면 Hurtbox 노드의 자식 노드CollisionShape2D가 나오는데요. 선택하고

우측 인스펙터(Inspector) 뷰에서 Shape 항목에서 [empty]˅ 버튼을 눌러 New RectangleShape2D를 선택합니다.

 

스냅(Snap)을 잠깐 꺼주고 더 작게 픽셀 단위로 크기를 설정해봅시다.

 

이 정도로 맞춰주시면 됩니다. 다 하셨으면 위에 스냅을 다시 켜줍시다.

 

Hurtbox 노드를 다시 선택하고 우측 탭 Node로 전환해줍시다. 저번 시간에 배운 시그널을 추가할 건데요.

area_entered를 더블 클릭하여 시그널을 생성하도록 하겠습니다.

 

Connect 버튼을 누릅니다.

 

아래 func _on_Hurtbox_area_entered(area): 부분에 pass를 지우고 queue_free()를 넣습니다.

 

그다음에 func _process 함수 내에서 조건 문이랑 queue_free()만 지웁니다.

 

Shift+Tab을 눌러 해당 드래그 영역을 들여 쓰기를 정렬해줍시다.

 

1
2
3
4
5
6
7
8
9
10
11
12
extends Node2D
 
const GrassEffect = preload("res://Effects/GrassEffect.tscn")
 
func create_grass_effect():
    var grassEffect = GrassEffect.instance()
    var world = get_tree().current_scene
    world.add_child(grassEffect)
    grassEffect.global_position = global_position
 
func _on_Hurtbox_area_entered(area):
    queue_free()

func _process 함수에 있던 줄을 func create_grass_effect():로 재작성합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
extends Node2D
 
const GrassEffect = preload("res://Effects/GrassEffect.tscn")
 
func create_grass_effect():
    var grassEffect = GrassEffect.instance()
    var world = get_tree().current_scene
    world.add_child(grassEffect)
    grassEffect.global_position = global_position
 
func _on_Hurtbox_area_entered(area):
    create_grass_effect()
    queue_free()

방금 새로 작성한 함수를 아래 func _on_Hurtbox_area_entered(area):에서 호출합니다.

 

World 으로 넘어가서 Player을 열어줍니다.

 

다시 링크 아이콘🔗을 눌러서 이번에는 Overlap/Hitbox.tscn를 선택합니다.

 

생성된 Hitbox 노드우클릭해서 Editable Children체크합니다.

 

자식 노드로 보이는 CollisionShape2D 노드가 선택된 상태에서 우측 인스펙터 뷰의 Shape 항목에 있는

New CapsuleShape2D를 눌러 CapsuleShape2D를 생성합니다.

 

생성된 콜리전 쉐이프(Collision Shape)스냅을 끈 상태에서 좀 더 작게 설정합니다.

지금 당장 디테일하게 맞출 필요는 없습니다. 어차피 수치로 다시 재조정할 것입니다.

 

한 번 플레이해봅시다. 움직여서 풀 근처로 가면 이펙트가 발생하여야 합니다.

 

종료하고 노드 하나를 추가해줍시다.

 

'position'을 검색하고 나오는 Position2D를 선택하고 생성합니다.

 

이름을 'HitboxPivot'으로 바꿔줍시다.

 

Position2D기즈모(Gizmo)의 한 종류입니다. 기즈모2D 작업공간 (2D workspace)에서

편집이나 디버깅을 위해 오브젝트의 위치나 범위, 특정 요소를 시각화하기 위한 노드입니다.

여기서 해당 노드피벗(Pivot)으로 활용할 수 있는데, 피벗은 쉽게 말하면 기준점입니다.

아무튼 모션에 따라 콜리전 쉐이프 위치를 바꿔야 하는데, 이를 쉽게 하기 위해서

해당 노드피벗을 정하고 회전시켜 콜리전 쉐이프 위치를 바꾸는 방법을 사용할 것입니다.

 

우측 인스펙터에서 Gizmo Extents 항목을 '8'로 바꿔줍니다.

 

Transform를 펼쳐서 Position 항목의 y값을 '-4'로 수정합니다.

 

구분을 위해 뷰에서 Hitbox의 이름을 'SwordHitbox'로 변경합니다

 

SwordHitboxHitboxPivot의 자식으로 만들어줍니다.

 

이름을 바꾸거나 부모&자식 관계를 바꾸면 Editable Children이 해제가 됩니다. 다시 체크하도록 합니다.

 

편집을 위해 모션을 바꿔봅시다. 우선 오른쪽 공격 모션부터 하겠습니다.

AnimationPlayer를 선택하고 드롭다운 메뉴를 펼쳐서 AttackRight를 선택합니다.

 

하단 돋보기로 애니메이션 에디터를 확대 한 뒤 커서를 0.2초에 맞춰줍니다. 이 구간을 공격 포인트로 삼을 것인데요.

우선은 콜리전 쉐이프 위치를 옮길 것입니다.

 

여기서 자식 노드CollisionShape2D 노드를 건드는 게 아니라 한 단계 위에 있는 SwordHitbox의 위치를

바꿀 것입니다. 인스펙터 뷰에서 Transform을 펼치고 Position 항목에 있는 x, y값을 '15, 0'으로 맞춰줍니다.

 

다시 CollisionShape2D 노드로 돌아갑시다. 인스펙터 뷰에서 Shape 항목에 있는 CapsuleShape2D 부분을 클릭합니다.

 

Radius, Height 항목을 각각 '10, 12'로 수정합니다.

 

이제 각 공격 모션마다 회전을 해야 하기 때문에 키를 넣어줄 것입니다.

HitboxPivot 노드를 선택해서 커서를 0초로 옮긴 뒤 Rotation Degrees 항목의 값을 '0'으로 맞추고

옆에 있는 키 모양 아이콘🔑을 클릭합니다.

 

애니메이션을 AttackDown로 바꿔주고 콜리전 쉐이프를 아래로 향하기 위해서 90도 회전, 따라서

Rotation Degrees 항목을 '90', 그다음에 🔑를 생성합니다.

 

애니메이션을 AttackLeft으로 바꿔주고 콜리전 쉐이프를 왼쪽에 두기 위해 180도 회전시킵니다.

따라서 Rotation Degrees 항목을 '180', 그다음에 🔑를 생성합니다.

 

애니메이션을 AttackUp으로 바꿔주고 콜리전 쉐이프를 위로 향하도록 270도 회전시킵니다.

값은 당연히 '270' 주시고 🔑는 생성합니다.

 

이제는 공격 방향대로 풀이 잘리는 모습이 보이지만, 여전히 히트 박스가 활성화되어 있어서 지나가기만 해도 잘립니다.

이를 수정하기 위해 다시 애니메이션 에디터를 보도록 하겠습니다.

 

애니메이션 에디터가 열려있는 상태에서 CollisionShape2D 노드를 선택합니다. 커서를 0.1초에 두신 다음에

우측 인스펙터 뷰에서 Disabled 항목에 있는 🔑를 생성해줍니다.

 

커서를 0.3으로 옮깁니다. 그리고 Disabled 항목에서 체크 해주시고 🔑를 생성합니다.

 

이렇게 하면 애니메이션이 끝날 즈음에 콜리전 쉐이프비활성화가 되어 더 이상 풀 근처 가도 자동으로 베어지지 않으며

공격 모션을 할 때만 충돌 판정이 나므로 그때만 풀이 베어지게 될 것입니다.

 

나머지 세 모션(AttackRight, AttackLeft, AttackDown)에도 동일하게 작업해줍니다.

 

디버깅을 위해 콜리전 쉐이프를 보는 옵션을 켜겠습니다.

좌측 상단 메뉴에 있는 Debug - Visible Collision Shapes체크합니다.

 

플레이를 해서 확인해봅시다.

 

다시 옵션을 체크 해제 해줍니다.

 

이 이미지는 따라 하지 마시고 눈으로만 봐주세요.

그런데 한 가지 문제점이 있습니다. 바로 아무 콜리전(Collision)이나 충돌하게 되면, 풀이 베어지는 이펙트가 재생되는데요

예를 들어 위의 이미지처럼 풀이 이런 식으로 겹치게 되면 풀이 베어지는 이펙트와 함께 사라집니다.

이는 내부에서 따로 확인하는 부분이 없기 때문에 그렇습니다. 주인공이 벨 때 켜지는 히트 박스

지금 풀이 각자 갖고 있는 허트 박스끼리도 동일한 콜리전으로 보고 있다는 점이죠.

 

더보기

본문 글에서는 그냥 충돌체(Collider)도 콜리전 범주안에 포함시켜 설명하고 있습니다.

밀히 따지면, 충돌체끼리만 충돌(Collision)이 발생하는 것이며, 그 외에는 충돌이 발생하지 않습니다.

고도 엔진에서는 충돌체 내용이 언급되는 부분은 키네마틱 바디(Kinematic Body) 같은 곳에서 자주 언급됩니다.

따라서 콜리전 쉐이프 충돌체 역할도 수행하는 것으로 보입니다.

비슷한 예시로 유니티 엔진에서는 콜라이더 컴포넌트로 구분되어 있습니다.

 

따라서 이를 구분해줘야 합니다. 하지만 코드로 작업해야 할까요? 더 간단한 방법으로

콜리전 레이어&마스크를 활용해 구분 지을 수 있습니다. 이는 고도 엔진이 아닌 다른 엔진에도 있는 기능이죠.

 

파일 시스템에서 두 을 각각 열어주도록 합니다.

 

우선 Hurtbox 부터 보도록 하겠습니다.

인스펙터 뷰로 넘어와서, 아래 Collision을 펼치면 레이어(Layer)Mask(마스크) 항목이 보입니다.

 

레이어마스크 아래에 사각형 박스가 있는데 마우스를 올리면, 레이어의 이름이 표시됩니다.

이걸 활용할 건데요. 그전에 이 레이어들을 식별하는데

편하기 위해서 프로젝트 설정(Project Settings)에서 바꾸도록 하겠습니다.

 

좌측 상단 메뉴에서 Project - Project Settings…를 선택합니다.

 

스크롤을 내려 2d Physics 항목을 선택합니다.

 

자 이제 사용할 각 레이어에 이름을 지어주면 됩니다. 우선 Layer 1 항목에 'World' 레이어라고 짓도록 하겠습니다.

 

Layer 2 항목은 'Player', Layer 3 항목에는 'PlayerHurtbox',

Layer 4 항목에 'EnemyHurtbox'라고 정하겠습니다. 설정을 다하셨으면 닫아주시고

 

Hurtbox 에서 레이어마스크 항목에서 켜져 있던 사각형 박스들을 꺼주도록 합시다.

 

Hitbox 도 마찬가지로 사각형 박스를 꺼주도록 합니다.

이 두 은 이전에 말씀드렸듯이, 템플릿 이기 때문에 미리 꺼두도록 합니다.

인스턴스 한 에 대해서만 키도록 할 것입니다.

 

기존에 열려있던 Player 으로 돌아갑시다. 최상위 부모인 Player 노드가 선택된 상태에서

우측 인스펙터 뷰에 Collision를 펼치고 레이어 항목에서 첫 번째 레이어를 끈 뒤 다음과 같이 두 번째 레이어를 켜봅시다.

 

그리고 World 을 열어서, DirtCliffTileMap 노드를 선택한 뒤 Collision를 펼치고

마스크의 첫 번째 레이어를 끄도록 하겠습니다. 참고로 아까처럼 사각형 박스를 클릭해도 되지만,

이렇게 부분을 클릭해서 체크하는 방식으로도 가능합니다.

 

이제 마스크의 역할이 궁금하실 텐데요,

마스크레이어가 체크된 것만 감지됩니다. 무슨 이야기냐 하면

만약에 이런 식으로 Player 노드마스크 항목에 첫 번째 레이어를 끄면

 

이런 식으로 지형을 뚫고 지나가게 됩니다. 그렇다는 이야기는 

충돌을 하기 위해서는 마스크가 필요하단 이야기죠. 그전에 충돌이 자연스럽게 됐던 것은 기본적으로

첫 번째 레이어가 체크되어있기 때문입니다. 하지만 이제 식별할 수 있는 이름을 레이어마다 주고

설정을 하였으므로 구분이 필요한 시점입니다.

 

Grass 으로 넘어가도록 합시다. 제 화면에서는 잠깐 꺼뒀기 때문에 없어서 다시 열었습니다.

맨 처음부터 을 안 끄고 고대로 진행하셨다면 탭에 남아 있을 겁니다.

 

뷰에서 Hurtbox 노드를 선택해주시고 우측 인스펙터 뷰에서 Collision을 펼친 다음

Layer 항목에서 를 눌러서 목록 중 EnemyHurtbox체크합니다.

 

그다음에 Player 으로 온 다음 SwordHitbox 노드를 선택하고 인스펙터 뷰에서 Collision을 펼친 다음

레이어가 아닌 Mask 항목에서 를 누른 뒤 EnemyHurtbox체크합니다.

 

World 으로 넘어가서, 풀 노드 하나 위치를 바꿔봅시다. 이런 식으로 풀끼리 겹치게 하시면 됩니다.

 

한 번 플레이를 해볼까요? 보니까 이렇게 되면 겹친 부분도 충돌이 작동하지 않고,

주인공이 공격했을 때만 풀이 베어지게 됩니다.

 

풀은 원래 위치로 돌려놓고 이번 시간은 마무리하겠습니다.

 

더보기

HeartBeast영상 중 [0:25 ~ 02:36]에서 Grass  GrassEffect를 설정하지 않는지에 대해서 이야기하였습니다.

결론부터 말씀드리자면, 충돌체에 대해 비활성화를 해야 하고 그렇지 않으면
계속 애니메이션이 재생되기 때문에 이를 해결하려면 추가로 코드를 작성해야 합니다.
그러므로 작업 방식에 차이로 그렇게 하지 않았다고 언급합니다. 자세한 내용은 해당 영상을 참고하세요.

 

다루지 않은 몇몇 레이어는 차후에 다뤄보도록 하고,

다음 파트에서는 이전에 다루지 못한 스테이트 머신(State Machine)을 구현해놓은 상태 중에서 

구르기(Roll)에 대한 애니메이션 작업에 대해서 진행하도록 하겠습니다.

그럼 다음 파트에서 뵙겠습니다.

728x90
728x90