본문 바로가기
Learning/Unity

유니티 강좌_#03_트랜스폼

by HappyStar 2020. 8. 27.
반응형

 

 

먼저 트랜스폼에서 가장 중요한 에 대해서 알아보겠습니다. 축은 X축,Y축,Z축이 있는데요. 보통 X는 좌우, Y는 상하, Z는 높이로 알고 계시는데 Unity에서는 조금 다릅니다. 아래의 그림에 나와있는 파란색 화살표가 Z 축입니다. 그리고 빨간색은 X축, 녹색은 Y 축입니다. 유니티에서는 "Z 축이 앞뒤, Y축이 위아래, X축이 좌우"입니다. 축은 게임을 만들 때 정말 많이 쓰이는 개념이니 잘 외워 두시기 바랍니다. 

 

 

 

Transform컴포넌트에서 두번째 줄을 보시면 Rotation이라는 키워드가 있습니다. 이 키워드는 Rotation이라는 단어의 뜻처럼 회전을 시켜주는 기능을 가지고 있습니다. X축을 증가시켜주면 좌우에서 위아래로 움직이고, Y축은 좌우, Z 축은 위상 하에서 위아래로 움직입니다. Rotation은 개발할때 헷갈릴 수 있으니 많이 연습하는게 좋습니다.

 

 

 

Scale은 Position의 축과 같은 방향으로 크기가 커집니니다. X축은 좌우로 크기가 커지고, Y축은 위아래, Z 축은 상하로 커집니다.

 

 

 

이제부터는 본격적인 프로그래밍을 통해서 우리가 직접 오브젝트들의 위치, 회전, 크기를 조절하지 말고 간단하게 키보드로 컨트롤 할 수 있게 만들어 보겠습니다. 먼저 스크립트들을 관리할 폴더를 만들어 주어야 합니다. Project창에서 오른쪽 마우스 클릭 -> Create -> Folder이렇게 클릭하시면 Assets에 폴더가 새롭게 생성됩니다. 이 폴더의 이름을 Scripts라고 정해주세요.

 

 

그리고 Scripts 폴더 안에서 오른쪽 마우스 클릭 -> Create -> C# Script를 선택해줍니다. 그리고 이름은 아무 이름을 하셔도 상관없지만 Script의 맨 앞쪽 문자는 대문자로 지정해주세요. 꼭 해야 되는 것은 아니지만 프로그래밍에서의 관례라고 생각하시면 됩니다. 그리고 띄어쓰기는 하시면 안 됩니다. 이름 짓기가 완료되었으면 약간의 로딩 후에 비주얼 스튜디오가 열립니다.*새로운 단어의 첫글자 마다 대문자로 해주면 알아보기 쉬워요!!!

 

 

 

코드가 나타나면 아래와 같이 나타날 겁니다. 그리고 using UnityEngine은 지우시면 안 됩니다. 이게 있어야지 유니티상에 있는 오브젝트를 제어할 수 있기 때문입니다. 아래의 Start() 함수는 MonoBehaviour에 포함되어 있는 메서드입니다.

이 함수는 맨 처음 한번 실행되고 더 이상 실행되지 않습니다. 보통 초기 값을 줄 때 사용하는 함수입니다. Update() 함수는 컴퓨터마다 다르지만 1초에 대략 60번 이상 실행됩니다. 게임에서 오브젝트를 제어할 때 많이 쓰는 함수입니다.

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

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

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

 

이제부터 "앞쪽 방향"키를 누르면 앞으로 가는 기능을 만들어 보겠습니다. 생각만 해도 설레지 않나요?

//#using UnityEngine;과 나머지 네임스페이스는 편의상 생략하였습니다.

public class Test : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.position = this.transform.position + new Vector3(0, 0, 1);
        }
    }
}

 

-Input

입력과 관련된 것들을 모아놓은 클래스입니다. 

 

-GetKey

스크립트에서 설정한 키를 누르게 되면 True가 반환되는 메서드입니다.

 

-KeyCode

enum타입이며 특정한 Key를 선택할 수 있습니다.

 

-this

이 클래스가 속한 부모 오브젝트나 부모 클래스를 가리킵니다. 위의 코드에서는 오브젝트를 가리킵니다.

 

-transform

position, rotation, scale 등을 제어하는 컴포넌트입니다.

 

-new

특정한 것을 생성하는 키워드입니다.

 

-Vector3

위치 데이터를 저장해주는 키워드입니다.

 

