본문 바로가기
코딩과 알고리즘

유니티3D(Unity3D) 1인칭 시점 조작 #1

현실에서 우리는 거울이나 또는 반사 물체를 통하지 않고는 나의 모습을 볼 수가 없습니다.
내가 볼 수 있는 것은 나의 눈을 통해 보여지는 사물들 뿐이지요.

가상공간 또한 나의 눈을 통해 보듯이 내 모습은 보이지 않고 주위 사물들만 보인다면 어떨까요?
아무래도 몰입감이 더 있겠지요?
나의 눈을 통해 보듯이 주위 사물들만 보이는 것, 이 것이 바로 1인칭 시점입니다.

오늘은 유니티3D 엔진에서의 1인칭 시점 이동에 대해 다뤄보겠습니다.
내가 눈을 통해 주위를 둘러보듯이 가상공간 안에서 주위 건물들을 둘러볼수가 있는게 1인칭 시점입니다.

이런 기능을 수행해주는 스크립트는 비교적 간단한데요.
전체 스크립트는 아래와 같습니다. 이번 시간에는 이 중 마우스 조작에 대해 살펴보겠습니다.

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

public class FirstPerson : MonoBehaviour
{
    public float turnSpeed = 4.0f; // 마우스 회전 속도
    public float moveSpeed = 2.0f; // 이동 속도

    private float xRotate = 0.0f; // 내부 사용할 X축 회전량은 별도 정의 ( 카메라 위 아래 방향 )
    
    void Update ()
    {
        MouseRotation();
        KeyboardMove();
    }
    
    // 마우스의 움직임에 따라 카메라를 회전 시킨다.
    void MouseRotation()
    {
        // 좌우로 움직인 마우스의 이동량 * 속도에 따라 카메라가 좌우로 회전할 양 계산
        float yRotateSize = Input.GetAxis("Mouse X") * turnSpeed;
        // 현재 y축 회전값에 더한 새로운 회전각도 계산
        float yRotate = transform.eulerAngles.y + yRotateSize;

        // 위아래로 움직인 마우스의 이동량 * 속도에 따라 카메라가 회전할 양 계산(하늘, 바닥을 바라보는 동작)
        float xRotateSize = -Input.GetAxis("Mouse Y") * turnSpeed;
        // 위아래 회전량을 더해주지만 -45도 ~ 80도로 제한 (-45:하늘방향, 80:바닥방향)
        // Clamp 는 값의 범위를 제한하는 함수
        xRotate = Mathf.Clamp(xRotate + xRotateSize, -45, 80);
    
        // 카메라 회전량을 카메라에 반영(X, Y축만 회전)
        transform.eulerAngles = new Vector3(xRotate, yRotate, 0);
    }
    
    // 키보드의 눌림에 따라 이동
    void KeyboardMove()
    {
        // WASD 키 또는 화살표키의 이동량을 측정
        Vector3 dir = new Vector3(
            Input.GetAxis("Horizontal"), 
            0,
            Input.GetAxis("Vertical")
        );
        // 이동방향 * 속도 * 프레임단위 시간을 곱해서 카메라의 트랜스폼을 이동
        transform.Translate(dir * moveSpeed * Time.deltaTime);
    }
}

 

지난번에는 유니티 샘플 프로젝트로 시작했지만, 오늘은 백지에서 시작할텐데요.
유니티를 시작한 후, 아래와 같이 3D 항목을 선택한 다음,

생성 버튼을 눌러주시면, 로딩 화면이 끝난 다음에 유니티3D 화면에 진입하게 됩니다.
우선 여러분의 화면이 저와 다를수 있기 때문에 맨 화면 오른쪽의 화살표 버튼을 눌러 2 by 3 으로 바꿔 주세요.
간혹 화면 해상도가 너무 낮은 경우 이 항목이 노출되지 않는 경우가 있기 떄문에 화면해상도를 높여준 다음에 다시 이 부분을 클릭하시면 노출될 것입니다.

