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

곱 번째 파트인 애니메이션 플레이어를 사용한 애니메이션 다루기입니다.

저번 시간에는 오브젝트 충돌 설정과 Ysort에 대해서 배웠습니다. 이번에는 캐릭터의 애니메이션을 추가하며

더욱 게임의 깊이를 더해가는 작업을 할 것입니다. 그동안 밋밋하게 움직이니까 게임 같지 않으셨겠지만

이제는 살이 붙어가는 파트라고 생각하시면 편할 것 같네요! 그럼 진행하도록 하겠습니다.

 

먼저 애니메이션 플레이어(Animation Player) 노드를 추가해야 합니다. 주인공 씬(Scene)에서 처리해야 하므로

아이콘을 클릭하여 주인공 으로 넘어가도록 합니다.

 

Player가 선택되어 있는 상태에서 +를 클릭해 노드를 추가합니다.

 

AnimationPlayer노드가 생성되자마자

하단의 애니메이션(Animation) 버튼이 활성화되는데 이 버튼을 클릭합니다.

 

애니메이션 버튼을 눌렀으면 해당 메뉴가 뜹니다. 거기서 New를 선택합니다.

 

'RunRight'라고 입력 후 OK 버튼을 누릅니다.

 

들어가기 앞서, 애니메이션 창의 내용을 간단하게 설명드리겠습니다.

  • 1. 스냅: 기준(시간/FPS)으로 한 값으로만 간격 이동합니다.
        ┗ 이동할 때는 현재 커서 쪽에 있는 타임라인에서 마우스 클릭&드래그
  • 2. 초/프레임 구분: 시간(초)으로 볼 건지, 프레임(FPS)으로 볼 건지 선택합니다.
  • 3. 애니메이션 길이: 총 애니메이션 길이(시간/FPS)를 설정합니다.
  • 4. 확대: 말 그대로 확대 비율입니다. 게이지 바가 오른쪽으로 갈수록 확대됩니다.

 

여기서 우리는 초(Seconds)로 할 것이므로 2번은 건들 필요 없고

스냅은 '0.1'로 잡도록 하겠습니다. 확대는 편하신 대로 하시면 됩니다.

 

이제부터 진행하시면서 헷갈릴 수도 있으므로 잘 따라오시길 바랍니다.

우선, 애니메이션 길이를 '0.6'으로 설정합니다.

 

Sprite를 선택하고 우측 탭 중 Animation에서 Frame을 '1'로 수정합니다.

 

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

 

그러면 키를 생성할 거냐고 묻는 창이 뜹니다. 또한 옵션으로

Use Bazier Curves가 있는데 이 기능은 사용하지 않습니다. 

 

우리가 하는 애니메이션 작업은 프레임 바이 프레임(Frame by frame)으로

여기에는 필요 없기 때문입니다. 따라서 그냥 Create을 눌러 진행합니다.

 

트랙이 생기고 키가 생성되었습니다.

처음 트랙 생성 시에는 커서가 자동으로 옮겨지지 않기 때문에.

여기서 타임라인에 있는 커서0.1로 옮겨주세요.

프레임 값이 '1'로 되었다면 다시 '2'로 수정해주시고

그다음 아까 눌렀던 키 아이콘🔑을 눌러 키를 생성합니다.

 

이미 애니메이션 트랙을 생성했기 때문에 아까 같이 묻는 창은 더 이상 생기지 않습니다.

추가로 키를 더 생성합니다.

 

키를 이렇게 순차적으로 생성합니다. 끝 0.5초 지점은 다른 프레임을 넣겠습니다.

 

여기 프레임부터는 위쪽 방향을 향합니다. 따라서 이 키프레임을 추가하는 건 적절하지 않은데요.

그럼 어느 프레임을 넣으면 좋을까요? 이전 프레임을 살펴보면, 1, 2까지는 뛰는 모션이고

3에 멈추고 4, 5가 다시 뛰는 모션입니다. 0 프레임도 멈추는 프레임이니 '0'을 추가하도록 합니다.

 

한번 확인해봅시다. 애니메이션 창에서 우측 하단에 있는 루프 아이콘을 클릭합니다.

 

그다음에 재생 버튼을 눌러서 재생해봅시다.

 

재생해보면 꽤 자연스럽게 움직입니다.

 

혹시 키 프레임을 넣다가 중간에 잘못된 키 프레임이 있으면 수정할 수 있습니다.

이렇게 키 프레임을 선택하시고

 

우클릭을 해서 지우거나 아니면 DELETE키를 눌러 빠르게 지울 수도 있습니다.

 

지우게 되면 이렇게 공간이 비게 되는 것을 확인할 수 있습니다.

 

또는 선택하고 나서 우측 상단 인스펙터(Inspector) 뷰에 있는 속성값(Properties)에서

Value 부분을 수정해 키 프레임의 값을 변경할 수 있습니다.

 

문제가 없다면 다음으로 넘어갑시다.

