2016년 8월 28일 일요일

[영어 연습] DirectX SDK 배포 변경 사항

DirectX 공부를 위해 책을 보면서 개발 환경을 구축하던 중, 책에 적힌 방식대로 DirectX SDK를 다운받아 설치하려고 하다가 공식 사이트에서 DirectX SDK가 없어졌길래 확인해 보니 Windows SDK로 통합되었다고 한다.

해당 내용은 https://msdn.microsoft.com/library/windows/desktop/ee663275.aspx 에서 확인할 수 있으며, 연습 겸 최상단에 이와 관련된 내용은 한국어로 번역을 해보았다.

[원문]
Where is the DirectX SDK?

Starting with Windows 8, the DirectX SDK is included as part of the Windows SDK.
We originally created the DirectX SDK as a high-performance platform for game development on top of Windows. As DirectX technologies matured, they became relevant to a broader range of applications. Today, the availability of Direct3D hardware in computers drives even traditional desktop applications to use graphics hardware acceleration. In parallel, DirectX technologies are more integrated with Windows. DirectX is now a fundamental part of Windows.

Because the Windows SDK is the primary developer SDK for Windows, DirectX is now included in it. You can now use the Windows SDK to build great games for Windows. To download the Windows 8 SDK, see Downloads for developing desktop apps.

[한국어]
DirectX SDK는 어디에 있나요?

윈도우 8부터는 DirectX SDK가 Windows SDK의 한 파트로 포함되었습니다.
우리는 원래 윈도위 상에서 게임 개발을 위한 고성능 플랫폼으로써 DirectX SDK를 만들었습니다. DirectX 기술이 발전하면서, 다양한 종류의 어플리케이션에 영향을 미치게 되었고, 오늘날 Direct 3D 하드웨어의 보급은 기존의 컴퓨터 어플리케이션마저 그래픽 하드웨어 가속을 사용하도록 만들어습니다. 동시에 DirectX 기술은 더욱 더 윈도우와 통합되었고, 이제 DirectX는 윈도우의 핵심적인 기술이 되었습니다.

Windows SDK는 윈도우에 있어 주요 개발자 SDK이기 때문에, DirectX는 이제 Windows SDK의 일부가 되었습니다. 여러분은 이제 윈도우에서 최고의 게임을 빌드하기 위해 Windows SDK를 사용할 수 있습니다. Windows 8 SDK를 다운 받기 위해서는, ‘Downloads for developing desktop apps’ 를 참고하시기 바랍니다.

2016년 8월 26일 금요일

02 - Road Runner Part 02 (Google VR SDK 활용)

이전 글에서 다루었던 내용이 유니티에서 오브젝트를 생성하고 Inspector를 다루는 데 초점을 맞추었다면, 이번에는 스크립트를 작성하고 Google VR SDK를 활용하는 데 초점을 맞추어 볼까 한다.

먼저 Part 01에서 작성했던 프로젝트를 불러오자.



현재 Scene을 살펴보면, 플레이어가 이동할 환경이 구성되어 있을 뿐, 막상 실제로 움직여야할 플레이어 캐릭터가 없는 것을 볼 수 있다.

또한 카메라가 부적합한 위치에 배치되어 있어 실행을 시켜보면 의도하지 않은 장면을 보여주고 있는 것 또한 확인할 수 있다. 

따라서 지금부터는 캐릭터를 Scene에 배치하고 카메라의 위치를 조정하여 실제로 길 위에 서 있는 것 같은 효과를 구현해볼 것이다. 

그럼 먼저, 길 위를 이동할 플레이어 캐릭터를 배치해보자. 이번 어플리케이션은 카드보드 1인칭 시점에서 머리 회전에 따른 카메라 이동이 어떻게 이루어지는 지를 체험해 보는 것이 목적이기 때문에, 캐릭터는 그냥 캡슐 오브젝트를 사용할 것이다.



그럼 첫번째로, [GameObject] -> [3D Object] -> [Capsule]를 선택하자.



생성이 되었다면, 위의 화면과 같이 캡슐 오브젝트가 생성된 것을 볼 수 있을 것이다.
하지만 현재 캡슐이 엉뚱한 위치에 생성되었기 때문에, 위치를 조정해줄 필요가 있다. 

또한, 길에 비해서 캡슐이 작기 때문에 현재 상태로 캡슐 상단에 카메라를 배치하면 길이 너무 넓어 보일 수 있기 때문에 캡슐의 크기도 조절해 줄 필요가 있다.



일단, 캡슐의 기즈모(캡슐 중심에 위치하고 있는 x,y,z축 화살표)를 사용하여 캡슐을 빨간색 길 위로 이동 시키자.  캡슐의 크기를 조정한 후에 다시 시작 위치를 잡아줄 것이기 때문에, 적당히 길 위로 옮겨도 충분하다.



캡슐을 옮겼다면, 이번에는 캡슐의 Scale을 조정하여 캡슐의 크기를 조정해보자.
캡슐을 선택한 후, Inspector에서 Transform의 Scale 값을 (2, 2, 2)로 조정하여 크기를 2배로 늘려준다.

이제 캡슐이 길의 넓이에 적당하게 커진 것을 볼 수 있다. 하지만 캡슐의 하단부가 길에 파 묻혀 있으므로 위로 올려줄 필요가 있다.



캡슐의 위치를 조정하는 김에, 캡슐이 길 중앙에 위치하도록 Position을 (0, 2.5, -8) 조정해주도록 하자.

이것으로 캡슐의 설정은 끝이 났다. 캡슐이 플레이어의 역활을 하므로 Capsule이라는 이름을 Player로 바꿔주도록 하자.