먼저 화면 설명을 드릴텐데요.
지난번에도 보았던, 이 창은 씬(Scene), 즉 장면이라고도 부르는 창인데요.
이해를 돕자면 "무대"라는 표현이 가장 적절할 것 같습니다.

각각의 물체들이 마치 배우처럼 무대에서 자기만의 역활을 소화하며 하나의 극을 소화해내는 것이지요.
유니티로 프로젝트를 만드는 것은 하나의 연극무대를 구성하는 것과도 같습니다.

그러다가 필요하다 싶으면 장면을 바꿔서 다시 새로운 연출을 하기도 하지요.
뭐.. 말은 거창하지만 크레이도 아직 배우는 입장이라 갈길은 좀 멉니다 :)

연극무대는 크게 2가지 다른 각도로 구성될 수 있습니다.
한가지는 극을 꾸미는 편, 즉 감독, 연출, 배우들이 모니터링하면서 서로 보는 각도와,
또 한가지는 뒤에서 도대체 무슨 일이 일어나는지 전혀 알수 없는 방청객들이 보는 각도 말입니다.

방청객을 플레이어라고 할 때, 유니티3D에서도 이 플레이어에게 보여지는 각도가 있습니다.
이를 바로 "카메라"라고 하지요.

먼저 한가지 테스트를 해보겠습니다.
GameObject - 3D Object - Cube 를 선택해보세요.

그러면 아래와 같이 상자가 생겨날 것입니다.

그리고 그 아래 Game 이라고 적힌 또 하나의 창은 이와 같이 표시가 되는데요.
이상하게 위와 아래가 화면이 다르지요?

왜 그럴까요? 바로 아래의 창이 플레이어가 볼 수 있는 방청객 모드의 창이기 때문입니다.
이 각도는 어떻게 정하는 것일까요? 바로 "카메라"가 결정합니다.

금방 생성한 상자의 왼쪽 좀 떨어진 곳에 보면, 흰 색으로 보여지는 부분이 있을텐데요.
이 것이 카메라입니다.

오른쪽에 보시면 Hierarchy 라는 창이 있는데요.
이 창은 무대의 구조, 곧 씬(Scene)의 구조를 표현한 창입니다.
여기서 Main Camera 를 선택하신 다음,

마우스를 다시 Scene 창으로 이동하면 금방 선택한 카메라에 화살표가 표시될 것입니다.
여기서 파란 화살표를 당겨서 이동해 보세요,

그러면 화면 아래 Game 창에서는 물체가 커지는 것을 보실 수 있는데요.
사실 커진 것이 아니라 물체 가까이 카메라가 이동한 것입니다.

만일 화살표를 반대쪽으로 당겨보면, 

카메라를 통해 비치는 물체는 한 없이 한 없이 작아지겠지요. 역시 거리가 멀어진 것 뿐입니다.
이 것이 바로 카메라를 통해 보는 1인칭 시점입니다.

카메라를 통해서 이렇게 물체를 가까이서, 멀리서 볼 수 있는 것 뿐만 아니라, 카메라를 회전시키면 물체가 좌우로 움직이는 듯이 보일텐데요.
카메라를 회전시키려면 화면 상단의 이와 같이 생긴 아이콘을 선택하시면 됩니다.

그러면 카메라가 주위에 이와 같은 위성주기(?)와 같은 선들이 생겨날텐데요.

녹색선을 마우스로 클릭해서 옆으로 살짝 옮겨 봅시다.

그러면 물체가 옆으로 이동한 듯 하나, 물체는 여전히 그 자리에 가만히 있는 것입니다
그저 카메라의 중심점이 다른 곳을 향하도록 회전한 것 뿐이지요.

하늘에서 바라본다면 카메라가 바라보는 시야각은 물체의 오른쪽을 향하고 있는 것입니다.
아래 그림 처럼 말이지요 :)

카메라는 단지 자신의 위치를 이동하고 바라보는 각도만큼 회전한 것 뿐입니다.

만일 그렇다면 지난 번처럼 플레이를 시작했을 때처럼,

