Unity 강의/Unity Course(2) - 절대강좌! 유니티

[Unity Course 2] 04. 주인공 캐릭터 제작 : 캐릭터의 이동

첨부엉. 2024. 6. 18. 16:34
반응형
위키북스 출판사 이재현 저자님의 '절대강좌! 유니티' 책을 참고하여 필기한 내용입니다.

 

캐릭터의 이동

유니티에서 어떤 물체를 이동시키거나 회전시키는 방법은 2가지

1. 게임 오브젝트에 있는 Transform 컴포넌트의 position, rotation 속성값을 지속해서 변경하는 것

2. 유니티 엔진에 내장된 물리 엔진을 이용해 물리적인 힘(Force) 또는 회전력(Torque)을 가해 변경시키는 것

 

애니메이션으로도 이동 및 회전할 수 있지만 이것 역시 Transform 컴포넌트의 속성값을 연속적으로 기록한 것으로 첫번째 방법에 해당된다.

using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    void Start()
    {
        
    }

    void Update()
    {
        float h = Input.GetAxis("Horizontal");  // -1.0f ~ 0.0f ~ +1.0f
        float v = Input.GetAxis("Vertical");    // -1.0f ~ 0.0f ~ +1.0f

        Debug.Log("h = " + h);
        Debug.Log("v = " + v);

        // Transform 컴포넌트의 position 속성값을 변경
        transform.position += new Vector3(0, 0, 1);

    }
}

 

1. 컴포넌트

2. 속성 ( 프로퍼티)

3. 저장할 값

 

Transform 컴포넌트의 position 속성은 Vector3(x,y,z) 타입으로 3개의 값(x,y,z)을 갖는다.

위 코드에서 new Vector3(0,0,1) 값을 누적시킨 것으로 x,y 값은 모두 0이고 z만 1이기 때문에

결국 position 속성의 z값만 증가한다.

 

실행하게되면 프레임마다 1씩 변경되기 때문에 굉장히 빠른 속도록 이동하는 모습을 볼 수 있다.

 

Vector3 구조체

속성 설명
magnitude 벡터의 길이(읽기 전용)
normalized 크기가 1인 벡터, 정규화 벡터(읽기 전용)
sqrMagnitude 벡터의 길이의 제곱 (읽기 전용)
x 벡터의 x 성분(3차원 공간의 x 좌표)
벡터의 y 성분(3차원 공간의 y 좌표)
z 벡터의 z 성분(3차원 공간의 z 좌표)

 

정규화 벡터

벡터 : 크기와 방향을 나타낼 수 있는 데이터 타입, 그중 각 축의 크기가 1인 벡터를 정규화 벡터 또는 단위 벡터라고 한다

정규화 벡터는 방향만 표시하는 벡터라고 생각하면된다.

 

*표시가 되어있는 셀 부분은 꼭 기억하기

* Vector3.forward Vector3(0,0,1)
  Vector3.back Vector3(0,0,-1)
  Vector3.left Vector3(-1,0,0)
* Vector3.right Vector3(1,0,0)
* Vector3.up Vector3(0,1,0)
  Vector3.down Vector3(0,-1,0)
* Vector3.one Vector3(1,1,1)
* Vector3.zero Vector3(0,0,0)

 

유니티는 왼손 좌표계를 사용

using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    void Start()
    {
        
    }

    void Update()
    {
        float h = Input.GetAxis("Horizontal");  // -1.0f ~ 0.0f ~ +1.0f
        float v = Input.GetAxis("Vertical");    // -1.0f ~ 0.0f ~ +1.0f

        Debug.Log("h = " + h);
        Debug.Log("v = " + v);

        // Transform 컴포넌트의 position 속성값을 변경
        //transform.position += new Vector3(0, 0, 1);

        // 정규화 벡터를 사용한 코드
        transform.position += Vector3.forward * 1;


    }
}

 

추가한 Vector.forward *1 의 의미는 [전진 방향 * 속력]과 같은 의미

 

컴포넌트 캐시 처리

update 한수는 프레임마다 한 번씩 호출되는 함수로서 항상 최적화에 주의를 기울여야한다.

