[Godot Engine] 03 - 스프라이트 이해와 활용
본 게시글은 'HeartBeast'의 Make an Action RPG 파트의 내용을 다룹니다.
본 강의는 연계 강의이기 때문에 꼭 이전 편들을 보면서 진행하셔야 내용을 이해하시는데 어려움이 없습니다.
세 번째 파트인 스프라이트 이해와 활용입니다.
이전 시간에 리소스 임포트와 프로젝트 환경설정을 건드렸으니
이번에는 본격적으로 노드(Node)를 생성하고 주인공을 배치 및 제어하는 시간을 갖도록 하겠습니다.
우선 노드를 하나 생성해봅시다. 씬(Scene) 뷰에 배치되는 오브젝트는 노드라는 이름을 가지고 있습니다.
우측 상단에 있는 '+'모양을 클릭해 생성해봅시다.
수많은 노드 타입들이 나옵니다. 우선은 주인공 캐릭터를 만들 것이기 때문에,
'Search' 아래 입력란에다가 bod 정도만 입력해봅시다.
그러면 뒤에 bod에 관련된 타입들이 필터링되는데요. 쭉 훑어보니 body로 끝나는 것들이 있네요.
여기서 우리는 'KinematicBody2D'를 사용할 것입니다. 클릭하시고 아래 'Create'를 눌러주세요.
여기서 PhysicsBody2D형태의 종류에 대해 잠시 간략하게 설명해 드리자면,
StaticBody2D: 물리적인 작용은 받으나 움직이지 않는 고정 물체에 쓰임
RigidBody2D: 물리적인 작용도 받고, 움직임을 제어할 수 있는 물체에 쓰임
KinematicBody2D: 충돌체(Collisions)를 가지고 있고 코드로 움직임을 제어하는 물체에 쓰임
키네마틱 바디는 주로 플레이어블 캐릭터에 자주 쓰입니다. 자세한 설명은 링크를 참고해주세요.
생성한 키네마틱 바디의 이름을 'Player'로 변경해주세요. 알아볼 수 있다면 다른 이름이라도 괜찮습니다.
오른쪽 탭을 보니까 충돌체와 노드의 위치를 조절하는 트랜스폼(Transform)이 보이네요,
하지만 주인공의 외형을 넣을 수 있는 건 딱히 보이지 않습니다.
그렇다는 이야기는 이 노드의 하위 노드로 스프라이트를 넣어서 외형을 만들어줘야겠지요?
생성 버튼을 누르고 검색창에 'sprite'를 입력하면 Node2D종류의 스프라이트가 보입니다. 클릭하고 생성합시다.
저번 시간에 텍스처를 작업 공간(Work Space)에 드래그&드롭했던 행위도 스프라이트를 생성하는 행위 중 하나입니다만,
가급적이면 생성 버튼을 통해 스프라이트도 생성하시는 것을 권장합니다.
텍스처(Texture) 항목이 보입니다.
텍스처 항목에다가 파일 시스템에 있는 Player.png를 드래그&드롭으로 갖다 놓읍시다.
갖다 놓자마자 긴 이미지가 하나 보이네요.
애니메이팅을 해보신 분이라면 딱 보자마자, "아, 이미지를 잘라서 써야겠구나"라고 직감이 오실 겁니다.
직감이 안 와도 상관없습니다. 그게 중요한 게 아니니까요. 아무튼 이 이미지들은 프레임 단위로 자를 수 있도록
되어 있는 이미지들입니다. 그리고 그 기능은 고도 엔진 에디터에서 제공되고 있지요.
Animation
을 펼치고 'Hframes'값을 조절해 볼까요?
값을 '60'정도 넣으면 적당할 것 같습니다.
'Frame'의 값을 조금씩 올려보면 각 프레임을 살펴볼 수 있습니다.
원래대로 돌려놓는 건 잊지 맙시다.
다음으로는 피벗(Pivot)에 대해서 설명하겠습니다.
이 부분은 따라 하셔도 되지만 굳이 따라 하실 필요는 없고 사진과 내용만 봐주세요.
여우 캐릭터의 가운데 위치하고 있는 십자 표시는 피벗을 가리킵니다. 이 경우 스프라이트의 피벗라고 말할 수 있죠.
피벗은 쉽게 말해 기준점이라고 생각하시면 됩니다. 피벗은 대부분 중앙에 위치합니다.
피벗의 위치를 바꿀 수 있지만 특별한 경우가 아니라면 변경하지 않습니다.
지금 보시는 바와 같이 선택하고 드래그&드롭을 하게 되면 스프라이트가 선택돼서 옮겨졌음을 알 수 있습니다.
화살표로 표시된 곳 또한 Player
의 피벗입니다.
현재 Player
가 선택되어 있죠? 아까 전에 우리는 스프라이트 위치를 옮겼습니다. 하지만 이렇게 되면
코드로 부모인 플레이어의 위치를 움직였을 때 스프라이트가 저렇게 떨어진 위치로 움직인다는 것을 의미합니다.
작업 공간에서 우리는 쉽게 노드를 이리저리 옮기고 싶습니다. 그런데 스프라이트가 선택되어서 초기 위치를 이상하게
설정하면 보기 안 좋겠죠? 이런 것을 방지하기 위해 자식 노드는 선택 안되게 하는 기능이 있습니다.
우선 스프라이트 위치를 원래대로 돌려놓고
씬 뷰에서 플레이어를 선택한 뒤 툴바 중에서 저렇게 생긴 아이콘을 클릭해주세요.
이렇게 되면 하위 노드들은 더 이상 선택이 되지 않습니다.
Player옆에 아까 클릭한 아이콘이 생겼다면 적용된 것입니다.
이제 한번 작업 공간에서 스프라이트를 옮겨봅시다.
Player의 위치
그리고 스프라이트의 위치가 동일해졌습니다.
여기까지 했으면 주인공은 만들어졌습니다. 하지만 주인공을 조종할 수 있는 수단이 없죠?
이제부터 스크립트를 생성해 주인공을 조작해볼 것입니다.
씬 뷰 근처를 보시면, 문서 같은 아이콘이 있습니다. 이것을 클릭하고
나오는 창에서 폴더 모양 아이콘 클릭
새로 열리는 창에서 Player 폴더 더블 클릭해서 들어간 다음 'Open'
이후 'Create'를 눌러 스크립트를 생성합니다.
추가로 Langauge에 대해 간략한 설명드리자면,
NativeScript: C, C++ 문법 및 기반 스크립트 작성 가능
GDScript: Python 문법 및 기반 스크립트 작성 가능 (초심자 추천)
VisualScript: C# 문법 및 기반 스크립트 작성 가능 (유니티 해보셨다면 이쪽이 편하실 수도 있습니다)
개인적으로 생각하기에는
프로그래밍 문법에 지식이 아예 없거나, 아직 배우는 중이라면 GDScript를 추천합니다.
파이썬은 다른 프로그래밍 문법에 대해 상대적으로 자유롭고 가볍고도 강력한 언어 중 하나입니다.
그것을 기반한 GDScript는 문법에 크게 구애받지 않고 쓸 수 있습니다.
이 글에서는 GDScript로 쭉 진행합니다. 각 함수에 대한
구현은 Docs 쪽 확인하시면 각 문법 스크립트 별로 존재하니 참고 바랍니다.
스크립트를 생성하고 나면 스크립트 편집기가 열리게 됩니다.
그리고 생성된 스크립트 언어에 따라 간단한 주석이 달리게 됩니다.
다시 스크립트 편집기로 넘어가서 주석을 지워줍시다.
스크립트를 생성하자마자 생기는 extends 경우 현재 노드 타입에따라 달라집니다.
함수 '_ready():'는 고도 엔진 내부함수로 위에서 변수를 선언하고 선언한 변수의
초기 설정을 주로 관리한다고 보시면 됩니다.
유니티로 치면 Initialization 부분의 Start() 함수 정도 되겠네요.
우선 가장 대중적인 것을 해봅시다. 바로 헬로 월드이죠.
다음과 같이 작성이 끝났다면 한번 실행해봅시다.
실행하고 나면 Output 부분에 Hello World. 가 출력된 것을 확인할 수 있습니다.
이번에는 _ready 함수가 아닌 다른 함수를 써봅시다.
기존에 있는 ready() 함수를 잠시 지우도록 합니다.
새로 작성한 _physics_process(delta)는 update함수의 일종으로 주로 물리적인 운동을 처리할 때 사용합니다.
그 이외에 다른 update 함수로 _process(delta)도 존재합니다. 이 경우는 물리적인 것을 처리하지 않는
이외에 상황에 쓰인다고 볼 수 있습니다.
다음과 같이 두고 실행해봅시다.
Output 부분에 hello world. 가 연달이 찍히는 것을 확인할 수 있습니다.
이번에는 아까 프린트 함수를 지우도록 하고 새로이 조건문을 걸어봅시다.
예를 들어 오른쪽 키를 눌렀을 때 오른쪽 키를 눌렀다는 메시지를 출력하는 걸로 변경하도록 하죠.
1
2
3
4
5
|
extends KinematicBody2D
func _physics_process(delta):
if Input.is_action_pressed("ui_right"):
print("오른쪽 키를 눌렀습니다.")
|
코드는 다음과 같습니다.
오른쪽 방향키를 눌렀다는 조건문이 정상적으로 작동하기에 한번 위치 값도 누를 때마다 변경되도록 고쳐볼까요?
1
2
3
4
5
|
extends KinematicBody2D
func _physics_process(delta):
if Input.is_action_pressed("ui_right"):
position.x += 10
|
다음과 같이 변경 해주세요.
잘 이동하긴 하나 단지 x값을 증가시키는 거라 딱히 의미가 있진 않습니다. 코드를 좀 더 수정해보죠.
1
2
3
4
5
6
7
8
9
10
11
|
extends KinematicBody2D
var velocity = Vector2.ZERO
func _physics_process(delta):
if Input.is_action_pressed("ui_right"):
velocity.x = 10
if Input.is_action_pressed("ui_left"):
velocity.x = -10
move_and_collide(velocity)
|
다음과 같이 수정합니다.
벡터2D 좌표를 선언해 초기화 해준 뒤, 버튼을 눌렀을 때 값을 대입해주고
마지막으로 고도 엔진에서 제공하는 이동 처리 함수로 해당 벡터 값만큼 이동합니다.
앞 뒤로 이동하기 위해 왼쪽 방향키를 눌렀을 때 조건문도 추가하였습니다.
앞 뒤로 잘 움직이긴 합니다만, 멈출 수가 없네요. 멈출 수 있도록 코드를 다시 손봅시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
extends KinematicBody2D
var velocity = Vector2.ZERO
func _physics_process(delta):
if Input.is_action_pressed("ui_right"):
velocity.x = 4
elif Input.is_action_pressed("ui_left"):
velocity.x = -4
else:
velocity.x = 0
move_and_collide(velocity)
|
이동 값이 너무 큰 것 같아서 '4'로 바꿔주었습니다.
또한 if -> elif로 바뀐 부분이 있고 else를 추가했습니다.
이 정도면 아까보단 꽤 나아진 것 같습니다. 왼쪽, 오른쪽 말고 위, 아래도 해봅시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
extends KinematicBody2D
var velocity = Vector2.ZERO
func _physics_process(delta):
if Input.is_action_pressed("ui_right"):
velocity.x = 4
elif Input.is_action_pressed("ui_left"):
velocity.x = -4
elif Input.is_action_pressed("ui_up"):
velocity.y = -4
elif Input.is_action_pressed("ui_down"):
velocity.y = 4
else:
velocity.x = 0
velocity.y = 0
move_and_collide(velocity)
|
다음과 같이 바꾸고 실행해봅시다.
어떤가요? 위아래로도 잘 움직이던가요? 하지만 움직임이 조금 불편하단 느낌을 받으실 겁니다.
다행히도 이 방법보다 더 좋은 방법이 있습니다. 코드를 짠 걸 지우게 돼서
아쉽지만 if조건문부터 else까지 있는 내용을 싹 지우도록 합시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
extends KinematicBody2D
var velocity = Vector2.ZERO
func _physics_process(delta):
var input_vector = Vector2.ZERO
input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
if input_vector != Vector2.ZERO:
velocity = input_vector
else:
velocity = Vector2.ZERO
move_and_collide(velocity)
|
그리고 다음과 같이 코드를 작성해주세요. 작성이 끝났다면 실행해서 확인해봅시다.
아까보다는 불편한 느낌이 없어졌습니다. 단지 아쉬운 거라곤 주인공의 캐릭터 속도가 '1'로
설정되어있다 보니까 이동 속도가 느리네요. 이건 다음 파트에서 고치는 걸로 하겠습니다.
추가로 HeartBeast 영상에 Input.get_action_strength()에 대해서
[29:05 쯤] 설명하는 부분이 있으니 영상 보시는 것을 추천드립니다.
그럼 다음 파트에서 뵙겠습니다.
+) 03/22 게시글 내용 일부 수정
+) 05/10 일부 용어 레퍼런스 링크 적용
+) Anchor 용어로 잘못 설명했던 부분을 Pivot으로 수정함
'개발일지 > 고도' 카테고리의 다른 글
[Godot Engine] 05 - 충돌 제어 (1) | 2021.02.01 |
---|---|
[Godot Engine] 04 - 주인공의 움직임을 자연스럽게 해보기 (3) | 2021.01.13 |
[Godot Engine] 02 - 리소스 임포트와 윈도우 해상도 조절 (0) | 2020.12.27 |
[Godot Engine] 01 - 고도 엔진 설치하고 실행해보기 (0) | 2020.12.26 |
[Godot Engine] 00 - 고도 엔진과 개발하는 2D 액션 RPG 게임 (0) | 2020.12.23 |