결론적으로 위의 코드는 자신의 position의 z 축에 1만큼 매 프레임마다 더해 주는 코드입니다. 코드 작성을 다 하셨다면 다시 유니티로 돌아가서 Cube를 클릭하시고 Cube의 Inspector의 맨 밑에 Add Component에서 자신이 작성한 스크립트의 이름을 치고 스크립트를 추가해줍니다. 그리고 유니티 상단 가운데에 있는 실행 버튼을 눌러줍니다. 

 

 

 

위쪽 방향키를 눌러주면 아래 사진과 같이 앞으로 쭉 가는 모습을 보여 줍니다. 그런데 너무 빠르지 않나요? 이 상태로 게임을 만들게 된다면 밸런스가 맞지 않습니다. 그래서 우리는 스크립트를 조금 더 수정해 줄 겁니다.

 

 

 

public class Test : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.position = this.transform.position + new Vector3(0, 0, 1) * Time.deltaTime;
        }
    }
}

 

Vector3 함수 뒤에 "* Time.deltaTime"이 추가되었습니다. Time은 시간과 관련된 함수이고 deltaTime은 대략 1/60 정도를 가집니다. 그러면 1초에 60번씩 1을 더 하던 게 1/60을 곱해주니 1초에 1씩 더 해지겠죠? 다시 한번 저장하고 유니티로 돌아가서 실행을 해 봅시다. 다시 실행해보시면 아까보다 느리게 가는 것을 알 수 있습니다. 이제는 또 다르게 오브젝트를 움직여 보도록 하겠습니다.

public class Test : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.Translate(new Vector3(0, 0, 1) * Time.deltaTime);
        }
    }
}

 

아까와 다르게 position이 사라지고 Translate메서드에 Vecotr3와 deltaTtime을 추가한 코드입니다. Translate 메서드는 특정 방향으로 움직이게 하는 기능을 가지고 있기 아까보다 코드를 더 간단하게 쓸 수 있습니다. 실행시키면 아까와 같이 느리게 가는 모습을 보실 수 있을 겁니다. 이제 오브젝트를 회전시키는 방법을 알아봅시다.

 

오브젝트 회전 

public class Test : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.eulerAngles = transform.eulerAngles + new Vector3(90, 1, 1) * Time.deltaTime;
            Debug.Log(transform.eulerAngles);
        }
    }
}

 

-eulerAngles

우리가 유니티의 transform창에서 보는 rotation입니다.

 

-Debug.Log

특정한 문구나 데이터를 출력할 수 있습니다.(오류나 특정한 것을 기록할 때 많이 사용합니다.)

 

위의 코드를 유니티에서 실행하시면 x축방향으로 잘 돌아갑니다. 그런데 90도가 넘게 되면 갑자기 버벅거리면서 더 이상 회전하지 않습니다. 왜 그럴까요? 콘솔의 eulerAngles의 값과 Transform의 Rotation값은 같아야 하지만 다릅니다. 그러면 외부에서 보이는 것과 내부에서 작동하는 것이 다르다는 겁니다. 이상하게 변한 값을 다시 받아와서 그 값에 이상하게 변해버린 값을 더해주기 때문에 이런 오류가 생기는 겁니다.

public class Test : MonoBehaviour
{
    Vector3 rotation;

    void Start()
    {
        rotation = this.transform.eulerAngles;
    }
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            rotation = rotation + new Vector3(90, 0, 0) * Time.deltaTime;
            this.transform.eulerAngles = rotation;
            Debug.Log(transform.eulerAngles);
        }
    }
}

 

위 코드는 Vector3 타입의 rotation이라는 변수를 만들고 rotation에 eulerAngles를 넣어주고 Update() 함수에서 rotation에 x축을 증가 한 값을 더해 주는 방식입니다. 아까는 자신의 값에서 계속 받아왔지만 이 코드는 따로 값을 저장하는 변수에 값을 더 해주는 것 이기 때문에 아까와 같은 오류는 발생하지 않습니다. 회전도 또 다른 방식으로 코드를 작성해 보도록 하겠습니다.

public class Test : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.Rotate(new Vector3(90, 0, 0) * Time.deltaTime);
        }
    }
}

 

위의 코드를 실행하면 아까와 같이 x축 방향으로 움직이지만 코드가 아까처럼 복잡하지 않고 간결합니다. 유니티에서는 eulerAngles로 오브젝트를 제어하는 것에 예상치 못한 문제가 발생할 수 있기 때문에 주의하라고 합니다. 그래서 Rotate() 메서드를 사용하면 코드를 더 간결하게 할 수 있습니다. 그리고 또 다른 방법을 알아봅시다. 