키보드의 조작을 통해 카메라 좌표가 제어되고
마우스의 조작을 통해 카메라가 바라보는 방향이 제어되도록 스크립트를 구성한다면,
그것이 바로 1인칭 자유 시점 조종이 되는 것입니다.

이제 하나씩 살펴 보실까요? :)


우선 현재 만들어진 Scene(장면) 은 무시하고 하나 더 생성해보겠습니다.
File - New Scene 을 선택하면 새로운 무대가 하나 생겨납니다.

Hierarchy ( 구조 ) 창에는 Untitled, 즉 이름이 붙여지지 않은 Scene과,
그 하위로 Main Camera, Directional Light 만 존재합니다.
참고로 Directional Light은 방향성 빛입니다.

이 상태에서 큐브를 하나 만들고, ( Game Objec - 3D Object - Cube )

큐브를 선택하면 나오는 Inspector 창에서 Scale X, Z 항목을 각각 5로 수정합니다. 

Scene 창은 아래와 같이 표현되어 있을 것입니다.

다시 Hierarchy ( 구조 ) 창에서 Cube 를 선택하고, Ctrl+D 키를 누르시면,
Cube (1) 이란 오브젝트가 새롭게 복사가 됩니다.

이 번에는 Inspector 창에서 Position 을 5로 변경하시면,

복제된 오브젝트는 이렇게 하늘에 위치하게 됩니다. 유니티에서는 Y 좌표가 높이거든요.

또 한번 Hierarchy ( 구조 ) 창에서 Cube ( 1 ) 을 선택 후, Ctrl + D 키를 누르면,
이번엔 Cube ( 2 ) 가 생겨납니다.

이번에는 Inspector 창에서 Position 의 Y값을 -5로 변경해주세요.

그러면 Scene ( 장면 ) 화면이 아래와 같이 바뀌었을 것입니다

그리고 Game 화면은 이와 같이 보일 겁니다.
실제로 유저가 시작하는 시점이 바로 이 장면이기도 합니다.

이렇게 준비하는 과정은 카메라 조작 스크립트와는 사실 관련은 없는데요.
카메라 조작과정에서 하늘이나 땅 방향을 바라볼 때 물체가 보이게 하기 위함입니다.

이제 스크립트를 준비해볼까요?
먼저 스크립트는 아셋(Assets)이라는 자원을 보관하는 폴더에 넣는 것이 일반적입니다.
Assets 폴더마우스 우클릭Create - Folder 메뉴를 선택하세요

폴더 이름을 입력하는 창이 나올 것입니다. 그러면 폴더명을 Scripts 라고 입력해주세요. ( 입력 후 Enter )

아셋(Assets) 폴더 하위에 Scripts 라는 폴더가 생겨 났을 것입니다.
보통 스크립트는 이 폴더 안에 넣는 것이 일반적입니다.
다른 폴더에 넣더라도 문제는 없지만 하나의 작은 약속이랄까요? :)

이제 폴더 오른쪽 창 ( Assets > Scripts > 리고 적힌 부분 ) 에서 마우스를 우클릭하고, 
Create - C# Script 메뉴를 선택하시면,

스크립트 아이콘이 뜨면서 스크립트 이름을 짓는 란이 뜰텐데요.
영어나 숫자로 지어주셔야 하고 첫번째 글자는 영문이어야 합니다. 1인칭 주인공 시점이므로 firstPersonCam 으로 지어보도록 하지요.

그리고 나서 스크립트를 더블클릭하시면 되는데 잠깐!

정상적인 설치과정을 거치셨다면 보통 Visual Studio 2019 또는 그 상위 버전 설치되었을텐데요.
만약 개발도구 선택이 안되어 있다면, Edit - Preference 옵션에 들어가셔서,

외부 편집기 설정에서 Visual Studio 2019 를 선택하시면 됩니다.
그리고 X 버튼으로 창을 닫아 주시면 됩니다.