다음으로, 카메라의 위치를 조정해보도록 하겠다. 카메라는 플레이어의 얼굴 위치에 있어야 하기 때문에 'Main Camera'의 Position을 적색 길 시작 지점(0. 4. -7)으로 변경하여 캡슐 상단에 위치 시키자.

그리고 카메라는 플레이어의 이동에 따라 같이 움직여야만 한다. 

이를 구현하는 방법은 여러가지가 있겠지만, 여기서는 간단히 Main Camera를 Player의 하위 오브젝트로 포함시켜서 플레이어의 이동에 따라 함께 이동하도록 할 것이다.



따라서 Road_XXX 오브젝트들을 Road 오브젝트 하위에 포함시킬 때와 마찬가지로, Main Camea 오브젝트를 Player 오브젝트를 Drag&Drop 하여 Player 하위에 포함시키자. 

Main Camera가 Player 하위로 옯겨지면, Position이 Player의 상대적인 좌표로 변경되는 것을 확인할 수 있다.

이제 길을 따라 이동할 플레이어의 설정이 끝났다.

지금부터는 플레이어 캐릭터가 길을 따라 이동하는 것을 구현해볼 것이다.
캐릭터의 이동은 플레이어가 조작하는 것이 아니라 정해진 패스를 따라가도록 Navigation 기능을 활용할 것이다.



Navigation을 활용할 경우, 도착할 목적지를 지정해 줄 필요가 있다. 

따라서 [GameObject] -> [Create Empty]를 선택하여 빈 오브젝트를 생성한 후, 이름을 Destination으로 변경해주자.

그리고 Destination 오브젝트의 Position을 노란색 길 끝부분(-20, 0.5, -12.5)로 변경하여 길 끝에 위치하게 한다.

위에서도 언급하였지만 길 끝에 배치된 Destination은 Navigation 기능을 활용하여 플레이어를 자동으로 이동시킬 때 목적지로 사용되게 된다.





이제 캐릭터의 이동을 제어하는 스크립트를 작성할 차례가 왔다.

Unity에서는 스크립트를 작성한 후, 다른 Component와 마찬가지로 스크립트를 오브젝트에 붙여 원하는 기능을 수행하도록 할 수 있다.

