전체 페이지뷰

2017년 6월 20일 화요일

Unity Tutorial: Roll-a-Ball, part 1

메뉴를 하나하나 다 알아보고 갈까 하였으나 그런 식으로는 끝도 없고 재미도 없어 지금부터는 예제를 따라 만들어볼까 합니다.
유니티 홈페이지의 자습서에 가면 몇개의 튜토리얼 예제들이 동영상으로 설명되어 있습니다. 그것들을 따라 만들면서 공부를 진행해볼까 합니다.

이번에는 그 중 가장 기초적인 Roll-a-Ball입니다.


에셋을 사용하지 않고 기본 제공 primitive shape만을 이용하여 게임 오브젝트를 배치하고 공을 굴려 주변 물체들을 모으는 게임입니다.

Setting up the Game


먼저 File>New Project 하여 새 프로젝트를 "Roll a Ball"이라는 이름으로 원하는 경로에 만들어 줍니다.

그러면 텅빈 씬과 함께 새 프로젝트가 생성됩니다.
오브젝트를 새로 추가하기 이전에 씬을 저장해 봅니다.
File>Save Scene 을 선택하면 프로젝트 내의 Assets 폴더가 표시되는데 그 안에 "_Scenes"(언더스코어를 사용한 것은 별다른 것이 아니고 이름 정렬시 가장 위에 오도록 하기 위한 것입니다)라는 폴더를 만들고 그 안에 MiniGame이라는 이름으로 씬을 저장하겠습니다.


이제 프로젝트 창의 폴더에 MiniGame이 나타납니다.

이제 플레이 필드인 게임 보드를 만들 차례입니다.
Unity에서 제공되는 Plane으로 만들 예정이며 GameObject>3D object>Plane 을 선택하거나 Hierarchy 창에서 Create>3D object>Plane 을 선택하여 만들 수 있습니다.


추가된 Plane 오브젝트를 우클릭 Rename 선택(또는 천천히 두번 클릭)하여 Ground로 이름을 바꿔줍니다.

다음으로 인스펙터 창의 Transform에 있는 톱니바퀴 메뉴에서 Reset을 눌러 오브젝트를 기준점인 (0,0,0)으로 이동시켜 줍니다.



다음으로 오브젝트를 선택하고 커서를 씬뷰 위에 둔 후 F키를 눌러주거나, Edit>Frame Selected를 선택하면 전체 오브젝트를 볼 수 있는 뷰로 화면이 옮겨집니다.

오브젝트를 보면 grid 모양들이 표시되어 있는데 그것들을 꺼 보겠습니다. 씬뷰 창 툴바에 Gizmos 메뉴를 누르고 Show Grid 체크 해제 합니다.


이제 Ground의 크기를 조절 하겠습니다.

몇 가지 방법으로 가능한데,
툴바의 Scale tool을 선택하고 오브젝트에 표시된 핸들을 잡아당기거나,
Scale tool

Transform 메뉴에서 각 축의 이름 위에 커서를 두고 드래그하여 조절 가능합니다.

또는 위의 Scale 항목에 직접 숫자를 입력할 수도 있습니다.

Plane은 평면이므로 높이(y축) 조절이 되지 않습니다. 통상 1로 설정되어 있고, 핸들로 숫자가 바뀌기는 하나 음수로 되면 반대편을 향할 뿐입니다.


다음으로 플레이어 오브젝트를 생성하겠습니다.
Hierachy 창에서 Create>Sphere를 선택하여 오브젝트를 생성하고 이름을 Player로 바꿔줍니다. 역시 Reset하여 위치를 원점으로 설정하여 준 뒤, Frame selected 선택하여 구형 오브젝트에 포커스를 맞춥니다.


이 (0,0,0) 포인트에서 Sphere 오브젝트는 플레인에 반쯤 묻혀있습니다. 이 구를 올려서 플레인 위로 올리려고 합니다. 유니티 primitive 오브젝트의 3차원 크기는 1X1X1이므로, 구의 y축을 0.5만 위로 올리면 플레인 위로 올라올 것입니다.




이제 게임뷰 창에서 보면 게임 오브젝트에 조명이 비춰져 있고 플레인에 그림자가 진 것을 볼 수 있습니다. 모든 Unity 씬에는 디폴트 sky box와 태양을 의미하는 조명이 설정되어 있어서 디폴트값을 설정할 필요는 없습니다. 그러나 배경이 전부 하얀 것보다는 색이 있는 것이 보기에 더 편할 것입니다.

이제 색을 넣는 법을 알아 보겠습니다.

