게임 엔진/Unity

Unity 4일차 - 오브젝트

FakeZero 2021. 6. 21. 01:05

지루하던 C# 스크립트의 기초 시간을 보내고 이제야 드디어 게임 엔진을 제대로 다루는 시간이 왔습니다. 강의에 앞서서 여기까지 노력해 주신 여러분 너무나도 감사드린다는 말씀을 드리고 싶군요.

오늘 강의부터는 간단한 게임을 만들며 게임엔진을 공부해 볼껍니다. 오늘 만들어 볼 게임은 바로 룰렛입니다.

 


1. 계획하기

게임을 만들기에 앞서 먼저 게임을 계획하는 일부터 시작하겠습니다.

이 단계는 의외로 놓치고 지나갈 수도 있는 부분입니다. 실제로 이 글을 보고 계시는 여러분도 '에이, 계획은 무슨. 그냥 따라하기만 하면 되지.'라고 생각하며 이 부분을 건너뛰는 분들이 계실 것이라 생각합니다. 하지만 계획이라는 단계는 게임 개발에 있어서 가장 중요한 부분을 담당합니다. 계획이 없고 그저 마음 가는대로만 만들게 되면 게임은 결국 이도저도 아닌 이른바 망겜이 되는 것입니다. 그러니 자신의 역량, 만약 게임을 같이 만드는 팀원이 있다면 팀원의 역량도 정확하게 파악하여 게임을 기획해야 합니다.

그러면 이제 이번에 만들 룰렛 게임에 대한 계획을 세워보죠.

1. 만들 게임: 룰렛 게임
2. 필요한 리소스: 룰렛 그림, 룰렛이 멈췄을 때 룰렛을 가리키는 바늘
3. 게임이 구동될 플랫폼: PC
4. 필요한 기능: 룰렛을 회전 및 멈추게 하는 기능

대략적으로는 이렇게 되겠군요. 지금은 이렇게 간단하게 쓰지만 게임이 점점 복잡하게 될 수록 이 과정은 더욱 세심하게, 더욱 체계적이게 이루어져야 합니다.


2. 리소스 준비하기

룰렛을 만들어야 하니 위에서도 말했듯이 룰렛 그림과 바늘이 필요하겠군요. 원래라면 그래픽을 다루는 소프트웨어를 이용해 직접 그려야 하겠지만 제 강의에서는 예제 리소스를 제공하도록 하겠습니다.

chapter3.zip
0.06MB

(본 파일은 <유니티 교과서>의 출판사, [길벗]에서 제공한 것임을 밝히며 2차 수정 및 배포를 금지합니다.)

압축 파일의 안에는 룰렛 그림과 바늘 그림이 들어가 있습니다. 압축을 풀어서 사용할 준비를 하도록 합시다.


3. 프로젝트 생성 및 사전 설정

새로운 게임을 만들게 되었으니 새로운 프로젝트를 만들 필요가 있겠군요. 저는 Roulette이란 이름으로 2D 게임 프로젝트를 만들도록 하겠습니다.

위와 같은 화면이 나왔다면 이제 리소스를 게임 엔진에서 쓸 수 있도록 불러와 보겠습니다.

위 처럼 Assets 창에 리소스 파일이 불러와졌다면 성공입니다.

이번엔 빌드 설정을 할껍니다. 게임 엔진에서 빌드란 만들어진 게임에 문제가 없는지 확인하고 게임을 정상적으로 작동 시킬 수 있도록 준비해주는 과정을 얘기합니다. 즉, 프로그래밍에서 컴파일에 해당하는 과정이라고 봐도 됩니다. 더 자세하게 얘기하면 복잡할 수 있으니 지금은 이렇게 이해해두도록 합시다.
Unity에서는 빌드 과정에 완전히 독립된 게임 파일을 만드는 과정도 포함되어 있습니다.

빌드 설정에 들어가면 해당 창이 뜰 것입니다. 우리는 윈도우를 사용하므로 그냥 두면 됩니다. 오른쪽 위의 x버튼을 눌러서 빌드 설정에서 나갑시다.
이제 게임 화면을 설정해야합니다. 이번 강의에선 1600 x 900 해상도를 사용하도록 하겠습니다.

위와 같은 과정으로 해상도를 설정해주시면 됩니다.

위 화면에서 흰 사각형으로 표시되어있는 구역이 게임화면이 되는 구역입니다.

자 이제 이 상태로 이 씬을 저장하겠습니다.

이 상태에서 그냥 save 버튼을 누르면

Assets창에서 Scenes 폴더에 들어가면 저장된 씬 파일을 볼 수 있습니다.


4. 오브젝트 배치

이제 씬 화면에 오브젝트를 배치해 보겠습니다.