public class Test : MonoBehaviour
{
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.rotation = new Quaternion(1, 1, 1, 1);
        }
    }
}

 

이제는 rotation을 사용하여서 회전을 시킬 건데 Quaternion이라는 키워드가 등장합니다. 그런데 Quaternion은 4개의 매개변수를 가지고 있습니다. 그리고 최댓값도 1입니다. 이렇게라면 도저히 어떻게 동작할지 예측이 안 갑니다. 그래서 Quaternion.Euler()를 이용하면 Vector3와 Time.deltaTime을 추가할 수 있습니다.

public class Test : MonoBehaviour
{
    Vector3 rotation;

    void Start()
    {
        rotation = this.transform.eulerAngles;
    }
    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            rotation = rotation + new Vector3(90, 0, 0) * Time.deltaTime;
            this.transform.rotation = Quaternion.Euler(rotation);



        }
    }
}

 

실행해보시면 큐브가 잘 돌아가지만 이 방법은 너무 복잡합니다. 그런데도 이런 방법을 쓰는 이유는 무엇일까요? 어느 한 오브젝트의 축을 특정한 값으로 고정해 놓은 뒤 다른 축의 값을 설정하면 축이 고장 나 버립니다. 이런 현상을 짐벌 락이라고 합니다. 하지만 Quaternion은 이런 현상이 없습니다. 그래서 Quternion을 사용합니다.

 

오브젝트 크기 조절

아래 코드는 위쪽 화살표 방향키를 눌렀을 때 큐브가 커지는 기능을 구현한 것입니다. 아주 간단합니다. 그리고 크기를 줄이고 싶으면 마이너스(-) 부호를 + 대신에 붙여주면 됩니다.

public class Test : MonoBehaviour
{
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.localScale = this.transform.localScale + new Vector3(2, 2, 2) * Time.deltaTime;
        }
    }
}

 

정규화 벡터

정규화 벡터는 방향을 가리키는 예약어를 사용합니다. right, left, forward, back 등 이 있습니다. 이 단어들의 뜻처럼 right는 오른쪽, left는 왼쪽, forward는 앞, back은 뒤입니다. 아래의 예시처럼 forward가 있는 자리에 위의 예약어들을 교대해서 추가하여 사용하면 됩니다. speed변수를 사용하여 속도도 조절할 수 있습니다.

public class Test : MonoBehaviour
{
    int speed = 5;
    
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.position = this.transform.position + (speed * this.transform.forward * Time.deltaTime);
        }
    }
}

 

여러 메서드

LookAt : LookAt은 특정한 대상을 바라보게 하는 메서드입니다. 아래와 같이 코드를 작성하고 유니티로 들어가서 이 코드가 속해있는 큐브를 선택한 뒤 큐브의 Inspector창에서 Test스크립트를 보면 우리가 작성한 코드와 같은 이름인 버튼이 생성됩니다. None (Game Object) 칸 오른쪽에 있는 동그란 버튼을 누르고 우리가 바라볼 대상을 선택합니다. 저는 시험 삼아서 새로운 오브젝트를 생성하였습니다.

 

 

public class Test : MonoBehaviour
{
    public GameObject new_cube;
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            this.transform.LookAt(new_cube.transform.position);
        }
    }
}

***GameObject의 지정자를 private로 하면 Inspector창에 뜨지 않습니다. 하지만 [SerializeField]를 
위칸에 입력해주면 강제로 Inspector창에 띄울 수 있습니다.***

 

위쪽 화살표 방향키 누르기 전

 

 

위쪽 화살표 방향키 누른 후

 

 

 

위와 같이 한 오브젝트를 바라보는 것을 알 수 있습니다.

 

RotateAround : 특정한 오브젝트 주위를 회전합니다. 비유하자면 지구가 태양을 공전하는 것에 비유할 수 있습니다.

public class Test : MonoBehaviour
{
    public GameObject new_cube;
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            transform.RotateAround(new_cube.transform.position, Vector3.up, 30 * Time.deltaTime);
        }
    }
}

 

실행결과 : Test스크립트를 가지고 있는 큐브가 우리가 지정한 큐브를 회전하는 것을 알 수 있습니다.

 

 

 

이번 강의 정말 길었죠? 이번 글이 제 블로그에서 가장 긴 것 같네요. 다음 강의에서 만나요!

 

*!*Unity B 시리즈 강의는 유튜브 채널 케이디 님의 강의와 여러 블로그를 참고하며 만들었습니다.*!*

 

반응형

댓글