Project 창에서 "Scripts"라는 폴더를 생성한 뒤 폴더 안에서 [마우스 우클릭] -> [Create] -> [C# Script]를 선택하여 스크립트를 생성하자.

스크립트를 생성하면 바로 이름을 지정할 수 있는데 여기서 지정한 파일 명이 스크립트의 클래스 명이 되고, 이후에 스크립트 파일명을 변경하더라도 내부 클래스 파일 명은 자동으로 변경이 되지 않기 때문에 처음에 파일명을 작성할 때 실제 사용할 클래스 명을 부여해야 한다는 점을 기억하자.



이 스크립트는 플레이어 캐릭터를 이동하는데 사용할 것이기 때문에 파일 명을 'Player'로 작성한 후, 더블 클릭하여 편집기를 실행시킨다



편집기를 실행하면(Visual Studio가 설치되어 있으면 자동으로 연동이 된다) 플레이어가 선언한 클래스와 Unity의 기본 함수인 Start()와 Update()가 선언되어 있는 것을 볼 수 있다.

여기서 잠깐 Class를 살펴보면 우리가 생성한 클래스가 MonoBehavior 클래스를 상속하고 있는 것을 볼 수 있다.

MonoBehavior는 Unity에서 Component로 사용되는 모든 요소가 상속받아야 하는 클래스로써, Component들에게 공통적인 속성과 기능을 부여한다. 

만약 스크립트가 MonoBehavior를 상속하지 않는 경우, 해당 스크립트는 오브젝트에 Component로 붙일 수 없다는 점을 짚고 넘어가자. 

Start() 함수는 스크립트가 부착된 오브젝트가 Scene에 존재하는 경우, 첫 프레임이 업데이트 되기 직전에 실행되는 함수이다. 

일반적으로 Start 함수를 호출하여 각종 변수에 초기 값을 부여하거나, 오브젝트의 존재하는 다른 Component의 정보를 가져온다.

Update 함수는 매 프레임마다 호출되며, 오브젝트의 동작을 제어하는데 활용된다.

이제 Player 클래스를 다음과 같이 작성하자.

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour {

    //플레이어 캐릭터가 도착할 목적지
    public GameObject DEST;

    NavMeshAgent _agent;

 // Use this for initialization
 void Start () {
        //오브젝트에 포함된 Navigation Component를 가져온다
        _agent = GetComponent();

        //Navigation의 목적지 지정
        _agent.destination = DEST.transform.position;

        //Navigation으로 이동 시 속도 지정
        _agent.speed = 10f;
 }
 
 // Update is called once per frame
 void Update () {
 
 }
}
맨 처음에 선언한 DEST 변수는 public으로 선언되었기 때문에 에디터 상의 Inspector에서 DEST의 타입과 일치하는 오브젝트를 직접 지정할 수 있게 된다.

NavMeshAgent는 Unity가 제공하는 네비게이션 클래스로써, 자신이 속한 오브젝트가 네비게이션 경로를 따라 이동할 수 있는 다양한 기능을 제공한다.

Start 함수를 살펴보면, GetComponent<NavMeshAgent>() 함수 값을 _agent 변수에 할당하는 것을 볼 수 있으며, 이것은 오브젝트가 가지고 있는 NavMeshAgent 컴포넌트를 _agent에 할당하라는 의미이다.

2번째 코드에서는 _agent.destination에 DEST.transform.position을 할당하고 있으며, 네비게이션의 목적지를 목적이 오브젝트인 DEST의 위치로 설정하는 코드이다.

마지막 코드는 speed라는 변수명에서도 알 수 있듯이 네비게이션으로 이동할 시에 오브젝트의 속도를 10f로 지정하라는 의미를 갖는다.

NavMeshAgent 클래스를 사용하면, 오브젝트를 정해진 경로를 따라 정해진 속도로 정해진 목적지로 이동시킬 수 있기 때문에 복잡한 코드를 필요로 하진 않는다는 장점이 있다.



이제 스크립트를 작성했으니, 작성한 스크립트를 플레이어 캐릭터에 적용시킬 차례가 왔다.
스크립트의 적용은 Material을 추가할 때와 마찬가지로, 먼저 Player 오브젝트를 선택하여 Inspector를 활성화시킨 다음, 스크립트 파일을 Drag&Drop 하면 된다.

정상적으로 스크립트가 적용이 되었다면, Inspector 창에 Player라는 스크립트가 추가된 것을 확인할 수 있을 것이다.

그리고 스크립트 항목을 살펴보면 우리가 클래스 내부에서 public으로 선언하였던 GameObject 타입의 DEST 변수가 존재하는 것을 확인할 수 있다.

현재는 None이라고 표시되며 어떤 오브젝트도 지정되지 않았다는 것을 알려주고 있지만, DEST 변수 우편에 원 버튼, 혹은 Hierarchy 창에 존재하는 오브젝트 혹은 Project 창에 존재하는 Prefab을 Drag&Drop 하여 DEST에 해당하는 오브젝트를 지정해 줄 수 있다.



여기서는 Destination 오브젝트가 네비게이션의 목적지가 되어야 하기 때문에, Hierarchy 창에 있는 Destination 오브젝트를 끌어다가 DEST 변수 위에 올려 놓도록 하자.



Destination 오브젝트를 할당하고 나면, None이었던 항목이 Destination으로 변경된 것을 확인 할 수 있을 것이다.

이제 캐릭터가 가야할 목적지를 지정하는 일까지 마무리 지었다. 하지만 현재 Player의 Inspector를 살펴보면, Player 스크립트 내부의 _agent 클래스 변수가 가져와야 할 NavMeshAgent 컴포넌트가 없는 것을 확인 할 수 있다.



당연히 그 이유는 아직 우리가 Player에 해당 컴포넌트를 추가해주지 않았기 때문이므로, Player 오브젝트를 선택한 상황에서 [Component] -> [Navigation] -> [Nav Mesh Agent]를 선택하여 NavMeshAgent 컴포넌트를 추가해주도록 하자.

컴포넌트가 추가되면, Inspector 창에 Nav Mesh Agent가 추가되며, 그 안에는 Agent의 크기 지정, 이동 속도, 각속도 및 가속도 등을 설정할 수 있는 것을 확인 할 수 있다.

현재로써는 딱히 수정할 값이 없으므로 다양한 값들을 조정할 수 있다는 사실만 짚고 넘어가자.


이제 어플리케이션을 다 작성한 것 같으므로 중앙 상단에 있는 플레이 버튼을 눌러 어플리케이션을 실행시켜 보자.


실행을 시켜보면 캡슐 상단에서 정면을 향해 바라보고 있는 화면이 출력되는 것을 확인할 수 있을 것이다.

하지만 한 가지 문제가 있는데 바로 캐릭터가 이동을 하지 않는다는 점이다
(움직이고 있다고 생각한다면 그냥 느낌 탓이다...)

왜 움직이지 않는가 생각을 해보면 지금까지 우리는 이동할 플레이어를 배치하고, 자동으로 이동할 수 있도록 스크립트를 작성하여 이동 속도와 목적지를 설정해 주었다.

그런데 여기서 한 가지, Navigation은 정해진 경로를 따라 자동으로 목적지까지 간다고 하였는데 어떤 경로를 따라 갈 것인지 지정을 해주지 않았다.

그렇기 때문에 현재 캐릭터가 이동을 하지 못하고 있는 것이다.

이제 플레이를 종료하고, Navigation이 이동할 경로를 지정해보도록 하자.



먼저 Hierarchy 창에서 플레이어의 이동 경로가 될 Road를 선택하자.

Road의 Inspector 창에서 이름 우측을 보면, Static이라는 이름과 함께 버튼과 화살표가 있는것을 확인할 수 있다.



여기에서 오른쪽에 있는 화살표를 클릭해보면, 다양한 종류의 Static 조건들이 있으며, 현재는 None으로 설정되 있는 것을 확인할 수 있다.

길을 Navigation의 경로로 사용하기 위해서는 Navigation 옵션을 통해 경로를 생성(Bake)해 주어야 하는데 경로를 생성할 오브젝트는 반드시 Navigation Static 옵션을 지녀야 한다.

따라서 모든 Road_XXX 오브젝트를 포함하고 있는 Road의 Static 속성을 None -> Navigation Static으로 변경하자.

변경을 하면 하위 오브젝트까지 속성을 변경할 것이냐고 물어보는 메세지 창이 뜨게 되는데, Yes를 눌러 하위의 모든 오브젝트 또한 Navigation Static 속성을 가지게 한다.

다음으로, [Window] -> [Navigation]을 선택하면, Object, Bake, Areas 3개의 항목을 가지고 있는 네비게이션 창이 나타나는 것을 볼 수 있다.



여기서 우리가 관심을 가져야 할 부분은 Bake 항목이며, Bake 항목에서는 길을 생성할 때 r길을 이동할 오브젝트(Agent)의 반경, 높이, 최대 이동 가는 각도 등을 조절하여 그에 적합한 경로를 생성할 수 있게 해준다.

이번에는 기본 값을 수정할 필요가 없기 때문에, Navigation 창 우측 하단에 있는 Bake 버튼을 눌러 경로를 생성하면 된다.



Bake 버튼을 클릭하면, Scene에 있는 Road 오브젝트에 반투명한 길이 생성된 것을 볼 수 있다.

이 반투명한 길이 바로 플레이어 캐릭터가 자동으로 이동하게 될 경로가 된다. 네비게이션 경로의 경우, 목적지와는 달리 따로 지정을 해주지 않아도 네비게이션 컴포넌트가 자동으로 네비게이션 경로를 따라 이동을 하게된다.

그럼 다시 한번 어플리케이션을 실행시켜 보자.


위의 영상을 보면 방금 전과는 달리 캐릭터가 길을 따라 이동하는 것을 볼 수 있다.

이제 어플리케이션에서 Cardboard에 적합한 카메라를 처리하는 일만 남았다.

우리는 맨 처음 Google VR SDK를 프로젝트에 추가하였다. 이 때 Assets 폴더에 2개의 폴더가 추가되었는데 그 중 하나가 바로 GoogleVR 폴더다.

Project 창에서 GoogleVR 폴더를 찾아 들어가보자. 폴더 안을 살펴보면 Prefabs 폴더가 있으며, Prefabs 폴더 안에는 GvrViewerMain이라는 Prefab이 포함되어 있는 것을 볼 수 있다.

이 GvrViewerMain 프리팹이 바로 Google VR SDK에서 제공하는 VR 카메라 구현 알고리즘이다.

GvrViewerMain을 끌어다가 Hierarchy에 놓고, 다시 한번 어플리케이션을 실행시켜보자.


아까와는 달리 카메라가 반으로 나뉘어져 좌안과 우안으로 분리되는 것을 확인할 수 있을 것이다.

참고로 Unity 상에서 어플리케이션을 실행시키면 마우스로 Game 화면을 회전할 수 없는데, Alt 키를 누른 상태에서 마우스를 회전시키면 마우스의 이동에 따라 화면이 회전하니 참고하도록 하자.

영상을 보면 좌우 회전이 커지면 양쪽 가장자리로 희색 물체가 나타나는 것을 볼 수 있는데 이는 카메라가 회전하면서 카메라 각도에 캡슐이 들어오면서 생기는 현상이다.
이는 카메라의 z값을 수정하거나 회전 범위에 제약을 둠으로써 해결할 수 있으며, 회전 범위 값을 제한하는 방법에 대해서는 나중에 다시 한번 다뤄볼까 한다.

마지막으로 실제 폰에서 어플리케이션을 실행시키고 폰을 카드보드에 넣고 머리에 착용하다 보면 이미 캐릭터가 목적지에 도착하여 이동하는 것을 볼 수 없는 문제가 생길 수도 있다.

그렇기 때문에 목적지에 도착하면 다시 처음부터 시작하도록 아래와 같이 Player Script의 Update 함수를 수정하자.

void Update () {
        //네비게이션이 가야할 거리가 0보다 작은 경우
        //즉 목적지에 도착한 경우를 나타낸다
        if(_agent.remainingDistance <= 0)
        {
            //Scene의 처음 상태로 되돌아간다
            Application.LoadLevel(Application.loadedLevel);
        } 
 }
위와 같이 Update 함수를 수정한 다음 다시 실행을 시켜보면 목적지에 도착했을 때 다시 처음부터 반복하는 것을 확인할 수 있을 것이다.

이제 어플리케이션을 완성했으니 Apk 파일을 만들어 직접 실행해보자

























[File] -> [Build Settings...]에 들어가면 다음과 같은 창이 뜰 것이다.

위에 있는 창은 실제 실행 파일에 포함할 Scene을 표시한다.

현재는 포함되어 있는 Scene이 없으며, 우리가 작성한 Scene 포함되어야 하므로 'Add Open Scenes'를 클릭하여 현재 Scene을 추가하자.

그리고 왼쪽 하단을 보면 다양한 플랫폼을 선택할 수 있는 Platform 창이 있는 것을 볼 수 있다.

여기서는 안드로이드 어플리케이션을 빌드하기 위해서 Android를 선택한 상태인 것을 볼 수 있다.

만약 다른 플랫폼이 선택되어 있다면, 안드로이드를 선택한 후, 'Switch Platform'을 선택하여 플랫폼을 변경하도록 하자.

참고로, 안드로이드 플랫폼 빌드 환경을 구성하는 방법은
http://jhpstudio.blogspot.kr/2016/08/unity-android-android-sdk.html
에 따로 설명을 해 놓았다

설정을 마쳤다면 'Build' 버튼을 눌러 파일을 생성하자.


 아마 위 화면과 같이 프로세스가 진행된 후에, 완성된 파일이 포함된 폴더가 팝업 될 것이다.















만약 Build가 처음인 경우, 다음과 같은 오류가 발생할 수 있다.



이것은 Android APK 파일의 Bundle Identifier가 잘못 설정된 문제로써, 이를 해결하기 위해서는 [Edit] -> [Project Settings] -> [Player]로 이동한 후, Settings for Android 항목에 있는 [Other Settings] -> [Identification] 항목에 위치한 Bundle Identifier의 이름을 바꾸어주어야 한다.

이름의 양식은 com.[Identifier 1(Company)].[Identifier 2(ProductName)] 을 지켜야 한다.
(본인의 경우 com.vrstudy.roadrunner 로 지정하였다)

또한 상단의 Product Name은 실제 어플리케이션의 이름이 되고, Company Name은 제작사의 이름이 되기 때문에 적절하게 변경해주도록 하자

생성된 파일은 APK 파일이기 때문에 직접 폰으로 복사한 뒤 폰에서 APK 파일을 실행시켜 설치를 진행하면 된다.



위의 화면은 실제 폰에서 실행된 어플리케이션의 화면을 캡쳐한 것으로, Unity에서 테스트했을 때와 똑같이 동작하는 것을 확인할 수 있다.

이로써, 첫번째 Cardboard 어플리케이션을 만들어보았다. 사실 대부분의 내용은 Unity의 기초적인 내용이었으며, Google VR SDK도 만들어진 Prefab을 사용했기 때문에 이게 정말 VR 어플리케이션 제작인지 의심스러운 면도 있을 것이다.

실제로 요 몇달 동안 카드보드를 다뤄보면서 느꼈던 점은, Cardboard를 활용할 경우 더욱 더 많은 신경을 쏟게 되는 부분이 카메라 제어보다 게임적인 요소, 그리고 사용자가 UI와 상호작용하는 방식이었다는 점이다.

그런 의미에서 다음 번에는 입력 장치가 없는 카드보드에서 어떻게 UI를 배치하고 상호작용할 것인지에 대해서 올려보고자 한다.


02 - Road Runner Part 02 (Google VR SDK 활용)

이전 글에서 다루었던 내용이 유니티에서 오브젝트를 생성하고 Inspector를 다루는 데 초점을 맞추었다면, 이번에는 스크립트를 작성하고 Google VR SDK를 활용하는 데 초점을 맞추어 볼까 한다.

먼저 Part 01에서 작성했던 프로젝트를 불러오자.



현재 Scene을 살펴보면, 플레이어가 이동할 환경이 구성되어 있을 뿐, 막상 실제로 움직여야할 플레이어 캐릭터가 없는 것을 볼 수 있다.

또한 카메라가 부적합한 위치에 배치되어 있어 실행을 시켜보면 의도하지 않은 장면을 보여주고 있는 것 또한 확인할 수 있다. 

따라서 지금부터는 캐릭터를 Scene에 배치하고 카메라의 위치를 조정하여 실제로 길 위에 서 있는 것 같은 효과를 구현해볼 것이다. 

그럼 먼저, 길 위를 이동할 플레이어 캐릭터를 배치해보자. 이번 어플리케이션은 카드보드 1인칭 시점에서 머리 회전에 따른 카메라 이동이 어떻게 이루어지는 지를 체험해 보는 것이 목적이기 때문에, 캐릭터는 그냥 캡슐 오브젝트를 사용할 것이다.



그럼 첫번째로, [GameObject] -> [3D Object] -> [Capsule]를 선택하자.



생성이 되었다면, 위의 화면과 같이 캡슐 오브젝트가 생성된 것을 볼 수 있을 것이다.
하지만 현재 캡슐이 엉뚱한 위치에 생성되었기 때문에, 위치를 조정해줄 필요가 있다. 

또한, 길에 비해서 캡슐이 작기 때문에 현재 상태로 캡슐 상단에 카메라를 배치하면 길이 너무 넓어 보일 수 있기 때문에 캡슐의 크기도 조절해 줄 필요가 있다.



일단, 캡슐의 기즈모(캡슐 중심에 위치하고 있는 x,y,z축 화살표)를 사용하여 캡슐을 빨간색 길 위로 이동 시키자.  캡슐의 크기를 조정한 후에 다시 시작 위치를 잡아줄 것이기 때문에, 적당히 길 위로 옮겨도 충분하다.



캡슐을 옮겼다면, 이번에는 캡슐의 Scale을 조정하여 캡슐의 크기를 조정해보자.
캡슐을 선택한 후, Inspector에서 Transform의 Scale 값을 (2, 2, 2)로 조정하여 크기를 2배로 늘려준다.

이제 캡슐이 길의 넓이에 적당하게 커진 것을 볼 수 있다. 하지만 캡슐의 하단부가 길에 파 묻혀 있으므로 위로 올려줄 필요가 있다.



캡슐의 위치를 조정하는 김에, 캡슐이 길 중앙에 위치하도록 Position을 (0, 2.5, -8) 조정해주도록 하자.

이것으로 캡슐의 설정은 끝이 났다. 캡슐이 플레이어의 역활을 하므로 Capsule이라는 이름을 Player로 바꿔주도록 하자.



다음으로, 카메라의 위치를 조정해보도록 하겠다. 카메라는 플레이어의 얼굴 위치에 있어야 하기 때문에 'Main Camera'의 Position을 적색 길 시작 지점(0. 4. -7)으로 변경하여 캡슐 상단에 위치 시키자.

그리고 카메라는 플레이어의 이동에 따라 같이 움직여야만 한다. 

이를 구현하는 방법은 여러가지가 있겠지만, 여기서는 간단히 Main Camera를 Player의 하위 오브젝트로 포함시켜서 플레이어의 이동에 따라 함께 이동하도록 할 것이다.



따라서 Road_XXX 오브젝트들을 Road 오브젝트 하위에 포함시킬 때와 마찬가지로, Main Camea 오브젝트를 Player 오브젝트를 Drag&Drop 하여 Player 하위에 포함시키자. 

Main Camera가 Player 하위로 옯겨지면, Position이 Player의 상대적인 좌표로 변경되는 것을 확인할 수 있다.

이제 길을 따라 이동할 플레이어의 설정이 끝났다.

지금부터는 플레이어 캐릭터가 길을 따라 이동하는 것을 구현해볼 것이다.
캐릭터의 이동은 플레이어가 조작하는 것이 아니라 정해진 패스를 따라가도록 Navigation 기능을 활용할 것이다.



Navigation을 활용할 경우, 도착할 목적지를 지정해 줄 필요가 있다. 

따라서 [GameObject] -> [Create Empty]를 선택하여 빈 오브젝트를 생성한 후, 이름을 Destination으로 변경해주자.

그리고 Destination 오브젝트의 Position을 노란색 길 끝부분(-20, 0.5, -12.5)로 변경하여 길 끝에 위치하게 한다.

위에서도 언급하였지만 길 끝에 배치된 Destination은 Navigation 기능을 활용하여 플레이어를 자동으로 이동시킬 때 목적지로 사용되게 된다.





이제 캐릭터의 이동을 제어하는 스크립트를 작성할 차례가 왔다.

Unity에서는 스크립트를 작성한 후, 다른 Component와 마찬가지로 스크립트를 오브젝트에 붙여 원하는 기능을 수행하도록 할 수 있다.

Project 창에서 "Scripts"라는 폴더를 생성한 뒤 폴더 안에서 [마우스 우클릭] -> [Create] -> [C# Script]를 선택하여 스크립트를 생성하자.

스크립트를 생성하면 바로 이름을 지정할 수 있는데 여기서 지정한 파일 명이 스크립트의 클래스 명이 되고, 이후에 스크립트 파일명을 변경하더라도 내부 클래스 파일 명은 자동으로 변경이 되지 않기 때문에 처음에 파일명을 작성할 때 실제 사용할 클래스 명을 부여해야 한다는 점을 기억하자.



이 스크립트는 플레이어 캐릭터를 이동하는데 사용할 것이기 때문에 파일 명을 'Player'로 작성한 후, 더블 클릭하여 편집기를 실행시킨다



편집기를 실행하면(Visual Studio가 설치되어 있으면 자동으로 연동이 된다) 플레이어가 선언한 클래스와 Unity의 기본 함수인 Start()와 Update()가 선언되어 있는 것을 볼 수 있다.

여기서 잠깐 Class를 살펴보면 우리가 생성한 클래스가 MonoBehavior 클래스를 상속하고 있는 것을 볼 수 있다.

MonoBehavior는 Unity에서 Component로 사용되는 모든 요소가 상속받아야 하는 클래스로써, Component들에게 공통적인 속성과 기능을 부여한다. 

만약 스크립트가 MonoBehavior를 상속하지 않는 경우, 해당 스크립트는 오브젝트에 Component로 붙일 수 없다는 점을 짚고 넘어가자. 

Start() 함수는 스크립트가 부착된 오브젝트가 Scene에 존재하는 경우, 첫 프레임이 업데이트 되기 직전에 실행되는 함수이다. 

일반적으로 Start 함수를 호출하여 각종 변수에 초기 값을 부여하거나, 오브젝트의 존재하는 다른 Component의 정보를 가져온다.

Update 함수는 매 프레임마다 호출되며, 오브젝트의 동작을 제어하는데 활용된다.

이제 Player 클래스를 다음과 같이 작성하자.

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour {

    //플레이어 캐릭터가 도착할 목적지
    public GameObject DEST;

    NavMeshAgent _agent;

 // Use this for initialization
 void Start () {
        //오브젝트에 포함된 Navigation Component를 가져온다
        _agent = GetComponent();

        //Navigation의 목적지 지정
        _agent.destination = DEST.transform.position;

        //Navigation으로 이동 시 속도 지정
        _agent.speed = 10f;
 }
 
 // Update is called once per frame
 void Update () {
 
 }
}
맨 처음에 선언한 DEST 변수는 public으로 선언되었기 때문에 에디터 상의 Inspector에서 DEST의 타입과 일치하는 오브젝트를 직접 지정할 수 있게 된다.

NavMeshAgent는 Unity가 제공하는 네비게이션 클래스로써, 자신이 속한 오브젝트가 네비게이션 경로를 따라 이동할 수 있는 다양한 기능을 제공한다.

Start 함수를 살펴보면, GetComponent<NavMeshAgent>() 함수 값을 _agent 변수에 할당하는 것을 볼 수 있으며, 이것은 오브젝트가 가지고 있는 NavMeshAgent 컴포넌트를 _agent에 할당하라는 의미이다.

2번째 코드에서는 _agent.destination에 DEST.transform.position을 할당하고 있으며, 네비게이션의 목적지를 목적이 오브젝트인 DEST의 위치로 설정하는 코드이다.

마지막 코드는 speed라는 변수명에서도 알 수 있듯이 네비게이션으로 이동할 시에 오브젝트의 속도를 10f로 지정하라는 의미를 갖는다.

NavMeshAgent 클래스를 사용하면, 오브젝트를 정해진 경로를 따라 정해진 속도로 정해진 목적지로 이동시킬 수 있기 때문에 복잡한 코드를 필요로 하진 않는다는 장점이 있다.



이제 스크립트를 작성했으니, 작성한 스크립트를 플레이어 캐릭터에 적용시킬 차례가 왔다.
스크립트의 적용은 Material을 추가할 때와 마찬가지로, 먼저 Player 오브젝트를 선택하여 Inspector를 활성화시킨 다음, 스크립트 파일을 Drag&Drop 하면 된다.

정상적으로 스크립트가 적용이 되었다면, Inspector 창에 Player라는 스크립트가 추가된 것을 확인할 수 있을 것이다.

그리고 스크립트 항목을 살펴보면 우리가 클래스 내부에서 public으로 선언하였던 GameObject 타입의 DEST 변수가 존재하는 것을 확인할 수 있다.

현재는 None이라고 표시되며 어떤 오브젝트도 지정되지 않았다는 것을 알려주고 있지만, DEST 변수 우편에 원 버튼, 혹은 Hierarchy 창에 존재하는 오브젝트 혹은 Project 창에 존재하는 Prefab을 Drag&Drop 하여 DEST에 해당하는 오브젝트를 지정해 줄 수 있다.



여기서는 Destination 오브젝트가 네비게이션의 목적지가 되어야 하기 때문에, Hierarchy 창에 있는 Destination 오브젝트를 끌어다가 DEST 변수 위에 올려 놓도록 하자.



Destination 오브젝트를 할당하고 나면, None이었던 항목이 Destination으로 변경된 것을 확인 할 수 있을 것이다.

이제 캐릭터가 가야할 목적지를 지정하는 일까지 마무리 지었다. 하지만 현재 Player의 Inspector를 살펴보면, Player 스크립트 내부의 _agent 클래스 변수가 가져와야 할 NavMeshAgent 컴포넌트가 없는 것을 확인 할 수 있다.



당연히 그 이유는 아직 우리가 Player에 해당 컴포넌트를 추가해주지 않았기 때문이므로, Player 오브젝트를 선택한 상황에서 [Component] -> [Navigation] -> [Nav Mesh Agent]를 선택하여 NavMeshAgent 컴포넌트를 추가해주도록 하자.

컴포넌트가 추가되면, Inspector 창에 Nav Mesh Agent가 추가되며, 그 안에는 Agent의 크기 지정, 이동 속도, 각속도 및 가속도 등을 설정할 수 있는 것을 확인 할 수 있다.

현재로써는 딱히 수정할 값이 없으므로 다양한 값들을 조정할 수 있다는 사실만 짚고 넘어가자.


이제 어플리케이션을 다 작성한 것 같으므로 중앙 상단에 있는 플레이 버튼을 눌러 어플리케이션을 실행시켜 보자.


실행을 시켜보면 캡슐 상단에서 정면을 향해 바라보고 있는 화면이 출력되는 것을 확인할 수 있을 것이다.

하지만 한 가지 문제가 있는데 바로 캐릭터가 이동을 하지 않는다는 점이다
(움직이고 있다고 생각한다면 그냥 느낌 탓이다...)

왜 움직이지 않는가 생각을 해보면 지금까지 우리는 이동할 플레이어를 배치하고, 자동으로 이동할 수 있도록 스크립트를 작성하여 이동 속도와 목적지를 설정해 주었다.

그런데 여기서 한 가지, Navigation은 정해진 경로를 따라 자동으로 목적지까지 간다고 하였는데 어떤 경로를 따라 갈 것인지 지정을 해주지 않았다.

그렇기 때문에 현재 캐릭터가 이동을 하지 못하고 있는 것이다.

이제 플레이를 종료하고, Navigation이 이동할 경로를 지정해보도록 하자.



먼저 Hierarchy 창에서 플레이어의 이동 경로가 될 Road를 선택하자.

Road의 Inspector 창에서 이름 우측을 보면, Static이라는 이름과 함께 버튼과 화살표가 있는것을 확인할 수 있다.



여기에서 오른쪽에 있는 화살표를 클릭해보면, 다양한 종류의 Static 조건들이 있으며, 현재는 None으로 설정되 있는 것을 확인할 수 있다.

길을 Navigation의 경로로 사용하기 위해서는 Navigation 옵션을 통해 경로를 생성(Bake)해 주어야 하는데 경로를 생성할 오브젝트는 반드시 Navigation Static 옵션을 지녀야 한다.

따라서 모든 Road_XXX 오브젝트를 포함하고 있는 Road의 Static 속성을 None -> Navigation Static으로 변경하자.

변경을 하면 하위 오브젝트까지 속성을 변경할 것이냐고 물어보는 메세지 창이 뜨게 되는데, Yes를 눌러 하위의 모든 오브젝트 또한 Navigation Static 속성을 가지게 한다.

다음으로, [Window] -> [Navigation]을 선택하면, Object, Bake, Areas 3개의 항목을 가지고 있는 네비게이션 창이 나타나는 것을 볼 수 있다.



여기서 우리가 관심을 가져야 할 부분은 Bake 항목이며, Bake 항목에서는 길을 생성할 때 r길을 이동할 오브젝트(Agent)의 반경, 높이, 최대 이동 가는 각도 등을 조절하여 그에 적합한 경로를 생성할 수 있게 해준다.

이번에는 기본 값을 수정할 필요가 없기 때문에, Navigation 창 우측 하단에 있는 Bake 버튼을 눌러 경로를 생성하면 된다.



Bake 버튼을 클릭하면, Scene에 있는 Road 오브젝트에 반투명한 길이 생성된 것을 볼 수 있다.

이 반투명한 길이 바로 플레이어 캐릭터가 자동으로 이동하게 될 경로가 된다. 네비게이션 경로의 경우, 목적지와는 달리 따로 지정을 해주지 않아도 네비게이션 컴포넌트가 자동으로 네비게이션 경로를 따라 이동을 하게된다.

그럼 다시 한번 어플리케이션을 실행시켜 보자.


위의 영상을 보면 방금 전과는 달리 캐릭터가 길을 따라 이동하는 것을 볼 수 있다.

이제 어플리케이션에서 Cardboard에 적합한 카메라를 처리하는 일만 남았다.

우리는 맨 처음 Google VR SDK를 프로젝트에 추가하였다. 이 때 Assets 폴더에 2개의 폴더가 추가되었는데 그 중 하나가 바로 GoogleVR 폴더다.

Project 창에서 GoogleVR 폴더를 찾아 들어가보자. 폴더 안을 살펴보면 Prefabs 폴더가 있으며, Prefabs 폴더 안에는 GvrViewerMain이라는 Prefab이 포함되어 있는 것을 볼 수 있다.

이 GvrViewerMain 프리팹이 바로 Google VR SDK에서 제공하는 VR 카메라 구현 알고리즘이다.

GvrViewerMain을 끌어다가 Hierarchy에 놓고, 다시 한번 어플리케이션을 실행시켜보자.


아까와는 달리 카메라가 반으로 나뉘어져 좌안과 우안으로 분리되는 것을 확인할 수 있을 것이다.

참고로 Unity 상에서 어플리케이션을 실행시키면 마우스로 Game 화면을 회전할 수 없는데, Alt 키를 누른 상태에서 마우스를 회전시키면 마우스의 이동에 따라 화면이 회전하니 참고하도록 하자.

영상을 보면 좌우 회전이 커지면 양쪽 가장자리로 희색 물체가 나타나는 것을 볼 수 있는데 이는 카메라가 회전하면서 카메라 각도에 캡슐이 들어오면서 생기는 현상이다.
이는 카메라의 z값을 수정하거나 회전 범위에 제약을 둠으로써 해결할 수 있으며, 회전 범위 값을 제한하는 방법에 대해서는 나중에 다시 한번 다뤄볼까 한다.

마지막으로 실제 폰에서 어플리케이션을 실행시키고 폰을 카드보드에 넣고 머리에 착용하다 보면 이미 캐릭터가 목적지에 도착하여 이동하는 것을 볼 수 없는 문제가 생길 수도 있다.

그렇기 때문에 목적지에 도착하면 다시 처음부터 시작하도록 아래와 같이 Player Script의 Update 함수를 수정하자.

void Update () {
        //네비게이션이 가야할 거리가 0보다 작은 경우
        //즉 목적지에 도착한 경우를 나타낸다
        if(_agent.remainingDistance <= 0)
        {
            //Scene의 처음 상태로 되돌아간다
            Application.LoadLevel(Application.loadedLevel);
        } 
 }
위와 같이 Update 함수를 수정한 다음 다시 실행을 시켜보면 목적지에 도착했을 때 다시 처음부터 반복하는 것을 확인할 수 있을 것이다.

이제 어플리케이션을 완성했으니 Apk 파일을 만들어 직접 실행해보자






















[File] -> [Build Settings...]에 들어가면 다음과 같은 창이 뜰 것이다.

위에 있는 창은 실제 실행 파일에 포함할 Scene을 표시한다.

현재는 포함되어 있는 Scene이 없으며, 우리가 작성한 Scene 포함되어야 하므로 'Add Open Scenes'를 클릭하여 현재 Scene을 추가하자.

그리고 왼쪽 하단을 보면 다양한 플랫폼을 선택할 수 있는 Platform 창이 있는 것을 볼 수 있다.

여기서는 안드로이드 어플리케이션을 빌드하기 위해서 Android를 선택한 상태인 것을 볼 수 있다.

만약 다른 플랫폼이 선택되어 있다면, 안드로이드를 선택한 후, 'Switch Platform'을 선택하여 플랫폼을 변경하도록 하자.

참고로, 안드로이드 플랫폼 빌드 환경을 구성하는 방법은
http://jhpstudio.blogspot.kr/2016/08/unity-android-android-sdk.html
에 따로 설명을 해 놓았다

설정을 마쳤다면 'Build' 버튼을 눌러 파일을 생성하자.


 아마 위 화면과 같이 프로세스가 진행된 후에, 완성된 파일이 포함된 폴더가 팝업 될 것이다.












만약 Build가 처음인 경우, 다음과 같은 오류가 발생할 수 있다.



이것은 Android APK 파일의 Bundle Identifier가 잘못 설정된 문제로써, 이를 해결하기 위해서는 [Edit] -> [Project Settings] -> [Player]로 이동한 후, Settings for Android 항목에 있는 [Other Settings] -> [Identification] 항목에 위치한 Bundle Identifier의 이름을 바꾸어주어야 한다.

이름의 양식은 com.[Identifier 1(Company)].[Identifier 2(ProductName)] 을 지켜야 한다.
(본인의 경우 com.vrstudy.roadrunner 로 지정하였다)

또한 상단의 Product Name은 실제 어플리케이션의 이름이 되고, Company Name은 제작사의 이름이 되기 때문에 적절하게 변경해주도록 하자

생성된 파일은 APK 파일이기 때문에 직접 폰으로 복사한 뒤 폰에서 APK 파일을 실행시켜 설치를 진행하면 된다.



위의 화면은 실제 폰에서 실행된 어플리케이션의 화면을 캡쳐한 것으로, Unity에서 테스트했을 때와 똑같이 동작하는 것을 확인할 수 있다.

이로써, 첫번째 Cardboard 어플리케이션을 만들어보았다. 사실 대부분의 내용은 Unity의 기초적인 내용이었으며, Google VR SDK도 만들어진 Prefab을 사용했기 때문에 이게 정말 VR 어플리케이션 제작인지 의심스러운 면도 있을 것이다.

실제로 요 몇달 동안 카드보드를 다뤄보면서 느꼈던 점은, Cardboard를 활용할 경우 더욱 더 많은 신경을 쏟게 되는 부분이 카메라 제어보다 게임적인 요소, 그리고 사용자가 UI와 상호작용하는 방식이었다는 점이다.

그런 의미에서 다음 번에는 입력 장치가 없는 카드보드에서 어떻게 UI를 배치하고 상호작용할 것인지에 대해서 올려보고자 한다.