모델에 텍스쳐나 칼라를 넣기 위해서 material을 사용합니다. 아직 material을 깊이 알아보거나 텍스쳐까지 들어가진 않을 것이고, 단순히 standard material을 사용하여 오브젝트에 색을 입혀 볼 것입니다.

material을 저장하기 위해 프로젝트에 새 폴더를 생성합니다.
프로젝트 창의 Create>Folder 선택하고 그 폴더의 이름을 Materials로 해 주고 그 폴더 안에 Create>Material 하여 새 material을 생성해준 뒤 이름을 Background로 바꿉니다.




 이제 이 Background material을 선택하면 인스펙터 창에 프로퍼티들이 나타납니다.

첫 번째 Albedo의 컬러영역을 클릭하면 색 조절 창이 나타나는데 짙은 파랑으로 표현하기 위해 RGB 값을 0,32,64로 입력합니다.

이 material을 바닥 plane에 적용하기 위해서는 그냥 material을 클릭-드래그하여 씬뷰의 Plane 오브젝트로 끌어놓으면 됩니다.

바닥 색이 짙은 파랑으로 바뀌었습니다.

이제 Player 오브젝트에 좀더 그림자가 잘 지게 하기 위해 메인 directional light를 회전시켜 놓으려고 합니다.

Hierachy 창에서 Directional Light을 선택하고 y-axis 값을 60으로 바꿔줍니다.

빛이 좀더 위쪽에서 비추어 모양이 잘 살아납니다.


Moving the Player


이제 플레이어 오브젝트를 움직여볼 차례입니다. 먼저 생각해야 할 것은 이 플레이어 오브젝트가 어떤 방식으로 움직여야할 것인가 하는 것입니다.

이 구형 오브젝트는 게임 내를 굴러다니고 벽을 만나면 부딪히고, 날아다니지는 않고, 큐브형의 오브젝트를 만나면 그것을 수집하길 원합니다.

그렇게 하려면 이 오브젝트에 물리법칙이 적용되어야 하는데, 그러려면 이 오브젝트에 rigid body의 성질이 붙어야 합니다.

먼저 구형의 Player 오브젝트를 선택하고 상단의 Component>Physics>Rigidbody를 선택하여 줍니다. 또는 인스펙터 창 하단의 Add Component>Physics>Rigidbody를 해주면 됩니다.


그러면 인스펙터 창 하단에 새로 Rigidbody라는 항목이 표시됩니다.

이 인스펙터 창의 항목 이름 옆에 붙은 삼각형을 눌러서 펼치고 접거나, 우상단 톱니 모양을 눌러 Move up/Move down으로 순서를 바꿀 수 있고 그것들은 게임에 영향을 미치지 않습니다.

이제 이 오브젝트가 우리의 컨트롤 하에 움직일 수 있게 해야 하는데 그것은 스크립트를 통해 이루어집니다.

프로젝트 뷰에 이제 스크립트들을 모아둘 폴더를 만듭니다.
프로젝트 뷰 상단의 Create>Folder 선택하여 폴더를 만들고 그 이름을 Scripts라고 하겠습니다.

새로운 C# 스크립트를 만들기 위해서는
메뉴에서 Assets>Create>C# Script 하거나
프로젝트 뷰 상단 Create>C# Script를 선택해도 가능하지만 스크립트 생성과 더불어 오브젝트에 붙여 주려면

오브젝트를 선택하고, 우측 인스펙터 창에서 Add Component>New Script하는 것이 효율적입니다. 이름은 PlayerController로 하겠습니다. 언어 종류는 C#으로 두고 Create and Add를 눌러주면 새 스크립트가 생성되어 인스펙터 창에 나타납니다.



이 때, 생성된 C# 스크립트는 루트폴더에(여기서는 Assets)에 있으므로 프로젝트뷰에서 드래그 하여 생성한 Scrips 폴더로 옮깁니다.

생성된 C# 파일을 선택하여 보면, 인스펙터 창에 미리보기가 나타나는데 여기서는 편집이 불가능하고, 이 스크립트를 열어야 합니다. 역시 방법은 여러가지가 있습니다.

오브젝트를 선택하고 나타나는 인스펙터 창의 스크립터 항목의 톱니 표시를 누르고 Edit Script를 선택하거나, 프로젝트 뷰에서 스크립트를 선택하고 나타나는 인스펙터 창 상단의 Open을 눌러도 됩니다.