오브젝트(Object)란 말뜻 그대로 물건이란 뜻입니다. 좀 더 정확하게는 게임에 배치되어 있는 물건이죠. 우리가 가지고 있는 사진, 3D파일 등의 게임에 집어넣을 수 있는 파일들, 특히 형상적인 무언가를 추가하는 파일을 게임에 추가하게 되면 Hierarchy창에 우리가 추가한 파일이 생기게 되는데 이때부터 우리가 추가한 파일은 게임의 사물, 오브젝트가 됩니다.

자 위와 같이 됬다면 성공입니다. 그런데 지금 화면이 뭔가 많이 확대 된 것 처럼 보이죠? 제가 Scene창을 건드려서, 화면을 움직였기 때문입니다.
Scene 창을 움직이는 방법은 간단합니다 Scene 창에 우클릭을 한 상태로 마우스를 움직이면 됩니다. 마우스의 휠을 굴리면 확대와 축소도 할 수 있습니다.

이제 룰렛의 위치를 설정해 줍시다. 조작 도구를 사용하면 화면에 2개의 화살표가 생기면서 룰렛 그림을 이동시킬 수 있지만 이번에는 특정한 좌표에 룰렛 그림을 둬 봅시다. Inspector창의 Transform 에서 Position 이라고 써있는 글자 옆에 있는 값은 위치 값입니다. 여기에 모두 0을 대입해서 룰렛이 화면의 정중앙에 오도록 해줍시다.

자 이제 같은 방법으로 바늘도 배치해 줍시다. 바늘은 (x, y, z) = (0, 3.2, 0)로 이동해 주도록 합시다.
성공하면 다음과 같은 상황이 될 겁니다.

이제 실행 도구를 통해 게임을 실행하면 게임 화면에 룰렛이 나타나는 것을 확인할 수 있습니다.

추가로 위의 과정을 통하면 기본 배경의 설정도 바꿀 수 있습니다.


5. 스크립트 작성

룰렛이 어딨는지 배치해줬으니 이제 룰렛에게 움직임을 줘야겠군요 C# 스크립트를 써봅시다. Assets 창에 우클릭해서 새로운 C# 스크립트를 만들어 봅시다. 저는 RouletteCotroller라는 이름으로 C# 스크립트를 만들었습니다.

오브젝트를 회전 시키는데는 Unity에서 지원하는 클래스와 메서드를 쓰도록 하겠습니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RouletteController : MonoBehaviour
{
	float rotSpeed = 0; //회전 속도
    
    void Start()
    {
    
    }
    
    void Update()
    {
    	//클릭하면 회전 속도를 설정한다.
        if(Input.GetMouseButtonDown(0))
        {
        	this.rotSpeed = 10;
        }
        
        //회전 속도 만큼 룰렛을 회전 시킨다.
        transform.Rotate(0, 0, this.rotSpeed);
    }
}

C# 기초 스크립트 시간을 보내면서 코드를 분석하는 능력은 어느정도 갖추었을 것이라 생각하니 새로 등장한 키워드 들만 정리하도록 하죠.

if(Input.GetMouseButtonDown(0))

이 if문 안에 들어가 있는 조건문은 Unity에서 제공하는 조건문으로 마우스를 클릭했을 때 True 값을 반환하는 키워드 입니다. 즉, 이 조건문은 bool형 자료형을 가지고 있습니다.
https://docs.unity3d.com/kr/530/ScriptReference/Input.GetMouseButtonDown.html

 

Unity - 스크립팅 API: Input.GetMouseButtonDown

이 값은 매 프레임 재설정되므로 Update 함수 내에서 이 함수를 호출해야 합니다. It will not return true until the user has released the mouse button and pressed it again. 버튼이 0이면 좌클릭, 1이면 우클릭, 2이면 중

docs.unity3d.com

Input.GetMouseButtonDown에 관한 자세한 사항은 위 링크에 있으나 간단하게 요약하면 아래와 같습니다.

키워드 반환 되는 값
Input.GetMouseButtonDown(0) 마우스를 좌클릭 했을 때, True값 반환
Input.GetMouseButtonDown(1) 마우스를 우클릭 했을 때, True값 반환
Input.GetMouseButtonDown(2) 마우스휠을 클릭 했을 때, True값 반환

*주의: 이 값은 매 프레임 재설정되므로 Update 메서드 내에서 이 함수를 호출해야 함.

따라서 자신의 취향에 맞춰서 원하는 값을 쓰시면 됩니다.

위 링크처럼 Unity에서 지원하는 메서드 등은 전용 사이트에 잘 설명이 되어 있습니다. 그러니 참고 바라겠습니다.


transform.Rotate(0, 0, this.rotSpeed);

이 키워드도 처음보는 키워드가 되겠군요. 딱 보면 클래스와 매서드로 보기 쉽상이지만 사실은 조금 다른 개념입니다.