위 화면에는 Visual Studio Code 가 선택되어 있는데 크레이 컴퓨터가 문제가 있어서 Visual Studio 2019 를 사용하지 못하기 때문입니다.

이제 C# 스크립트를 더블클릭해볼까요?
크레이는 비주얼 스튜디오 코드 에디터 화면이 떴지만, 보통은 비주얼 스튜디오 2019 또는 그 이후 버전 화면이 떳을 것입니다. 뭐 상관은 없습니다 :)

이제 스크립트 내용을 아래와 같이 바꿔 주세요.
그리고 Ctrl+S 로 저장해 주시면 됩니다.

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

public class firstPersonCam : MonoBehaviour
{
    public float turnSpeed = 4.0f; // 마우스 회전 속도    
    private float xRotate = 0.0f; // 내부 사용할 X축 회전량은 별도 정의 ( 카메라 위 아래 방향 )
    
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        // 좌우로 움직인 마우스의 이동량 * 속도에 따라 카메라가 좌우로 회전할 양 계산
        float yRotateSize = Input.GetAxis("Mouse X") * turnSpeed;
        // 현재 y축 회전값에 더한 새로운 회전각도 계산
        float yRotate = transform.eulerAngles.y + yRotateSize;

        // 위아래로 움직인 마우스의 이동량 * 속도에 따라 카메라가 회전할 양 계산(하늘, 바닥을 바라보는 동작)
        float xRotateSize = -Input.GetAxis("Mouse Y") * turnSpeed;
        // 위아래 회전량을 더해주지만 -45도 ~ 80도로 제한 (-45:하늘방향, 80:바닥방향)
        // Clamp 는 값의 범위를 제한하는 함수
        xRotate = Mathf.Clamp(xRotate + xRotateSize, -45, 80);
    
        // 카메라 회전량을 카메라에 반영(X, Y축만 회전)
        transform.eulerAngles = new Vector3(xRotate, yRotate, 0);
    }
}

이제 다시 유니티 화면으로 돌아와서 이 스크립트를 카메라에 적용해주면 되는데요.
스크립트를 드래그해서 Main Camera 오브젝트에 놓아주시면 됩니다

적용이 되었는지 확실치 않아 보일텐데, Main Camera 오브젝트를 선택하고, Inspecter 창의 제일 아랫쪽을 보시면 ( 스크롤시켜야 보입니다 )
First Person Cam 이라는 컴포넌트가 추가되어 있을텐데요. 이게 금방 드래드해 넣은 스크립트입니다.

자, 이제 플레이를 해보겠습니다.
먼저 큰 화면으로 시원 시원하게 플레이하기 위해 "플레이할 때 큰화면 모드"로 바꿔보겠습니다.
Game 창의 Maximize On Play 버튼을 선택해 주세요.

그리고 상단의 플레이 버튼을 누르시면,

큰 화면으로 플레이가 됩니다.

이제 마우스를 이리 저리 움직여 보세요,
그러면 마치 가상 공간안에 있는 것처럼 이 곳 저곳 방향을 둘러볼 수 있습니다.

키보드로 이동 조작 기능은 아직 빠져 있습니다.
단순히 마우스로 둘러보는 기능일 뿐인데요.

어떻게 스크립트가 직동하는 건지 잠깐 살펴보겠습니다.

참고로 상단의 플레이 버튼을 한번 더 누르면 플레이가 중지되어 원래의 편집화면으로 복귀합니다.

.처음에 유니티가 기본으로 만들어준 스크립트는 아래와 같습니다.

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

public class firstPersonCam : MonoBehaviour
{   
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
    
    }
}

이 중 아래와 같은 부분을 이벤트라고 하는데요. 

void Start(){ ... }
void Update(){ ... }

유니티의 이벤트는 매우 여러가지가 있으나 가장 자주 쓰이는 2가지를 유니티가 자동 코딩해서 제공해주는 것이지요.
이 안에다가 기능을 짜 넣으면 되는 것입니다.

