[섹션2] Transform(트랜스폼)

<Transform: 객체 이동시키기>

 

  • Asset 다운 받기

1. 우선 asset store에서 내가 필요로하는 asset을 다운받는다.

2. 다운받은 asset의 구성은

   - Model: 움직이는 객체 (영화에서 나오는 배우같은 역할)

   - Materials: 재질 (옷을 입힌다)

   - Animation: 객체의 세세한 동작(코드로 구현할 수 없는 부분에 대한 애니메이션 효과)

   => 해당 Model을 Scene에 

   해당 모델도 이전에 배웠던 것과 마찬가지로 여러  component로 이뤄짐

 

 

<객체 이동>

  • 객체의 이동: 기본

1. 위에서 다운 받은 Unity chan의 Model을 객체로 놓고 PlayerController script 파일을 만든다.

2. 위의 객체 이름을 Player라고 지정한 후 component에 PlayerController를 추가한다. 

다음을 보면 PlayerController가 추가된 것이 보임.

 

3. PlayerController의  Script 파일의 코드: 'transform'을 이용하여 해당 객체의 위치를 변경할 수 있다. transform.positon을 이용하여 해당 vector를 다음과 같이 바꾼다.  (Vector3(0.0f, 0.0f, 1.0f) -> 좌표에서의 (0,0,1) 과 같음

* 원래 GameObject 아래에 Transform, PlayerController가 존재함 이때, PlayerController에 접근하기 위해서는 GameObject와 Transform에 접근해야하지만 너무 자주 사용하기때문에 바로 transform에 접근할 수 있도록함.

PlayerController의  Script 파일의 코드

  => Player가 움직이기는 하지만, 굉장히 어색하기 움직임

 

  • 객체의 이동: 심화

1. 객체의 position에 변수를 곱하여 좀 더 자연스럽게 움직이도록 조절한다. 

2. 다음과 같이 speed를 설정한다. 전역 변수로 _speed 변수 선언 이때, public 으로 설정하면 유니티에서 속도 바로 조절 가능 하지만, 객체 지향적 관점에서 멤버 변수는 private으로 선언하므로 [serializeField]라는 어트리뷰트를 이용하면 c#에서 reflaction을 통해 unitity 툴에 바로 접근 가능

* 이때 public으로 GameObject를 선언하여 바로 object에 접근가능하도록 할 수 있다. 

3. position에 약간의 변수를 곱하여 좀 더 자연스럽게 움직이도록 한다. Time.deltaTime은 시간의 차이에 대한 변수, speed는 속도에 대한 변수이다. 위에서 new Vector3(0.0f, 0.0f, 1.0f)를 바로 Vector3.forward, back, left, right을 이용하여 바로 사용가능 => 가독성이 더 높아짐

 

  • 객체 이동: 적용

1. Scene에는 Local View와 Global View가 있다. Global View는 전체 Scene에서의 좌표를 말하고 Local View는 객체의 좌표가 바라보는 좌표를 말한다. 

2. 객체의 좌표가 바라보는 쪽으로 이동시키기 위해 transform.TransformDirection을 이용하여 Local -> Global로 변환한다. InverseTransformDirection은 반대로 World -> Local로 좌표를 변환하는 것 

 

3. transform.Translate을 이용하면, 객체가 바라보는 좌표를 기준으로 움직인다.

 

<Vector>

위의 transform에서 보면 알 수 있듯이 position을 옮길 때, Vector를 사용하여 옮긴다. 이때의 Vector의 의미에 대해 알아보자.

 

1. Vector는 크게 두개로 나뉨

-> 위치 벡터

-> 방향 벡터

 

  • 위치 벡터

1. Vector 는 3개의 float 변수로 이뤄짐 -> (x, y, z)

이것을 이용하여 MyVector 구조체를 만들어주자

 

MyVector 구조체

2. 다음과 같이 사용 가능

 

3. Vector의 operator(덧셈, 뺄셈 등등)을 정의: 다음과 같이 operator를 통하여 내가 정의한 MyVector 구조체의 연산자 정의

 

=> 위와 같이 MyVector를 position으로 사용하여 객체의 위치를 표현할 수 있다. 

 

  • 방향 벡터

방향 벡터는 두 명의 Player가 있을 때, A Player에서 B Player로 이동하고 싶을 경우 해당 벡터의 차이만큼 이동해야한다. -> 해당 벡터의 방향으로 이동해야한다. 이것을 방향 벡터라 함. 

 

1. 아래의 두 벡터의 차이가 바로 방향 벡터

 

2. 방향 벡터로 알 수 있는 것은 거리(크기), 방향 이 두가지 이다. 

 

3. 거리를 알기 위해서 magnitude 함수를 이용하면 된다. 피타고라스 정리를 이용한다. * 이미 Vector내의 magnitude를 알 수 있는 함수 내장되어 있음. 

 

4. 방향을 알기위해(= 크기가 1자리인 벡터를)알기 위해 다음과 같이 normalized 함수를 이용한다. * 이미 Vector내의 normalized 를 알 수 있는 함수 내장되어 있음. 

 

5. normalized를 이용하여 position을 변경할 수 있다. 

 

<Rotation>

  • 회전 시키기

1. 절대 회전값: 회전할때의 각도를 명시하여 사용한다 => transform.eulerAngles 를 사용

  : 절대적인 값을 정의해야함.

2. 변경된 사항을 사용 delta+-가 가능하도록 => transform.Rotate를 사용함

3. 회전에 대한 변수는 Vector가 아닌 Quaternion으로 w라는 float 변수가 추가된다. *이유는 gimbal lock 문제 (몰라도됨) => Quaternion을 이용하여 바로 rotation을 조작.

 

  • 방향키에 따라 Player가 해당 방향을 바라보도록

게임에서 마우스로 어떤 방향을 가르키면 Player가 해당 방향으로 rotation을 돌린 후 이동한다. 이때의 Rotation을 하기 위해서는 Player의 방향을 바꾸어야함

 

1. Player의 바라보는 방향을 바꾸기 위해서는 Quaternion.LookRotation함수가 필요 

   : transform.rotation의 변수가 Quaternion이고 Quaternion의 LookRotation을 통해 해당 Vector로 바라보는 방향을 바꾼다. 이때 World view 기준으로 북쪽을 바라본다. 나머지 key에 대해서도 바꿔줌

 

=> 이렇게 바꿔주면 바라보는 방향이 변하긴 하지만 어색하게 변한다. 

 

2. Quaternion.Slerp을 사용하여 바라볼때 스무스하게 움직이도록 변경한다. 

   ** Quaternion.Slep(Quternion a, Quternion b, float c): 시작 방향이 a, 끝 방향이 b, c는 그 사이를 조절 0.0 ~ 1.0, a와 b 사이의 각도를 c 정도로 나누어서 방향 전환 

 

3. 이전에 배웠던 transform과 같이 사용

: 이렇게 사용하면 현재 key에 따라 Player의 rotation을 변경해줬으므로 forward, back, left, right 할 필요없이 Player가 바라보는 방향으로 forward를 해주면 됨, 나머지 key에 대해서 모두 Vector3.forward를 해주면 된다. 

하지만 이렇게 될 시, 어색하게(회전하면서) 움직인다 => Slerp을 통해 해당 방향을 바로 옮기는 게 아닌 스무스하게 옮겨서

4. 위의 문제를 해결하기위해 Translate을 사용하여 Local View로 하지않고 position을 사용하여 아예 Global로 좌표를 이동 시킨다. Vector도 foward, back, left, right 사용 

 

<Input Manager>

위에서 만든 transform에 대한 내용은 많은 Player와 객체에 이용된다. 그때마다 update 문으로 매번 관리하는 것은 과부화 => 별도의 Manager를 통해 관리

 

1. InputManager class 선언: keyboard 등의 입력이 들어오는지 확인

: OnUpdate 함수를 통해 KeyAction이 들어올 경우 Invoke로 전파

2. Managers에서 위의 InputManager 불러옴

3. PlayerController에서 해당 Managers를 이용하여 KeyAction이 들어올 경우 OnKeyborad() 함수 호출. 이때 이전에 다른 곳에서 호출 될 수도 있기 때문에 -를 해준 후에 +를 해준다. 

OnKeyboard 함수는 이전의 Update()함수의 내용

 

 

<전체 소스 코드>

  • PlayerController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class PlayerController : MonoBehaviour
{ 
    [SerializeField]
    float _speed = 10.0f;
    // Start is called before the first frame update

    void Start()
    {
        Managers.Input.KeyAction -= OnKeyboard;
        Managers.Input.KeyAction += OnKeyboard;
    }

    float _yAnlgle = 0.0f;
    // Update is called once per frame
    void Update()
    {
       

        

    }

    void OnKeyboard()
    {
        if (Input.GetKey(KeyCode.W))
        {
            // W를 누르면 해당 위쪽(북쪽)방향을 바라보도록 rotation 변경
            //transform.rotation = Quaternion.LookRotation(Vector3.forward);
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.forward), 0.2f);
            transform.position += (Vector3.forward * Time.deltaTime * _speed); // 시간 x 거리 = 속력
        }
        if (Input.GetKey(KeyCode.S))
        {
            //transform.rotation = Quaternion.LookRotation(Vector3.back);
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.back), 0.2f);
            transform.position += (Vector3.back * Time.deltaTime * _speed);
        }

        if (Input.GetKey(KeyCode.A))
        {
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.left), 0.2f);
            //transform.rotation = Quaternion.LookRotation(Vector3.left);
            transform.position += (Vector3.left * Time.deltaTime * _speed);
        }

        if (Input.GetKey(KeyCode.D))
        {
            transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), 0.2f);
            //transform.rotation = Quaternion.LookRotation(Vector3.right);
            transform.position += (Vector3.right * Time.deltaTime * _speed);
        }
    }
}

 

  • Managers
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Managers : MonoBehaviour
{
    static Managers s_instance; // static의 특성을 이용햐여 유일성 보장
    static Managers Instance { get { Init();  return s_instance; } } // 외부에서 사용할때는 해당 함수를 이용하여 가져옴

    InputManager _input = new InputManager();
    public static InputManager Input { get { return Instance._input; } }

    // Start is called before the first frame update
    void Start()
    {
        Init();
    }

    // Update is called once per frame
    void Update()
    {
        _input.OnUpdate();
    }

    static void Init()
    {
        // 초기화
        if(s_instance == null)
        {
            GameObject go = GameObject.Find("@Managers"); // 원본 Managers를 가져옴

            // scene에 @Managers가 없을 경우
            if (go == null)
            {
                go = new GameObject { name = "@Managers" };
                go.AddComponent<Managers>();
            }

            DontDestroyOnLoad(go); // 절대 삭제 안됨
            s_instance = go.GetComponent<Managers>(); // object의 component를 가져올 수 있음

        }
        

    }
}

 

  • InputManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Managers : MonoBehaviour
{
    static Managers s_instance; // static의 특성을 이용햐여 유일성 보장
    static Managers Instance { get { Init();  return s_instance; } } // 외부에서 사용할때는 해당 함수를 이용하여 가져옴

    InputManager _input = new InputManager();
    public static InputManager Input { get { return Instance._input; } }

    // Start is called before the first frame update
    void Start()
    {
        Init();
    }

    // Update is called once per frame
    void Update()
    {
        _input.OnUpdate();
    }

    static void Init()
    {
        // 초기화
        if(s_instance == null)
        {
            GameObject go = GameObject.Find("@Managers"); // 원본 Managers를 가져옴

            // scene에 @Managers가 없을 경우
            if (go == null)
            {
                go = new GameObject { name = "@Managers" };
                go.AddComponent<Managers>();
            }

            DontDestroyOnLoad(go); // 절대 삭제 안됨
            s_instance = go.GetComponent<Managers>(); // object의 component를 가져올 수 있음

        }
        

    }
}

'Development > 유니티' 카테고리의 다른 글

[섹션5]Camera(카메라)  (0) 2021.07.13
[섹션4] Collsion(충돌)  (0) 2021.07.09
[섹션 3] 프래팹(Prefab)  (0) 2021.07.08
[섹션1] 유니티 기본 구성 및 기초  (0) 2021.06.29
환영합니다!  (0) 2021.06.28