<Transform: 객체 이동시키기>
- Asset 다운 받기
1. 우선 asset store에서 내가 필요로하는 asset을 다운받는다.
2. 다운받은 asset의 구성은
- Model: 움직이는 객체 (영화에서 나오는 배우같은 역할)
- Materials: 재질 (옷을 입힌다)
- Animation: 객체의 세세한 동작(코드로 구현할 수 없는 부분에 대한 애니메이션 효과)
=> 해당 Model을 Scene에
해당 모델도 이전에 배웠던 것과 마찬가지로 여러 component로 이뤄짐
<객체 이동>
- 객체의 이동: 기본
1. 위에서 다운 받은 Unity chan의 Model을 객체로 놓고 PlayerController script 파일을 만든다.
2. 위의 객체 이름을 Player라고 지정한 후 component에 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에 접근할 수 있도록함.
=> 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 구조체를 만들어주자
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 |