유니티 기본 코드 외에 제일 먼저 입력된 코드는 아래와 같은데요.
이 코드는 이벤트 안에 정의되지 않았습니다. 그리고 괄호같은걸로 묶여 있지가 않은데
이런걸 일컬어 변수 또는 프로퍼티(속성)라고 합니다. 

public float turnSpeed = 4.0f; // 마우스 회전 속도    
private float xRotate = 0.0f; // 내부 사용할 X축 회전량은 별도 정의 ( 카메라 위 아래 방향 )

public, private 등에 대한 설명은 지금은 넘어가기로 하고,
float 는 소숫점이 포함되는 실수라는 의미입니다.
turnSpeed 라는 변수를 4.0값으로 선언했는데요. float 유형의 값은 보통 뒤에 f 를 붙이게 되어 있습니다 ( 4.0f )
xRotate 도 마찬가지이지요.

이 2개의 변수를 마우스 제어 기능에서 사용하게 됩니다.

마우스 제어 스크립트를 하나씩 살펴볼까요?

우선 Start 이벤트는 맨 처음에 한번 호출됩니다. 하지만 Update 이벤트는 매번 수시로(매프레임마다) 호출되지요.

그렇다면 사용자의 시점을 마우스 움직임에 따라 지속적으로 바꿔주려면 Update 이벤트 안에 코드를 넣어야 합니다.

마우스의 상대적인 움직임을 알아내는 유니티 함수는 아래와 같은데요.

Input.GetAxis("Mouse X") : 마우스를 좌우로 움직인 양
Input.GetAxis("Mouse Y") : 마우스를 위아래로 움직인 양

이 값을 그대로 적용하면 속도가 너무 느립니다.
그래서 4.0f 값이 들어 있는 turnSpeed 값을 곱해 주는 것입니다.

우선 좌우로 마우스를 이동한 량을 구하고,

float yRotateSize = Input.GetAxis("Mouse X") * turnSpeed;

현재 카메라의 바라보는 방향에 좌우 회전 방향값을 더해 줍니다. y축을 회전하는 것이지요.

float yRotate = transform.eulerAngles.y + yRotateSize;

"엇 마우스를 좌우로 움직였는데 X축이 회전하는게 아닌가요?"

"Y축 회전이 맞습니다."
Y축이 하늘 방향이기 때문이며, 팽이가 Y축을 중심으로 돌아간다고 생각하시면 됩니다.

그리고 마우스 위아래 이동시 회전할 각도도 회전량을 계산합니다.

float xRotateSize = -Input.GetAxis("Mouse Y") * turnSpeed;

마우스 상하 이동시 좀 특별한 처리가 추가되는데요.
최대로 올려다 볼수 있는 각도와, 내려다 볼 수 있는 각도를 제한하는 것입니다.

그래서 X축 이동량 xRotateSize 를 계산할 때 아래와 같은 처리를 해줍니다.

xRotate = Mathf.Clamp(xRotate + xRotateSize, -45, 80);

정면이 각도 0이라고 할때 아래로 내려다 보는 각도는 80도, 하늘을 올려다 볼 수 있는 각도는 45도로 제한됩니다.

그리고 계산된 최종 각도를 카메라의 각도에 대입해서 바라보는 방향을 바꿔치기 하는 것입니다.
매 순간 순간마다 말이지요 :)

transform.eulerAngles = new Vector3(xRotate, yRotate, 0);

오늘은 여기까지!
키보드를 다루는 법에 대한 설명은 다음에 또 진행하겠습니다.

필요하신 분에게 도우미 되셨을지요?
오늘도 여기까지 읽어주셔서 감사합니다 :)


다음 챕터 1인칭 시점 조작 #2

itadventure.tistory.com/390

 

유니티3D(Unity3D) 1인칭 시점 조작 #2

이번 시간에는 지난 시간에 이어 1인칭 시점 조작의 이동편에 대해 다루어 보겠습니다. 3D 가상공간에서 1인칭 시점으로 거리를 걷는 것은 현실과 아주 유사합니다. 만일 내가 바라보는 방향이

itadventure.tistory.com

 

반응형