이동 로직은 Transform 컴포넌트의 Position 속성을 조금씩 변경하는 것으로 프레임마다 Transform 컴포넌트에 접근하는 방식은 바람직하지 않다.

 

앞서서는 update함수에 Transform 컴포넌트의 transform을 사용했는데 이를 변수에 담아두고 사용하는 방식이 미세하지만 빠르다. 그렇기에 아래와 같이 변경한다.

using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    private Transform tr;

    void Start()
    {
        tr = GetComponent<Transform>();    
    }

    void Update()
    {
        float h = Input.GetAxis("Horizontal");  // -1.0f ~ 0.0f ~ +1.0f
        float v = Input.GetAxis("Vertical");    // -1.0f ~ 0.0f ~ +1.0f

        Debug.Log("h = " + h);
        Debug.Log("v = " + v);

        // Transform 컴포넌트의 position 속성값을 변경
        //transform.position += new Vector3(0, 0, 1);

        // 정규화 벡터를 사용한 코드
        tr.position += Vector3.forward * 1;


    }
}

 

 tr = GetComponent<Transform>();

 GetComponent 는 함수명

<Transform> 는 형식(추출할 클래스)

() 인자(매개변수)

 

tr = GetComponent<Transform>();
tr = GetComponent("Transform") as Transform;
tr = (Transform)GetComponent(typeof(Transform));

 셋다 같은 뜻으로 첫 번째 방법에 사용한 제네릭(Generic) 타입은 별도의 형 변환이 필요없고 소스 코드를 간결하게 작성할 수 있다는 장점

 

Translate 함수

void Translate(Vector3 direction, [Space relativeTo]

Translate 함수를 사용하면 다음과 같이 간결하고 가독성 있는 코드로 표현할 수 있다.

 

 Translate 함수의 두번째 인자는 이동하는 게임 오브젝트가 월드 좌표 기준으로 이동할지 로컬 좌표로 이동할지 결정

아래와 같이 변경

using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    private Transform tr;

    void Start()
    {
        tr = GetComponent<Transform>();
        tr = GetComponent("Transform") as Transform;
        tr = (Transform)GetComponent(typeof(Transform));
    }

    void Update()
    {
        float h = Input.GetAxis("Horizontal");  // -1.0f ~ 0.0f ~ +1.0f
        float v = Input.GetAxis("Vertical");    // -1.0f ~ 0.0f ~ +1.0f

        Debug.Log("h = " + h);
        Debug.Log("v = " + v);

        // Transform 컴포넌트의 position 속성값을 변경
        //transform.position += new Vector3(0, 0, 1);

        // 정규화 벡터를 사용한 코드
        //tr.position += Vector3.forward * 1;

        // Translate 함수를 사용한 이동 로직
        tr.Translate(Vector3.forward * 1);


    }
}

 

Time.deltaTime

: 이전 프레임의 시작 시각부터 현재 프레임이 시작되는 시각의 차, 이전 프레임부터 현재 프레임까지 걸릴 시간의 차

 

deltaTime을 곱하지 않았을 때는 프레임당 지정한 유닛만큼 이동하고

곱하면 초당 지정한 유닛만큼 이동한다.

Update 함수에서 이동 및 회전하는 로직을 작성했다면 반드시 곱해야한다.

 

using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    private Transform tr;
    public float moveSpeed = 10.0f;

    void Start()
    {
        tr = GetComponent<Transform>();
        tr = GetComponent("Transform") as Transform;
        tr = (Transform)GetComponent(typeof(Transform));
    }

    void Update()
    {
        float h = Input.GetAxis("Horizontal");  // -1.0f ~ 0.0f ~ +1.0f
        float v = Input.GetAxis("Vertical");    // -1.0f ~ 0.0f ~ +1.0f

        Debug.Log("h = " + h);
        Debug.Log("v = " + v);

        // Transform 컴포넌트의 position 속성값을 변경
        //transform.position += new Vector3(0, 0, 1);

        // 정규화 벡터를 사용한 코드
        //tr.position += Vector3.forward * 1;

        // Translate 함수를 사용한 이동 로직
        tr.Translate(Vector3.forward * Time.deltaTime * v * moveSpeed);
    }
}

 

