본문 바로가기
유니티 게임 개발

[유니티 강좌] 2D RPG 게임 만들기 - 2 / 캐릭터 움직이기

by 호일이 2020. 4. 28.

 

 

https://hoil2.tistory.com/5?category=857730

[유니티 강좌] 2D RPG 게임 만들기 - 1 / 캐릭터 생성

유니티로 간단한 2D RPG 게임을 만들어보겠습니다. 완전 초보를 대상으로 글을 쓰는 것이기 때문에 매우 상세하게 설명하게 될 예정입니다! 유니티를 아직 설치하지 않았다면  https://unity3d.com/kr/g

hoil2.tistory.com

(전 포스팅을 보지 않으셨다면 이해가 힘들 수도 있습니다.)

 

 

 

1. 소드맨 움직이기

캐릭터 생성까지 마쳤다면 캐릭터를 움직여야겠죠?

 

하지만 안타깝게도 움직이려면 스크립트를 작성해야 합니다.

 

 

먼저 Scripts 라는 폴더를 만들고

폴더 안에서 우클릭 -> Create -> C# Script 를 눌러 스크립트 파일을 생성합니다.

C# 파일 이름 : Sword_Man

생성하고 파일의 이름을 바로 변경 하시는게 편합니다.

 

Sword_Man 파일을 더블 클릭하면 Visual Studio가 켜지면서 스크립트 코드가 뜹니다.

 

 

만약 Visual Studio가 켜지지 않는다면

 

 

Edit -> Preferences -> External Tools 의 Exxternal Script Editor를 Visual Studio 로 변경해주시면 됩니다.

여기에 Visual Studio가 안뜬다면 Browse...를 눌러서 직접 찾아 설정해주시면 됩니다.

 

 

잠깐 간단하게 새로만든 스크립트의 코드를 설명하겠습니다.

참고용으로 대충 보셔도 상관없습니다.

 

 

유니티에서 제공하는 레퍼런스들을 스크립트 파일 내에서 쓸 수 있게 합니다.

유니티에서만 쓸 수 있는 기능들을 포함하고 있어 대부분의 경우에는 적혀있어야 하지만

유니티의 기능이 쓰이지 않는 스크립트를 작성할 때는 필요하지 않습니다.

 

 

클래스 명은 파일 이름과 동일하게 설정됩니다.

: Monobehaviour 는 유니티에서 제공하는 함수, 변수 등을  Sword_Man 클래스 내에서 사용할 수 있게 한다는 뜻입니다.

(UnityEngine의 안에 정의되어 있기 때문에 UnityEngineusing 되지 않으면 사용할 수 없습니다.)

 

 

Start는 스크립트가 실행되고 Update가 실행되기 전에 단 한번 실행됩니다. 직접 해보시면 바로 이해 되실거에요.

 

 

Update는 매 프레임마다 호출되는 메시지입니다. 게임 동작을 수행하는 가장 흔한 기능입니다.

 

 

 

캐릭터를 조종하려면 캐릭터의 정보가 필요합니다.

'오른쪽으로 10만큼 이동' 이라는 명령을 할 때 아무거나 조종될 순 없으니까요.

 

캐릭터의 정보는 게임 오브젝트들한테 기본적으로 붙어있습니다.

소드맨의 부모 오브젝트를 클릭해보면 Inspector 창에 Transform 이라는 컴포넌트가 있을 겁니다

 

 

 

 

 

0

 

Transform 안에 Position 의 x,y 좌표를 바꿔주면 Scene창의 소드맨이 좌표에 따라 움직이는 걸 볼 수 있습니다.

Rotation, Scale는 소드맨의 회전, 크기에 영향을 줍니다.

sword_man 오브젝트의 Transform 컴포넌트를 조작하면 소드맨을 움직일 수 있다는 것을 알았습니다.

 

이제 스크립트에서 Transform 컴포넌트를 가져오겠습니다.

 

 

 

맨 윗줄에서 소드맨 오브젝트를 저장할 공간을 하나 만들어줬습니다.

GameObject는 단순히 오브젝트들을 저장할 수 있는 클래스이고

 

hierarchy창의 게임 오브젝트

 

저런 오브젝트를 저장할 때 써야 합니다.

objSwordMan은 그냥 변수명입니다.

 

Start 는 스크립트가 시작될 때 한번만 실행되는 것이라고 생각하면 됩니다.

objSwordMan.transform 까지는 위에서 조작한 Transform 컴포넌트까지 도착한 것이고,