transform이라는 키워드부터 보도록 하죠. 결론부터 말하면 transform은 클래스가 아닙니다. transform은 클래스가 아닌 컴포넌트라는 이름으로 불립니다.

또 요상한 개념이 등장해서 짜증날 수도 있겠지만 클래스와 컴포넌트는 명백히 차이를 가지고 있으므로 그 차이에 대해서 조금만 자세히 알아보도록 합시다.

클래스와 컴포넌트는 둘 다 메서드를 저장한다는 공통점이 있지만 메서드가 실행되는 과정에서 차이점이 생깁니다.

클래스는 위 그림과 같은 특징을 가집니다. 정확한 설명은 아니지만 지금은 이렇게 알아두도록 합시다.
위의 그림을 요약하면 이렇습니다.
[클래스의 요소가 다른 쪽으로 이동하게 되면 그쪽의 요소로 변경 됩니다.]

컴포넌트는 Unity에서 설정 자료라는 뜻으로 쓰이며 Unity에서는 GameObject라는 특정한 장소에 컴포넌트를 추가해서 기능을 늘릴 수 있습니다. GameObject는 쉽게 상자라고 생각하시면 편합니다.

이제 그림을 요약해 드리죠. 한 마디로
[컴포넌트의 요소는 다른 쪽으로 이동하게 되면 그쪽의 요소로 변경되지 않습니다.]
이전의 요소를 그대로 반영한다는 뜻이죠.

컴포넌트에 대한 설명을 이렇게 간단하게 해보았습니다만 실제로 컴포넌트와 클래스의 차이는 좀 더 다릅니다. 오늘 한 설명은 '저런식으로 되어있구나' 정도로 이해하시면 되겠습니다.

자, 이제 본론으로 돌아와서 Unity에서는 Transform이라는 컴포넌트를 제공합니다. 오디오를 담당하는 컴포넌트, 물리적 이동을 담당하는 컴포넌트가 있는 한편 Transform 컴포넌트는 오브젝트의 좌표과 회전, 이동을 담당합니다. 이 컴포넌트는 오늘 사용할 컴포넌트 이기도 하죠.

이제 이 Transform이라는 컴포넌트를 GameObject라는 상자에서 꺼내야 합니다. 꺼내는 방법은 다음과 같습니다.

GetComponent<|컴포넌트 이름|>(|인자|)

위 키워드를 통해 컴포넌트를 꺼낼 수 있으며 이번 경우엔 Transform을 |컴포넌트 이름| 자리에 집어 넣으면 되겠군요.
다만 이렇게 일일이 쓰는 것은 매우 불편합니다. 심지어 Transform 컴포넌트는 자주쓰는 컴포넌트이죠. 그래서 Unity에서는 더 편리한 방법을 제공합니다.
(|인자|는 이때까지 메서드와 클래스를 선언하거나 불러올 때도 얘기했던 변수를 얘기합니다. 이제부턴 편하게 |인자|라고 부르도록 하겠습니다.)

transform

앞의 대문자 T를 소문자 t로 바꾸기만 하면 이 키워드가 GameObject 상자 안에 있는 Transform 컴포넌트를 꺼낼 수 있습니다.
저희가 이번 시간 쓸 메서드는 Transform 컴포넌트 안에 있는 Rotate라는 메서드입니다. 그러니 이 메서드를 컴포넌트 안에서 꺼내서 써야합니다.

GetComponent<Transform>().Rotate(|인자|);

원래는 위와 같이 써야 Rotate 메서드를 불러올 수 있겠지만

transform.Rotate(|인자|);

Unity 덕에 이렇게나 쉽게 컴포넌트에서 메서드를 꺼낼 수 있게 되었습니다.


자 이제야 겨우 Rotate 메서드에 대해서 설명할 수 있겠습니다.

Rotate 메서드는 말 그대로 오브젝트를 회전시키는 메서드입니다. 그렇다면 인자는 어떤 값을 넣어줄까요? 바로 오브젝트를 회전시킬 축에 대해서 얼마나 회전시킬지에 대한 값을 넣어주면 됩니다.

그렇다면 Rotate 메서드에서 말하는 회전축이란 뭘까요? 잠시 벡터 얘기로 돌아가도록 하죠.

저번 시간에 Vector 클래스를 배울 때에 3D는 위와 같이 좌표가 구성되어 있다고 했습니다.
다만 위 그림에서 각 축에 대한 기준은 매우 일반적인 기준으로 실제로 축의 기준은 경우마다 다를 수 있습니다. Rotate 메서드에서는 축을 다음과 같이 정의합니다.

Rotate 메서드는 위 사진의 축을 기준으로 인자에 대입한 값의 속도로 오브젝트를 회전시킨다고 보면 됩니다.
이번 경우는 Z축을 축으로 this.rotSpeed만큼 즉, 클릭했을 때의 기준 10만큼의 속도로 오브젝트가 회전하겠군요.


