#유니티 #TransFrom #위치 이동 구현

sejun-Lee·2025년 4월 16일

UnityEngine

목록 보기
3/12

Transform


  • Transform 컴포넌트는 오브젝트의 위치, 회전각, 크기(배율)에 대한 데이터를 가지고 있습니다.
  • 유니티에서는 더 빠른 연산을 위해 오브젝트의 위치, 회전, 크기에 대한 연산을 4x4 행렬로 구현합니다.
  • 추가로 자신이 가진 데이터를 쉽게 컨트롤 할 수 있는 지원 함수들이 내장되어 있습니다.
Position
유니티에서 오브젝트의 위치 정보는 x, y, z 3축으로 구성됩니다. 이는 유니티에서 Vector3 클래스를 통해 간편하게 다룰 수 있으며, 이를 사용해 게임 오브젝트의 위치 이동을 구현할 수 있습니다.
// Position을 직접적으로 변경하는 방법
Transform.position = new Vector3(x, y, z);

// Translate 함수를 이용한 축(x, y, z) 기준의 위치이동 방법
Transform.Translate(Direction * Speed);

// 선형 보간을 이용해 거리에 따라 빠르게 이동하며 천천히 감속하는 방법
Transform.position = Vector3.Lerp(StartPosition, EndPosition, Interpolation)

// 목표지점을 향해 일정한 속도로 이동하는 방법
Transform.position = Vector3.MoveTowards(transform.position, Target.Position, Speed);

Rotation
인스펙터의 Transform 컴포넌트에서는 회전값이 포지션과 동일하게 x, y, z축으로 보여지지만 내부 동작은 4원소를 사용하는 쿼터니언을 통해 이뤄집니다. 이는 연산속도가 빠르고 3축을 사용했을 때의 짐벌락 현상을 방지하는 데 효과적입니다.
쿼터니언은 연산적으로 효과적이지만, 유니티 입문 시점에서는 이해하기 복잡한 개념입니다. 이 때문에 보다 직관적인 3축으로 컨트롤 할 수 있도록 Quaternion클래스의 Euler를 사용합니다.
// rotation에 값을 직접 지정해 회전하는 방법
Transform.rotation = Quaternion.Euler(x, y, z);

// Rotate 함수를 이용한 회전
Transform.Rotate(Vector3.up, Speed);

// RotateAround를 이용한 중심축 기준으로 회전
Transform.RotateAround(Target.position, Vector3.up, Speed);

// LookAt을 이용한 대상을 향해 회전시키기
Transform.LookAt(Target.position);

Tip : Quaternion (펼치기/접기)

Scale
직관적인 이름 그대로 게임 오브젝트의 크기를 비율로 지정할 수 있는 요소입니다
// Scale 조절
Transform.localScale = new Vector3(x, y, z);

// lossyScale은 부모 오브젝트의 스케일에 상관 없이 자신 고유의 Scale 수치를 반환
Transform.lossyScale

World Space VS Local Space

  • 게임 내에서 모든 오브젝트는 Transform 컴포넌트를 가집니다.
  • 형체가 없는 오브젝트라 할 지라도, 위치를 지니고 있다는 뜻이죠 이때, 게임 환경 내의 절대 좌표와 상속 관계에서의 상대 좌표를 사용할 수 있습니다.
  • 우리는 이를 월드 스페이스(World Space)와 로컬 스페이스(Local Space)라고 부릅니다.

월드 스페이스 (World Space)

  • 월드 스페이스는 게임 환경 내의 고정된 글로벌 좌표계를 의미하며, 절대 좌표라고도 표현할 수 있습니다.
  • 이 좌표계는 게임의 시작부터 끝까지 변하지 않으며, 모든 오브젝트들이 이 월드 스페이스 내에서 위치를 가지게 됩니다.
  • 월드 스페이스에서의 오브젝트의 위치, 회전, 크기는 월드 포지션(World Position), 월드 회전(World Rotation), 월드 스케일(World Scale)로 표현됩니다.

