- 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
- 게임 내에서 모든 오브젝트는 Transform 컴포넌트를 가집니다.
- 형체가 없는 오브젝트라 할 지라도, 위치를 지니고 있다는 뜻이죠 이때, 게임 환경 내의 절대 좌표와 상속 관계에서의 상대 좌표를 사용할 수 있습니다.
- 우리는 이를 월드 스페이스(World Space)와 로컬 스페이스(Local Space)라고 부릅니다.
월드 스페이스 (World Space)
- 월드 스페이스는 게임 환경 내의 고정된 글로벌 좌표계를 의미하며, 절대 좌표라고도 표현할 수 있습니다.
- 이 좌표계는 게임의 시작부터 끝까지 변하지 않으며, 모든 오브젝트들이 이 월드 스페이스 내에서 위치를 가지게 됩니다.
- 월드 스페이스에서의 오브젝트의 위치, 회전, 크기는 월드 포지션(World Position), 월드 회전(World Rotation), 월드 스케일(World Scale)로 표현됩니다.
로컬 스페이스 (Local Space)
- 로컬 스페이스는 오브젝트나 컴포넌트의 개별적인 좌표계를 의미하며, 상대 좌표라고도 표현할 수 있습니다.
- 특히 부모-자식 관계에서 중요하며, 자식 오브젝트의 로컬 스페이스는 부모 오브젝트에 대해 상대적입니다.
- Ex> 자식 오브젝트의 로컬 포지션이 (0, 0, 0)이라면, 이는 부모 오브젝트의 위치와 동일하다는 것을 의미합니다.
- 로컬 스페이스에서의 오브젝트의 위치, 회전, 크기는 로컬 포지션(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); // 좌우 회전
}
}