프리팹과 오브젝트
프리팹과 오브젝트의 차이점은?
Hierarchy 창의 오브젝트들이 인스턴스라면, Project 창의 Prefab은 클래스라고 할 수 있다.
즉, 프로젝트 창의 Prefab을 끌어다 하이어라키 창에 놓으면 인스턴스화 되며 오브젝트가 생성된다.
- 마우스 오른쪽 클릭후 Create Empty Parent 를 누르면 부모 오브젝트가 생성된다.
- 오브젝트에서 Select를 누르면 원래의 프리팹이 위치한 장소로 이동하여 Project창에 띄워준다.
- 오브젝트에서 기존 프리팹과 달라진 변경사항이 있으면 Overrides에 Apply All이 활성화되고, 이를 클릭하면 프리팹과 다른 인스턴스들에도 변경사항이 동일하게 적용된다.
🎓 새로운 지식 :: 메인 카메라 설정
카메라 오브젝트의 Tag를 MainCamera로 설정하면 해당 카메라가 게임 플레이 화면의 메인 카메라로 사용된다.
캐릭터 이동 기능 추가하기
- 먼저, 멤버변수로 이동속도, 회전속도, 점프력, 움직일캐릭터 등을 선언한다.
public float speed = 20; //이동속력
public float rotSpeed; //회전속력
public float jumpPower; //점프력
public GameObject CharacterObject; //캐릭터
private bool isGrounded = true; // 점프시 땅을 밟았는가?를 판단하기 위해 선언.
Vector3 direction = Vector3.zero; //위치를 저장하는 멤버변수. 벡터를 (0,0,0)으로 초기화
Vector3 rotDirection = Vector3.zero; //회전각도를 저장하는 벡터
- public으로 멤버변수 작성 시 ⇒ 에디터에 해당 항목들이 표시됨
- GameObject 칸에는 원하는 오브젝트를 끌어다 바인딩 하면 된다.
void Update()
{
direction = Vector3.zero;
rotDirection = Vector3.zero;
// 매 업데이트 마다 direction, rotDirection을 초기화 해주어 그 다음 인풋이 적용되도록 한다.
if (Input.GetKey(KeyCode.W)) ...
if (Input.GetKey(KeyCode.S)) ...
if (Input.GetKey(KeyCode.A)) ...
if (Input.GetKey(KeyCode.D)) ...
//벡터의 정규화
direction.Normalize();
rotDirection.Normalize();
}
키보드 키 입력 받기
if (Input.GetKey(KeyCode.W))
{
direction += CharacterObject.transform.forward;
}
transform.forward : 오브젝트가 바라보고 있는 방향을 기준으로 앞 방향(Blue axis = Z축 방향)
벡터의 정규화 (normalize)
벡터의 길이를 정규화 하여 길이가 1인 벡터를 만드는 로직.
예를 들어 x방향으로 1, y방향으로 1이 주어졌을 때 정규화를 하지 않으면 대각선 방향으로 나아갈 때 1보더 더 먼 거리를 이동하게 된다. 정규화를 거치면 대각선으로 이동할 때에도 1만큼만 나아가게 할 수 있다.
Vector3 direction = Vector3.zero;
...
direction.Normalize();
캐릭터 이동하기
void FixedUpdate()
{
// current, target까지 speed * Time.fixedDeltaTime만큼 이동한 벡터의 위치를 반환한다.
Vector3 nextPosition = Vector3.MoveTowards(transform.position,
transform.position + direction * 1000,
speed * Time.fixedDeltaTime);
// nextPosition으로 물리이동한다.
GetComponent<Rigidbody>().MovePosition(nextPosition);
}
현재 위치 = transform.position
목표 위치 = transform.position + direction*1000
이동 거리 = speed * Time.fixedDeltaTime (속력*시간)
🎓 새로운 지식 :: Update()와 FixedUpdate()의 차이점
Update()와 Time.deltaTime
Update()는 매 프레임마다 한번 씩 실행된다. 따라서, fps가 높아질수록 더 많이 실행하게 된다.
deltaTime은 한 프레임과 그 다음 프레임이 출력되는 사이의 시간을 의미한다.
deltaTime = 1/frameRate
30fps일 때 Update()는 1초동안 30번 실행되고, 이 때 deltaTime은 1/30초가 된다. 60fps라면 Update()는 1초동안 60회 실행, deltaTime은 1/60초다.
FixedUpdate()와 Time.fixedDeltaTime
FixedUpdate()는 정해진 물리 시간마다 한 번 실행되며, Time.fixedDeltaTime은 그 기준이 되는 물리 시간이다.
따라서 Update()와는 달리 FixedUpdate()는 fps와 관계없이 상수시간마다 동작한다.
Edit > ProjectSetting > Time 탭에서 fixed Timestep을 0.02로 설정하였다면 Time.fixedDeltaTime은 현실 시간의 0.02초이며, FixedUpdate() 또한 0.02초마다 한 번씩 실행된다.
💡질문 :: 사용하려는 변수가 지역변수와 이름이 같은 경우 어떻게 해야하나요?
⇒ this.~ 를 사용해줍니다.
public int name;
void Func(int name)
{
this.name = name;
name = 1; //지역변수에 1이 들어간다.
this.name = 1; //public int name에 1이 들어간다.
}
+) 강사님 코멘트 : 다만, 같은 이름의 변수들을 사용하는 것은 권장하지 않습니다. 규칙을 정하여 _name처럼 사용하는 등 다른 방식을 취하는 것이 좋습니다.
예시 : public int name;
void Func2(int _name)
{ name = _name; }
캐릭터 회전 기능 추가하기
키보드 키 입력받기
void Update(){
...
if (Input.GetKey(KeyCode.A))
{
rotDirection += -CharacterObject.transform.right;
}
if (Input.GetKey(KeyCode.A))
{
rotDirection += CharacterObject.transform.right;
}
}
transform.right : 오브젝트가 바라보고 있는 방향을 기준으로 오른쪽(Red axis = X축 방향)
캐릭터 회전하기
void FixedUpdate(){
...
if (rotDirection.sqrMagnitude > 0.001f) //sqrMagnitude가 유효한지(0이 아닌지) 확인
{
// newRot을 계산한다.
Quaternion newRot = Quaternion.RotateTowards(CharacterObject.transform.rotation,
Quaternion.LookRotation(rotDirection),
Time.fixedDeltaTime * rotSpeed);
CharacterObject.transform.rotation = newRot;
}
}
Quaternion.RotateTowars : From -> To 로 회전할 값을 정함.
현재 내가 보고 있는 방향(From) : CharacterObject.transform.rotation
내가 바라볼 방향(To) : Quaternion.LookRotation(direction)
회전하는 양 : Time.FixedDeltaTime*rotSpeed
💡질문 :: Quaternion이 뭔가요?
⇒ 인스펙터 창에는 3축의 회전을 직관적으로 이해할 수 있도록 오일러각(x,y,z)이 쓰여있지만 실제로 이를 계산하기 위해서 유니티에서는 쿼터니언을 사용합니다. 쿼터니언은 4개의 요소(x,y,z,w)를 통해 회전을 표현하며 이에 대해서는 게임 수학을 다룰 때 다시 설명하겠습니다.
점프 기능 추가하기
땅에 Ground 태그 추가하기
- 아무 오브젝트나 클릭하여 Inspector 창 > Tag > Add Tag 를 눌러 새 태그를 추가한다.
- 태그 리스트에 Ground 라는 이름의 태그를 추가해준다.
- Collider 컴포넌트를 가진 오브젝트의 태그를 Ground로 설정해준다.
키 입력받기 & 점프 한번만 뛰게 만들기
private bool isGrounded = true;
...
void Update(){
...
if (Input.GetKeyDown(KeyCode.Space) && isGrounded) //땅에 닿아있을때만 점프
{
GetComponent<Rigidbody>().AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
isGrounded = false; //땅에서 떨어지면 false
}
...
}
스페이스바가 입력되었으며 땅에 착지한 상태일때, Rigidbody에 AddForce()함수를 통해 물리력을 가한다.
리지드바디에 Vector3.up * jumpPower만큼 ForceMode.Impulse효과를 준다.
ForceMode 힘이 적용되는 방식 중 Impulse는 정지 상태서 이동을 시작하려 할 때 순간적으로 힘을 가하여 캐릭터가 점프하는 모습을 만들어낸다.
점프한 상태에서는 다시 점프할 수 없도록 isGrounded를 false로 바꾸어준다.
참고하면 좋은 자료👍 :: ForceMode
https://blog.naver.com/gold_metal/221486016593
땅과 충돌하면 착지 상태로 인식하기
오브젝트가 무언가와 부딪히면 collision이 발생하여 이를 인식할 수 있다. 캐릭터가 점프했다가 내려와 Ground 태그를 가진 땅에 닿게 되면 이를 인식하여 isGrounded를 다시 true로 바꾸어주면 된다.
- OnCollisionEnter(Collision collision) : 이 오브젝트가 다른 콜라이더와 충돌을 일으켰을 때, 충돌 판정이 일어나며 이 오브젝트와 충돌된 객체가 collision에 담겨져서 반환된다.
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Ground")) //충돌을 일으킨 콜라이더가 Ground인지?
{
isGrounded = true; //충돌 : Ground에 닿았으면 true.
}
}
+ 강사님 코멘트 : collision.gameObject.CompareTag("Ground")) 와 collision.gameObject.tag == "Ground" 는 동일하지만, 개인적으로 명확한 구분을 위해 CompareTag()를 활용하는 것을 더 추천합니다.
🎓 새로운 지식
GetComponent<Rigidbody>() ... 를 너무 자주 사용하는 것 같을 때에는 변수화를 통해 이를 최적화 할 수 있습니다.
Start(){ Rigidbody rigidbody = GetComponent<Rigidbody>(); }
Update() { rigidbody.AddForce.... } 와 같이 활용할 수 있습니다.
이러한 방법을 캐싱이라고 하며 최적화(캐싱, 메모리콜 ..등) 에 대해서는 다음에 최적화를 다룰 때 설명하겠습니다.
'UNITY > 유니티게임스쿨' 카테고리의 다른 글
[유니티게임스쿨 TIL] 유니티 기본 :: 캐릭터 애니메이션 설정하기, 트리거 활용하기 (0) | 2024.06.05 |
---|---|
[유니티게임스쿨 TIL] C# 기초 문법 이해 :: 파일 입출력, Collection, LINQ (0) | 2024.06.04 |
[유니티게임스쿨 TIL] 유니티 기본 :: 유니티 설치와 인터페이스 이해 (0) | 2024.05.31 |
[유니티게임스쿨 TIL] C# 기초 문법 이해 :: 재귀 함수, 연산자, 클래스와 인터페이스 (0) | 2024.05.30 |
[유니티게임스쿨 TIL] C# 기초 문법 이해 :: 데이터 타입과 반복문 (0) | 2024.05.29 |