로컬 스페이스 (Local Space)

  • 로컬 스페이스는 오브젝트나 컴포넌트의 개별적인 좌표계를 의미하며, 상대 좌표라고도 표현할 수 있습니다.
  • 특히 부모-자식 관계에서 중요하며, 자식 오브젝트의 로컬 스페이스는 부모 오브젝트에 대해 상대적입니다.
  1. Ex> 자식 오브젝트의 로컬 포지션이 (0, 0, 0)이라면, 이는 부모 오브젝트의 위치와 동일하다는 것을 의미합니다.
  2. 로컬 스페이스에서의 오브젝트의 위치, 회전, 크기는 로컬 포지션(Local Position), 로컬 회전(Local Rotation), 로컬 스케일(Local Scale)로 표현됩니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TransformTester : MonoBehaviour
{
    
        트랜스폼 (Transform)
        게임  오브젝트의 위치, 회전, 크기를 정의하는 컴포넌트
        게임오브젝트의 부모-자식 상태를 자장하는 컴포넌트
        게임오브젝트는 반드시 하나의 트랜스폼 컨포넌트를 가지고 있으며, 추가 & 삭제할 수 없다.
     

    public Vector3 targetPos;
    [SerializeField] float moveSpeed; // 이동 속도
    [SerializeField] float rotateSpeed; // 회전 속도
    [SerializeField] Transform turret;


    [SerializeField] Transform source;
    [SerializeField] Transform target;
    [Range(0f, 1f)]
    [SerializeField] float rate; // 보간 비율



    private void Update()
    {
        #region Position
        // 1. 벡터를 이용한 위치 설정
         transform.position = new Vector3(1, 2, 3); // 트랜스폼의 위치를 (1, 2, 3)으로 설정

        if (Input.GetKeyDown(KeyCode.Space))
        {
            transform.position = targetPos; // 트랜스폼의 위치를 targetPos로 설정
        }

         2. 트랜스레이트를 이용한 위치 설정 -> 원하는 방향으로 이동시키기
        if (Input.GetKeyDown(KeyCode.Space))
        {
            transform.Translate(Vector3.forward); // 트랜스폼을 forward-> 앞으로 이동
        }

        transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
        // 1프레임당 걸린시간 -> 단위시간 : 델타타임
        // 1프레임당 걸린시간 = Time.deltaTime

        // 3. 목적지로 이동시키기
        transform.position =  Vector3.MoveTowards(transform.position, target.position, moveSpeed * Time.deltaTime);
        //Towards : 방향을 설정하는 메서드 -> ~을 향해서

        // 4. 보간해서 이동시키기
        transform.position = Vector3.Lerp(transform.position, target.position, rate);
        // linearly interpolate 선형보간
        #endregion


        #region Rotation
        // 3축을 사용했을 때, 짐벌락 현상이 발생할 수 있다. Quaternion을 사용하자
        // 1. 회전 직접 지정 : Euler를 이용하여 Quaternion으로 변환하여 사용 권장
        transform.rotation = Quaternion.Euler(0, 60, 0);

        // 1-1 오일러를 쿼터니언으로 변환 -> Quaternion.Euler
        Quaternion a = Quaternion.Euler(0, 60, 0);
        // 1-2. 쿼터니언을 오일러로 변환   -> .eulerAngles
        Vector3 b = transform.rotation.eulerAngles;
        // 1-3. 방향벡터를 쿼터니언으로 변환
        Quaternion c = Quaternion.LookRotation(Vector3.right);
        // 1-4. 현재 회전을 방향백터로 변환
        Vector3 d = transform.right; // 오른쪽 방향벡터

        // 2. 축을 기준으로 회전
        transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime); // y축을 기준으로 회전
        transform.Rotate(Vector3.forward, rotateSpeed * Time.deltaTime); // z축을 기준으로 회전

        // 3. 지점을 기준으로 회전
        transform.RotateAround(target.position, Vector3.up, rotateSpeed * Time.deltaTime); // y축을 기준으로 회전

        // 4. 지점을 바라보도록 회전
        transform.LookAt(target.position); // target을 바라보도록 회전

        // <target 방향만 바라 봄. 위아래는 무시함>
        Vector3 t = target.position;
        t.y = 0f;
        transform.LookAt(t); 

        //<target 방향을 바라보는 속도 조절>
        Vector3 targetDir = (target.position - transform.position).normalized; // target 방향벡터
        Quaternion targetRot = Quaternion.LookRotation(targetDir); // target 방향벡터를 쿼터니언으로 변환
        transform.rotation = Quaternion.Lerp(transform.rotation, targetRot, rotateSpeed * Time.deltaTime); // 현재 회전과 목표 회전을 보간하여 회전
        #endregion


        // 월드기준
        Vector3 worldPostion = transform.position; // 월드좌표계에서의 위치
        Quaternion worldRotation = transform.rotation; // 월드좌표계에서의 회전

        // 로컬기준
        Vector3 localPosition = transform.localPosition; // 로컬좌표계에서의 위치
        Quaternion localRotation = transform.localRotation; // 로컬좌표계에서의 회전

        turret.rotation = Quaternion.identity;  

        transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime, Space.World); // Space.World -> 월드좌표계에서 앞으로 이동

        transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime, Space.World); // Space.World -> 월드좌표계에서 y축을 기준으로 회전

    }
}