.position은 짐작하셨듯이 Transform의 밑에 position 값까지 도착한 것입니다.

드디어 position을 이용해 x,y,z의 값을 바꿀 수 있습니다.

 

objSwordMan.transform.position.x = 10;

objSwordMan.transform.position.y = 5;

objSwordMan.transform.position.z = 0;

 

"이렇게 바꾸면 안되나요?"

 

objSwordMan.transform.position.x = 10; // 안됨!

아쉽게도 대부분의 사람들이 귀찮다고 생각하고 저도 그렇게 생각하는

직접 position 구조체의 필드에 값 넣기는 안된다는 것입니다.

 

따라서 값을 넣을 때는

objSwordMan.transform.position = new Vector3(x,y,z); // Vector3 

이런 방식을 사용해야 합니다.

 

코드를 Ctrl + s 로 저장하고 유니티로 돌아가서 실행을 하면...

 

소드맨의 위치는 변화가 없습니다.

 

스크립트는 파일 상태로만 있으면 실행되지 않습니다.

 

게임 오브젝트 어딘가에 붙여져 있어야 실행이 됩니다.

 

스크립트 파일을 소드맨의 Transform 컴포넌트 아래로 드래그 앤 드롭 합니다.

 

 

 

잘 붙었는데 아까 저희가 만든

public GameObject objSwordMan;

의 변수명이 None(Game Object) 라는 값을 가지고 있네요

 

스크립트에서 public으로 게임 오브젝트int, float 등과 같은 변수를 선언하면

부착된 스크립트에 저렇게 칸이 생깁니다. 유니티 엔진에서 제공해주는 편리한 기능이죠.

 

objSwordMan의 부분에 원하는 오브젝트를 넣을 수 있는데

저희는 당연히 소드맨의 오브젝트를 넣어줍시다.

 

hierarchy창의 sword_man를 None 칸으로 드래그 앤 드롭 합니다.

 

 

잘 들어갔으면 오브젝트 이름이 뜹니다.

 

 

이제 시작 버튼을 눌러보겠습니다.

 

 실행 전                                                                                      실행 중

 

Start() 가 실행되고 안의 코드

objSwordMan.transform.position = new Vector3(0,0,0); 가 정상적으로 실행된 모습입니다.

종료하면 모든 게임 오브젝트가 되돌아가게 됩니다.

 

스크립트로 돌아와서 살짝 코드를 수정해보겠습니다.

 

 

오브젝트를 저장할 공간을 없애고 transform.position만 썼는데 과연 소드맨은 움직일까요?

 

실행해보면 아주 잘됩니다.

 

굳의 게임 오브젝트를 받아오지 않아도 스크립트가 부착된 오브젝트의 transform을 자동으로 받을 수 있습니다.

만약 Sword_Man 스크립트를 다른 오브젝트에 붙였다면 소드맨이 아닌 다른 오브젝트가 움직입니다.

 

이제 연속으로 이동해보겠습니다.

 

 

Update에 transform.Translate(Vector3.right); 를 추가합니다

 

Translate는 지정된 방향으로 움직이게 하는 함수이고

Time.deltaTime은 1프레임 당 걸리는 시간입니다.

이것의 대한 설명은 밑에 추가설명에서 자세히 하겠습니다.

 

움직이기만 하면 어색하니 동작을 추가해야 하는데..

애니메이션에 대해 쓰면 간단한 포스팅이 될 것 같지가 않아 빼기로 결정했습니다.

애니메이션을 만들고 싶으신 분들께는 죄송하지만 다른 포스팅에서 집중적으로 다루겠습니다. ㅠㅠ

 

열심히 만들었던 소드맨을 과감하게 지우고 완성된 소드맨 프리팹을 불러옵시다.

에디터에서 만들어진 오브젝트를 프로젝트 창에 저장하면 프리팹(Prefab) 에셋이 됩니다. 이 프리팹 에셋은 복제가 가능해서 재사용할 때 만들어줍니다.

 

Low_Swordman -> 3.Prefab -> sword_man을 씬 창에 드래그 앤 드롭

 

Sword_Man 스크립트도 다시 붙여줍니다.

 

실행해보면 만들어져있던 애니메이션이 실행되면서 움직이게 됩니다.

 

0

 

소드맨을 방향키에 따라 움직이기 위해 Sword_Man 스크립트를 수정하겠습니다.

 

Swrod_Man.cs 소드맨 조작 코드

 

 

float h = Input.GetAxis("Horizontal")

방향키 <-, -> 에 따라 반환값이 바뀌는 함수입니다.