Vector3.forward 는 Vector3(0,0,1)의 의미인데 이 단위 벡터에 Time.deltaTime 과 속력(moveSpeed)를 곱하면 변수에 지정한 속도로 이동 

tr.Translate(이동할 방향 * Time.deltaTime * 전진/후진 변수 * 속도);

public, private 접근 제한자

접근제한자 : 외부 클래스, 멤버 변수 등의 접근을 허용하는 범위를 정

 

[접근 제한자] class 클래스_명
{

}
[접근 제한자] 변수_타입 변수명;

private Transform tr;
public float moveSpeed 10.0f;

 

public : 외부 클래스에서 접근가능

private : 동일 클래스에서만 접근 가능, 외부에서는 접근 불가능

protected : private과 동일하게 외부에서는 접근이 불가능하고, 상속으로 받은 파생 클래스에서만 접근 가능

internal : 같은 어셈블리에서만 접근 가능, 클래스의 경우 접근 제한자를 생략하면 internal이 기본 값으로 설정

 

public 으로 선언한 moveSpeed의 경우에는 인스펙터 창에서 직접 수정할 수 있다.

인스펙터 뷰에 노출된 변수의 우선순위

인스펙터 창에서 moveSpeed를 20으로 변경하고 실행하면 스크립트에 작성된 10을 무시하고 20으로 동작하게 된다

public 으로 선언하게 되면 이러한 점을 잘 고려하여 코드를 작성해야한다.

private 변수의 인스펙터 뷰 노출

private의 경우는 인스펙터 창에서 확인 할 수 없지만 Debug 창으로 변경하게 되면 맞게 들어갔는지 확인할 수 있다.

이외에도 SerializeField 속성을 사용하는 것

이 속성은 private 속성을 유지하면서 인스펙터에 노출시킬 수 있다.

[SerializeField]
private Transform tr;

 

인스펙터창에서 moveSpeed를 8로 적절하게 변경

 

벡터의 덧셈 연산

using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    [SerializeField]
    private Transform tr;
    public float moveSpeed = 10.0f;

    void Start()
    {
        // 컴포넌트를 추출해 변수에 대입
        tr = GetComponent<Transform>();
    }

    void Update()
    {
        float h = Input.GetAxis("Horizontal");  // -1.0f ~ 0.0f ~ +1.0f
        float v = Input.GetAxis("Vertical");    // -1.0f ~ 0.0f ~ +1.0f

        Debug.Log("h = " + h);
        Debug.Log("v = " + v);

        // 전후좌우 이동할 방향 벡터 계산
        Vector3 moveDir = (Vector3.forward * v) + (Vector3.right * h);

        // Translate 함수를 사용한 이동 로직
        tr.Translate(moveDir * Time.deltaTime * moveSpeed);
    }
}

 

이때 두개의 키를 동시에 눌러 대각선으로도 이동해보면 속도가 살짝 빨라진다.

피타고라스 정의에 의해 속도가 1이 아니라 1.414..임을 알 수 있다. 대각선으로 이동할 때 속도가 빨라진 원인

 

따라서 길이가 1인 벡터로 변환해 방향 성분만 사용해야함

 

길이가 1인 벡터를 단위 벡터 또는 정규화 벡터라고한다(Normalized Vector)

using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    [SerializeField]
    private Transform tr;
    public float moveSpeed = 10.0f;

    void Start()
    {
        // 컴포넌트를 추출해 변수에 대입
        tr = GetComponent<Transform>();
    }

    void Update()
    {
        float h = Input.GetAxis("Horizontal");  // -1.0f ~ 0.0f ~ +1.0f
        float v = Input.GetAxis("Vertical");    // -1.0f ~ 0.0f ~ +1.0f

        Debug.Log("h = " + h);
        Debug.Log("v = " + v);

        // 전후좌우 이동할 방향 벡터 계산
        Vector3 moveDir = (Vector3.forward * v) + (Vector3.right * h);

        // Translate 함수를 사용한 이동 로직
        tr.Translate(moveDir.normalized * Time.deltaTime * moveSpeed);
    }
}

위와 같이 코드를 변경한다.

 

반응형