애니메이션을 추가로 더 생성합니다. Animation - New 버튼을 눌러 생성하도록 합니다.

 

생성할 애니메이션 이름은 'RunUp'으로 합니다.

 

이 애니메이션 또한 시간을 '0.6'으로 바꿔주도록 하고 커서는 '0'초에다 두세요.

 

그다음에 스프라이트에서 프레임을 '7'까지 올려줍니다.

6도 위를 향하고 있지만 첫 번째 프레임으로 쓰지 않고 아까처럼 마지막 프레임으로 활용합니다.

 

이제 옆에 키 모양 아이콘🔑을 눌러서 프레임 트랙을 생성합니다.

 

커서를 0.1로 옮기고 나서 키 프레임을 생성합니다.

그다음 순차적으로 '11'까지 프레임을 넣어주신 뒤 12 프레임은 추가하지 않고

 

다시 6 프레임으로 돌아가서 '6'프레임을 추가합니다.

 

이번에도 루프 아이콘 켜주시는 거 잊지 마시고요.

 

프레임을 정상적으로 넣었다면 다음과 같이 재생될 것입니다.

 

현재 RunRightRunUp 애니메이션을 만들었습니다. 이번에는 왼쪽 애니메이션을 만들도록 합시다.

아까 했던 대로 Animation - New를 눌러 애니메이션을 생성합니다. 이름은 'RunLeft'로 합니다.

 

동일하게 애니메이션의 시간은 '0.6', 루프는 켜줍시다.

 

왼쪽 모션은 키프레임이 13에서 시작됩니다.

키 프레임을 '13'까지 올린 뒤 13 프레임에서 키 모양 아이콘🔑을 눌러 프레임 트랙을 생성합니다.

 

타임라인에서 다시 커서를 0.1로 옮기는 거 잊지 마시고요.

 

커서를 옮기면 키프레임 값이 아까와 비슷하게 '14'->'13'으로 변경될 텐데요. '14'로 올려주시고

순차적으로 키 프레임을 생성해서 키프레임 값이 18까지 오를 때까지 생성해줍시다.

 

이제 키 프레임의 값을 '12'로 수정해주시고 키를 눌러 추가하시면 됩니다.

 

마찬가지로 재생해서 자연스러운지 확인해 봅시다.

 

뛰는 모션 중 마지막 모션인 아래 방향을 만듭시다. 애니메이션 이름은 'RunDown'으로 합니다.

 

애니메이션 시간과 루프는 이전과 동일하게 맞춰주세요. 그리고 첫 프레임은 '19'에서 시작합니다.

 

프레임 트랙을 생성하고 여기까지 빼먹은 것 없이 키를 순차적으로 넣으시면 됩니다.

 

마지막 프레임은 돌아와서 '18'프레임을 넣습니다.

 

재생을 시켜서 자연스러운지 확인해 봅시다.

여기까지 4방향에 대한 뛰는 애니메이션을 만들어보았습니다.

이제는 Idle(대기) 모션을 만들도록 하겠습니다.

 

Animation - New로 애니메이션을 생성합니다 이름은 'idleRight'로 짓습니다.

 

이번에는 애니메이션 시간을 '0.1'로 변경합니다. Run 모션에서 죄다 0.6로 일치시켰듯이 Idle 모션에는

애니메이션 시간을 '0.1'로 다 동일하게 진행할 것입니다. 루프는 당연히 켜주세요.

 

그다음 프레임 값을 '0'으로 변경 한 뒤 키를 눌러 프레임 트랙을 생성합니다.

 

아까처럼 새로운 애니메이션을 생성합니다. 이름은 'IdleUp'으로 합니다.

 

프레임은 '6' 프레임을 넣습니다. 지금 이미지에서는 6 프레임을 넣었기 때문에 '7'로 값이 올랐으므로 참고하세요.

애니메이션 시간과 루프는 이전과 동일하게 합니다.

 

다시 새로운 애니메이션을 생성합니다. 이름은 'IdleLeft'로 정하고 프레임은 '12'를 넣습니다.

애니메이션 시간과 루프는 이전과 동일합니다.

 

마지막으로 애니메이션을 생성합니다. 이름은 'IdleDown'으로 정하고 프레임은 '18'를 넣습니다.

애니메이션 시간과 루프는 이 역시 동일합니다.

 

여러분들이 여기까지 애니메이션을 잘 만들어왔다면 이렇게 드롭다운 메뉴를 클릭했을 때

생성했던 애니메이션들이 쭉 나열되어 보일 것입니다.

 

우선은 RunRight를 선택해 애니메이션이 재생되는지 확인해 봅시다.

 

그다음 AutoPlay on Load이 부분을 클릭합니다.

이후 게임을 플레이해봅시다.

 

애니메이션이 로드돼서 캐릭터가 계속 뛰고 있는 것처럼 보이네요.

창을 꺼주시고 아까 킨 AutoPlay on Load를 다시 눌러서 비활성화하도록 합니다.

테스트를 위해 잠깐 킨 거니까요. 이제 코드로 넘어갑시다.

 