<- 방향키를 누르면 -1,

안누르면 0,

-> 방향키를 누르면 1 을 반환합니다.

v 변수에 방향 값을 저장했습니다.

if(h > 0) transform.localScale = new Vector3(-1,1,1);

transform.localScale은 현재 오브젝트의 크기를 조절합니다.

1이 기본값인 x좌표의 크기를 반대로 반대로 바꾸면 좌우가 반전이 됩니다.

즉 ,오른쪽 방향키를 눌렀을 때 좌우 회전을 시킵니다.

else if(h < 0) transform.localScale = new Vector3(1,1,1);

위 설명과 마찬가지입니다.

transform.Translate(new Vector3(h,0,0) * Time.deltaTime);

방향 값을 가지고 있는 h변수에 따라 움직이도록 설정했습니다.

 

실행 화면

 

 

0

 

 

2. 소드맨 애니메이션 설정

애니메이션을 설정하는 곳 Animator

 

 

소드맨을 불러왔다면 Animator 창에 소드맨의 애니메이션들이 있습니다.

 

소드맨이 움직일 때 애니메이션도 바뀌도록 설정하겠습니다.

 

1) Make Transition 설정

 

0

 

 

Make Transition이란 연결되는 애니메이션을 지정하는 겁니다.

Idle -> Run // Idle에서 Run으로 애니메이션 바꾸는게 가능

Run -> Idle // Run에서 Idle로 애니메이션 바꾸는게 가능

 

2) 애니메이션 변경 조건 지정

 

 

Parameters -> +버튼 -> Bool

변수명 moving

 

Idle -> Run 

 

 

화살표 클릭 -> Conditions 추가 -> moving -> true

moving 변수가 true일 때 Idle에서 Run으로 애니메이션이 전환된다는 뜻입니다.

 

Run -> Idle

 

 

위의 설명과 마찬가지입니다.

 

Sword_Man.cs 

 

 

animator = GetComponent<Animator>();

부착된 오브젝트의 Animator의 데이터를 받아옵니다.

 

if(h>0 or h<0) animator.SetBool("moving", true);

움직일 때 animator Parameter인 moving을 true로 바꿉니다.

 

else animator.SetBool("moving", false);

움직이지 않을 때 moving을 false로 바꿉니다.

 

 

이대로 실행해서 걸어보면 걷는 애니메이션 실행은 되지만 약간 반응이 느린 느낌이 듭니다.

빠른 반응을 위해 animator로 돌아가서 몇가지 수정하겠습니다.

 

 

 

둘 다 Has Exit Time 해제, Fixed Duration 해제, Transition Duration 0, Transition Offset 0으로 바꿔줍시다.

애니메이션 전환이 부드럽게 되길 원하신다면 Transition Duration 값을 올리면 됩니다.

 

실행 화면

 

0

 

 

 

추가 설명

1. Time.deltaTime을 무조건 써야 하는지.

Time.deltaTime은 1프레임 당 걸리는 시간이라고 했었죠.

모니터마다 초당 프레임이 60, 120, 144 ... 등등 여러가지가 있습니다.

극단적으로 60 프레임 모니터 A와 240 프레임 모니터 B를 비교해보겠습니다.

1초마다 60번 Update문을 반복하는 'A', 1초마다 240번 Update문을 반복하는 'B'

A에서 게임을 개발하다가 B에서 게임을 실행시키면 너무 값이 달라지겠네요.

속도로 비교했을 때 A보다 B가 4배 빠릅니다. 장난아니죠.

이것을 해결하기 위해 Time.deltaTime을 값에 곱하게 되면 프레임이 아무리 달라져도 결과는 같습니다.

 

2. 좌우 반전을 할 때 transform.localScale을 사용한 이유

보통 회전 관련된 것은 transform.rotation을 사용합니다만

애니메이션에 rotation 항목 지정되어 있어서 그런지 transform.rotation은 변경이 되지 않더군요.

그래서 어쩔 수 없이 transform.localScale을 사용하게 되었습니다.

 

3. Transition Duration 값을 올리면 방향 전환할 때 버벅이는데요.

방향키를 바꿀 때 Input.GetAxis() 함수는 반환값 0이 무조건 나오는 것 같습니다.

따라서 else animator.SetBool("moving", false); 이 코드를 실행하는 거죠.

Input.GetAxis() 말고 Input.GetKey() 함수를 사용하면 괜찮을 겁니다.

 

읽어주셔서 감사합니다!

반응형

댓글