<위치 이동 구현>

  • 유니티는 게임엔진이라는 타이틀에 걸맞게 여러 기본 지원기능을 내장하고 있습니다.
  • Input Class를 통해 여러 키 여부를 알수 있고, 이를 통해 오브젝트의 포지션을 변경해 주는 것이 우리의 목표입니다.

GetKey

  • 단일 키 입력에 대한 여부를 반환받기 위한 함수입니다.
  • 키가 눌리는 판정에 따라 세 가지 bool 타입 반환 함수를 적절하게 사용합니다.
Input.GetKey()      // 키 입력이 진행되는 동안 true를 반환합니다. 
Input.GetKeyDown()  // 키 입력이 시작되는 순간 1회만 true를 반환합니다.
Input.GetKeyUp()    // 키 입력이 종료되는 순간 1회만 true를 반환합니다.

GetAxis

  • 유니티에서 제공하는 Input Manager에 대응하는 코드입니다.
  • 유니티 좌표상의 X(Horizontal), Z(Vertical)축에 해당하는 입력 데이터이며, 미 입력시 0, 방향과 입력에 따라 -1.0 ~ 1.0 사이의 float 타입을 반환합니다.
  • 이 함수는 게임패드(조이스틱)와도 연동되는 코드이며, 숙련된다면 유용하게 사용할 수 있습니다.

GetAxisRaw

  • GetAxis와 마찬가지로 Input Manager에 대응하고 유니티 좌표상의 X, Z축에 해당하는 입력 데이터를 -1, 0, 1로 반환합니다.
  • 이는 키보드 입력에 대한 이동 구현에 적합한 함수입니다.

public class InputTester : MonoBehaviour
{
    public float result; // 결과값을 저장할 변수

    private void Update()
    {
        #region Device
        // <Device>
         특정한 장치를 기준으로 입력 감지.
         특정한 장치의  입력을 감지하기 위해서는 Input.GetKey()를 사용한다.

         1. 키보드 입력 && 게임패드 입력
        if (Input.GetKey(KeyCode.Space))     // 키 입력이 진행되는 동안 true
        {
            Debug.Log("Get Key!");
        }
        if (Input.GetKeyDown(KeyCode.Space))    // 눌렀을 때 한번만 true
        {
            Debug.Log("Get Key Down!");
        }
        if (Input.GetKeyUp(KeyCode.Space))      // 눌렀다 떼었을 때 한번만 true
        {
            Debug.Log("Get Key Up!");
        }

        // 2. 마우스 입력
        if (Input.GetMouseButton(0)) // 0 왼쪽 버튼 클릭
        {
            Debug.Log("Left Mouse Button Clicked");
        }
        if (Input.GetMouseButtonDown(1)) // 1 오른쪽 버튼 클릭
        {
            Debug.Log("Right Mouse Button Clicked");
        }
        if (Input.GetMouseButtonUp(2)) // 2 가운데 버튼 클릭
        {
            Debug.Log("Middle Mouse Button Released");
        }
        #endregion       


         <InputManager> -> Edit > Project Settings > Input Manager
         여러 장치의 입력을 입력매니저에 이름과 입력을 정의
         입력매니저의 이름으로 정의한 입력의 변경사항을 확인
         유니티 에디터의 Edit > Project Settings > Input Manager에서 관리
         단, 유니티 초창기의 방식이기 때문에 키도드, 마우스, 조이스틱에 대한 장치만을 고려함

        <InputManager>방식으로 입력을 감지
        if (Input.GetButton("Fire1")) // 입력매니저에 정의된 이름으로 입력을 감지
        {
            Debug.Log("Fire1 버튼 눌림");
        }

        result = Input.GetAxis("Horizontal"); 
        result = Input.GetAxisRaw("Horizontal");

        float axis = 0;
        if(Input.GetKey(KeyCode.LeftArrow))
        {
            axis = -1;
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            axis = +1;
        }
    }
}

public class TankMover : MonoBehaviour
{
    [SerializeField] float moveSpeed;// 이동 속도
    [SerializeField] float rotateSpeed; // 회전 속도

    private void Update()
    {
        Move(); // 이동
        Rotate(); // 회전
    }

    private void Move()
    {
        float input = Input.GetAxis("Vertical"); // W, S키 입력
        transform.Translate(Vector3.forward * moveSpeed * input * Time.deltaTime); // 전진 후진
        //transform.Translate(Vector3.forward * moveSpeed * input * Time.deltaTime, Space.World); // Space.World -> 월드를 기준으로 전진 후진
    }

    private void Rotate()
    {
        float input = Input.GetAxis("Horizontal"); // A, D키 입력
        transform.Rotate(Vector3.up * rotateSpeed * input * Time.deltaTime); // 좌우 회전
    }
}
profile
초보 개발자

0개의 댓글