Player 노드 옆에 있는 해당 아이콘을 클릭해주세요.

 

스크립트(Script) 편집기로 넘어와서 우리가 애니메이션 플레이어 노드

어떻게 연결할 것인지에 대해서 알아보도록 합시다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
extends KinematicBody2D
 
const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500
 
var velocity = Vector2.ZERO
 
var animationPlayer = null
 
func _ready():
    animationPlayer = $
 
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")
    input_vector = input_vector.normalized()
    
    if input_vector != Vector2.ZERO:
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
        
    velocity = move_and_slide(velocity)

현재 코드에 추가하여 다음 코드를 작성합니다. 비슷한 부분은 그대로 두고 빠진 부분을 추가하세요.

여기까지 작성했으면 오류가 잠시 뜰 텐데 무시하고 다음을 봐주세요.

 

$까지 작성하셨다면, 저런 팝업이 생기셨다가 사라졌거나 혹은 남아있으실 것입니다.

 

여기서 $의 역할은 현재 노드 구조에서 자식 노드로 들어간 노드들을 참조할 수 있도록 해줍니다.

그래서 아까 팝업에서 현재 위의 이미지에 보이는 노드들이 뜬 이유가 바로 그거죠.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
extends KinematicBody2D
 
const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500
 
var velocity = Vector2.ZERO
 
var animationPlayer = null
 
func _ready():
    animationPlayer = $AnimationPlayer
 
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")
    input_vector = input_vector.normalized()
    
    if input_vector != Vector2.ZERO:
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
        
    velocity = move_and_slide(velocity)

마저 작성하도록 합시다.

 

자 그러면 왜 var animationPlayer 는 선언과 동시에 바로 $AnimationPlayer 값을 넣지 않는 것일까요?

만약에 이렇게도 작성할 수 있지 않을까요?

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extends KinematicBody2D
 
const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500
 
var velocity = Vector2.ZERO
 
onready var animationPlayer = $AnimationPlayer
 
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")
    input_vector = input_vector.normalized()
    
    if input_vector != Vector2.ZERO:
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
        
    velocity = move_and_slide(velocity)

이렇게 되면 한 줄로 줄일 수 있어 좀 더 간결해 보입니다.

 

자 그럼, 애니메이션 플레이어 변수도 선언하고 참조도 했겠다 변수를 활용해봐야겠죠?

그런데 우린 아직 처음이라 뭐가 있는지 잘 모릅니다. 이럴 때 Docs를 확인하는 게 도움이 됩니다.

 

우리가 사용해볼 함수는 play 입니다. 뒤에 있는 것들은 매개변수인데,

자세히 보실 분들은 눌러서 확인하셔도 되지만 여기서 따로 깊게 다루진 않겠습니다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extends KinematicBody2D
 
const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500
 
var velocity = Vector2.ZERO
 
onready var animationPlayer = $AnimationPlayer
 
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")
    input_vector = input_vector.normalized()
    
    if input_vector != Vector2.ZERO:
        animationPlayer.play(
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
        
    velocity = move_and_slide(velocity)

코드는 다음과 같이 작성해봅시다.

 

아마 'play('를 입력하는 도중에 위의 이미지처럼 떴을 것입니다. 여기서 "RunRight"를 선택해 주세요.

만약 안 떴다면 아래 코드처럼 작성해주세요.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
extends KinematicBody2D
 
const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500
 
var velocity = Vector2.ZERO
 
onready var animationPlayer = $AnimationPlayer
 
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")
    input_vector = input_vector.normalized()
    
    if input_vector != Vector2.ZERO:
        animationPlayer.play("RunRight")
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        animationPlayer.play("IdleRight")
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
        
    velocity = move_and_slide(velocity)

추가로 else: 부분에도 애니메이션을 실행하는 코드를 작성해 줍시다.

다 작성했다면 게임을 플레이해서 확인해 봅시다.

 

입력 값을 조건으로 좌우도 구분해 봅시다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
extends KinematicBody2D
 
const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500
 
var velocity = Vector2.ZERO
 
onready var animationPlayer = $AnimationPlayer
 
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")
    input_vector = input_vector.normalized()
    
    if input_vector != Vector2.ZERO:
        if input_vector.x > 0:
            animationPlayer.play("RunRight")
        else:
            animationPlayer.play("RunLeft")
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        animationPlayer.play("IdleRight")
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
        
    velocity = move_and_slide(velocity)

다음 코드를 작성합니다. 

 

확인해 봅시다. 좌우는 잘 구분되어 재생이 되지만, 다른 방향으로 이동할 때 어색한 모습이 보이네요.

코드로 해결을 할 수 있겠지만 더 좋은 기능인 애니메이션 트리(Animation Tree)를 활용하여 여러 가지 방향에

캐릭터 애니메이션을 각기 재생하는 기능을 활용할 것입니다.

 

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

 

+) 05/10 일부 용어 레퍼런스 링크 적용

728x90
728x90