6. 스크립트 적용 및 수정

자 이제, 스크립트를 적용시켜보고 게임을 실행해봅시다. 스크립트를 오브젝트에 적용시키는 방법은 1일차에서 설명했으나 다시 한 번 설명해드리겠습니다. 단순히 Assets 창의 스크립트 파일을 끌어다가 Hierarchy 창의 오브젝트에 올려놓으면 됩니다. 이번 경우 회전해야하는 오브젝트는 roulette 오브젝트이므로 roulette 오브젝트에 RouletteController C#스크립트를 적용하도록 하겠습니다.

C# 스크립트를 성공적으로 적용했다면 아래와 같이 될 것입니다.

그럼 한번 이제 실행시켜 봅시다.

실행해서 화면을 클릭해서 룰렛을 돌려보면 뭔가 잘못되었음을 알 수 있습니다.
맞습니다. 저희는 멈추는 스크립트를 만든 적이 없습니다...그러므로! C# 스크립트를 수정하도록 합시다.

public class RouletteController : MonoBehaviour
{
	float rotSpeed = 0; //회전 속도
    
    void Start()
    {
    
    }
    
    void Update()
    {
    	//클릭하면 회전 속도를 설정한다.
        if(Input.GetMouseButtonDown(0))
        {
        	this.rotSpeed = 10;
        }
        
        //회전 속도 만큼 룰렛을 회전 시킨다.
        transform.Rotate(0, 0, this.rotSpeed);
        
        // 룰렛을 감속시킨다.
        this.rotSpeed *= 0.999f;
    }
}

룰렛을 회전 시키는 키워드 밑에 룰렛을 감속시키는 연산을 추가했습니다.
원리는 고등학교 2학년에서 배우는 [함수의 극한]의 개념을 이용합니다.
간단히 얘기하면 원래의 속도에 0.999를 곱함으로써 원래 속도에서 0.001%를 빼는 식으로 속도를 줄이고 있습니다. 매우 작은 수이지만 이것이 굉장히 많이 반복되면 결국 0에 가까워져서 멈춘것으로 보이게 됩니다. 게다가 돌아가는 속도가 매우 조금씩 줄어들기 때문에 멈추는 과정이 매우 자연스럽게 이루어집니다.

아까보다 훨씬 자연스러워졌죠? 실질적으로는 룰렛은 계속 매우 미세하게 회전하고 있습니다. 그러나 저희 눈에는 그게 보이질 않죠. 그정도로 매우 미세합니다.

게임이라는 것은 결국 보이는게 전부입니다. 그러니 우리 게임 개발자들은 게이머를 속이는데 온갖 힘을 들여야합니다. 설령 그것이 정확한 수학적이고 과학적인 상황이 아니더라도 게이머가 정확하다고 받아들인다면 게임 개발자의 의도는 성공적으로 전해진 것입니다. 실제로 세계적으로 성공한 많은 게임들은 게임 속 많은 요소들이 플레이어를 속이고 있습니다.

이제 씬을 세이브 한 후 다음 과정으로 넘어갑시다.


7. 빌드하기

이제 게임을 만들었으니 이걸 파일화 시켜봅시다. 우리가 평소 자주하는 게임처럼 exe파일로 만들자는 소리입니다.
이 과정을 부르는 방법과 과정은 게임 엔진마다 다르지만 위에서도 설명했듯 적어도 Unity는 빌드라는 용어로 통일하고 있습니다.

자 이제 빌드 설정에 들어가서 다음 과정을 거치면 게임 파일이 완성됩니다.

이제 실행을 해보고 정상적으로 게임이 작동하는지만 확인하면 끝납니다.

세상에 이게 무슨 일입니까. 게임엔진으로 실행했을 때와는 완전히 다른 결과를 보여줄 뿐더러 부자연스럽기까지 합니다. 1분이 넘어가도록 룰렛이 돌아간다니요.

이처럼 게임 엔진에서의 게임이 빌드한 게임에도 그대로 반영되리라는 법은 없습니다. 그렇기 때문에 만들어진 게임을 다시 검수하는 과정이 만드시 필요한 것입니다.

저는 이후 0.999를 0.99로 고쳤습니다. 수정 후의 게임이 수정 전 게임보다 훨씬 자연스러웠습니다.


자 이렇게 해서 처음으로 엔진을 사용해 게임을 만들어 보았습니다. 어떤 느낌이 들으셨는지 모르겠군요. 의외로 쉬웠다고 하실 분들도 계실 것 같고 개념이 어렵다고 하실 분들도 계실 것 같습니다. 이럴 때일 수록 기본을 탄탄히 해야합니다. 저번 시간 강의들을 다시 보면서 기본을 익히시는 것도 좋겠죠.

오늘 강의는 여기까지 입니다. 질문있으면 댓글로 남여주세요.