그러면 지정한 에디터로 스크립트를 열 수 있게 됩니다. 저는 Visual Studio가 연결되어 있습니다(에디터는 메뉴의 Edit>Preferences>External tools에서 변경 가능합니다).

에디터에서 클래스 내에 자동 생성된 부분을 먼저 지우고 기본 골격만 다음과 같이 남깁니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class PlayerController : MonoBehaviour {
 
}
cs


이제 스크립트에 포함될 내용을 생각해 봅니다.
매 프레임 플레이어의 input을 받고 움직임에 반영해야 할 것입니다. 그렇다면 어디서 이 input을 체크해야 하겠습니까?

거기에는 두가지 옵션이 있습니다. UpdateFixedUpdate가 그것입니다.
Update()는 프레임마다(프레임 렌더링 전에) 호출되는 주기적인 메소드이고, FixedUpdate()는 물리적 계산시 마다 호출되는 메소드로서 우리의 물리 코드가 들어갈 곳으로 적당합니다.

우리의 오브젝트는 rigid body를 적용하여 물리 법칙을 계산해야 하므로 FixedUpdate에 코드를 작성하는 것이 맞을 것입니다.

유저로부터 어떻게 입력을 받고 처리하는가를 알아보기 위해 유니티 스크립팅 API에서 Input을 검색해 봅니다. 그러면 클래스의 설명과 변수, 함수들이 설명되어 있습니다. 필요한 함수를 골라 클릭해보면 예제까지 나와 있어서 상당히 유용합니다.

우리는 그 많은 함수 중 GetAxis를 이용하려 합니다.
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
cs

이과 같은 방식으로 입력의 수평, 수직값을 받아올 수 있습니다.

오브젝트에는 rigid body가 적용되었습니다. 같은 방식으로 API에서 Rigibody를 검색하여 용법을 알아보고 우리의 오브젝트에 적용하는 방법을 연구해 봅니다.

지금 사용할 함수는 AddForce()입니다. 이것을 적용해야 우리의 오브젝트에 힘을 가해 움직일 수 있게 됩니다.
public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force);
cs
이 함수는 위와 같은 방식으로 사용 가능하다는 것을 API를 통해 알 수 있습니다.
Vector3(x,y,z)는 3차원 상에서 벡터와 위치를 나타내는 구조체입니다.

스크립트 내부에 rb라는 변수를 두고, 게임 시작시 호출되는 Start()함수 내에 Rigidbody에 대한 레퍼런스를 얻어서 연결합니다.
private Rigidbody rb;
 
void Start()
{
    rb = GetComponent<Rigidbody>(); 
}
cs

그리고 FixedUpdate에서 Vector3값을 생성하여 그 값만큼 rb에 힘을 가해주면 됩니다.
전체 코드는 아래와 같습니다.

public class PlayerController : MonoBehaviour {
 
    private Rigidbody rb;
 
    void Start()
    {
        rb = GetComponent<Rigidbody>(); 
    }
    void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");
 
        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
 
        rb.AddForce(movement);
    }
}
cs


파일을 세이브한 뒤 다시 유니티로 돌아갑니다.

이제 게임뷰로 가서 플레이를 누르고 방향키를 눌러서 조작해보면 볼이 움직이는 것을 알 수 있습니다. 움직임이 좀 느린 감이 있지만 기본 컨셉은 잘 작동됩니다.

이 속도를 조절하기 위해서 코드를 좀 수정합니다.

단순히
rb.AddForce(movement * 100);
cs
처럼 바꿔줘도 되겠지만 저 숫자를 바꾸고 편집할 때마다 재컴파일 해야하는 등 시간이 많이 걸립니다. 이걸 단순히 바꿔주기 위해 클래스 내에 public 변수를 하나 둡니다.
public class PlayerController : MonoBehaviour {
 
    public float speed;
    private Rigidbody rb;
 
    void Start()
    {
        rb = GetComponent<Rigidbody>(); 
    }
    void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");
 
        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
 
        rb.AddForce(movement*speed);
    }
}
cs

speed라는 변수를 하나 두고, AddForce 사용시 그것을 팩터로 곱해 주었습니다.
바뀐 파일을 저장해주고 Unity로 돌아가서 오브젝트의 인스펙터창을 보면,


Speed라는 프로퍼티가 하나 생성된 것을 알 수 있습니다.
이제 여기다 직접 숫자를 넣어 속도를 조절해 볼 수 있게 되었습니다.

숫자를 바꾸어 가며 테스트 해 보시기 바랍니다. 속도가 바뀐 것을 알 수 있으실 겁니다.

댓글 없음:

댓글 쓰기