Unity Chapter 6-5. 어메이징 볼링 게임 만들기 : 카메라 추적
카테고리: Unity Lesson 1
태그: Unity Game Engine
인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!
Chapter 6. 어메이징 볼링 게임 만들기
🔔 카메라 추적
Main Camera
- 회전하는 포신을 쫓아갔으면 좋겠다.
- 날아가는 Ball을 쫓아갔으면 좋겠다.
- 줌인 줌아웃 자유롭게 됐으면 좋겠다.
Main Camera 오브젝트
- Projection 👉 Orthographic 값으로 바꿔 준다.
- Perpective
- 하얀색 투영 선이 대각선이며 한 점으로 모인다.
- 멀리 있는건 작게 보이고 가까이 있는건 크게 보인다. 원근감 O
- Orthographic
- 하얀색 투영 선들이 평행하다.
- 멀리 있으나 가까이 있으나 크기가 똑같이 보인다. 원근감 X
- 2D 에서 많이 사용된다.
- 3D 에서 사용하면 ‘길건너 친구들’ 게임 처럼 3D면서도 원근감이 없는 독특한 시점이 연출 된다.
- Perpective
- 위치 0, 5, 0 / 회전 40, -60, 0 으로 해주자.
Main Camera
는 기본적으로 비추는 물체에 딱 붙어버린다.- 이렇게 딱 붙으면 오히려 물체가 화면 전체를 차지해서 제대로 안보임
Main Camera
와 비추려는 오브젝트의 거리를 좀 벌려주어야 한다.
카메라가 비추려는 오브젝트와 카메라의 거리를 벌린 체로 추적
문제 점
씬 창 말고 실제 게임 화면을 담는 Main Camera
는 기본적으로 촬영하려는 오브젝트에 딱 붙어버린다. 이에 따라 해당 오브젝트가 카메라 화면을 다 차지해버려 오브젝트가 제대로 보이지 않는 현상이 발생 함. 얼굴에 밀착해서 촬영하면 피부만 보이지 오히려 이목구비는 안보이는 것처럼.. 😂 추적을 한다는 것은 카메라 위치를 목적지 위치와 일치하게끔 변화시켜 가는 과정인데 나중에 추적 완료해서 카메라 위치 = 목적지 위치
가 되버린다면 타겟 오브젝트가 카메라 화면을 다 차지해버리니까 … !
해결 방안
Main Camera
와 추적 촬영하려는오브젝트
는 일정한 거리가 벌려져야 한다.
- 우선
Main Camera
를Rig
의 자식으로서 넣어준다. Main Camera
는Rig
와 일정 거리를 벌린 체 자식으로서 부모인Rig
을 따라다니고- 일정거리는
Main Camera
의 좌표를 수정해주면 된다.
- 이때의 좌표는 부모인
Rig
로부터의 Local 좌표
- 일정거리는
Rig
은 카메라가 진짜 비추려는오브젝트
위치를 추적하여 따라가게 한다.
- 결론적으로
Main Camera
는 투명하고 빈 오브젝트인Rig
따라다니며 진짜 비추려는오브젝트
를 일정한 거리를 유지한 체 추적하는 모양새가 된다. 문제 해결! Rig
이란 배의 추를 말한다. 배 밑에서 배에 끌려 다니는 추.Main Camera
는 비추려는 오브젝트를 따라다니는게 아니라 오브젝트(배)를 따라다니는 `Rig` 오브젝트에 일정한 거리를 벌린체로 붙어서 따라다니게 할 것임- 그럼
Main Camera
를 추적하려는 오브젝트의 자식으로서 넣어주고 로컬 좌표를 수정하여 추적하려는오브젝트
와 일정한 거리를 유지한 체로 추적하면 되는 것 아닌가?- 라고 강의를 들으며 생각 했지만 이러면 추적하는 대상이 바뀔 때 마다 부모-자식 관계 해제해야하고 또 새로운 대상의 자식으로 넣어줘야하고 이런 과정을 반복해야하니 번거로워서 그런 것 같다고 추측해본다. 😐
Rig 과 Main Camera 오브젝트 설정
Rig
라는 이름의 빈 오브젝트 생성- 위치는 원점, 회전은 40, -60, 0
Main Camera
와 회전 값을 똑같이 했다.
Main Camera
를Rig
의 자식으로 넣어준다.- 카메라는
Rig
의 자식이므로Rig
가 타겟 오브젝트 위치로 점점 이동할 때마다 카메라도 같이Rig
를 따라다니게 된다.
- 카메라는
Main Camera
의 위치를 (0, 0, -65)로 바꿔준다. ✨- 이때의 위치는 Local 이므로 부오니
Rig
오브젝트로부터의 거리를 말한다. Rig
로부터 z 방향으로 -65만큼 떨어 뜨려준다는 것.- ✨✨
Rig
로부터 일정 거리를 유지한 체 따라다니게 된다.✨✨
- 이때의 위치는 Local 이므로 부오니
Rig
오브젝트에 📜CamFollow.cs 스크립트를 추가해준다.
📜CamFollow.cs
Rig 오브젝트가 하는 일
- 원하는 오브젝트를 트랙킹
- 각각의 상태에 따라 줌아웃 줌인 상태를 다르게 해줄 것.
- ex) 포탄을 발사할 땐 맵 전체를 보도록 줌 아웃을 해줄 것.
3가지 상태 (카메라 상태 종류)
- 라운드를 대기하는 상태 :
Idle
- 줌 크기 14.5로 크게!
- 포탄 발사를 대기하는 상태 :
Ready
- 줌 크기 5로 작게!
- 발사된 포탄을 추적하는 상태 :
Tracking
- 줌 크기 10으로 적당히!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamFollow : MonoBehaviour
{
public enum State
{
Idle, Ready, Tracking
}
private State state
{
set
{
switch(value)
{
case State.Idle:
targetZoomSize = roundReadyZoomSize;
break;
case State.Ready:
targetZoomSize = readyShotZoomSize;
break;
case State.Tracking:
targetZoomSize = trackingZoomSize;
break;
}
}
}
private Transform target;
public float smoothTime = 0.2f;
private Vector3 lastMovingVelocity;
private Vector3 targetPosition;
private Camera cam;
private float targetZoomSize = 5f;
private const float roundReadyZoomSize = 14.5f;
private const float readyShotZoomSize = 5f;
private const float trackingZoomSize = 10f;
private float lastZoomSpeed;
void Awake()
{
cam = GetComponentInChildren<Camera>();
state = State.Idle;
}
private void Move()
{
targetPosition = target.transform.position;
Vector3 smoothPosition = Vector3.SmoothDamp(transform.position, targetPosition, ref lastMovingVelocity, smoothTime);
transform.position = smoothPosition;
}
private void Zoom()
{
float smoothZoomSize = Mathf.SmoothDamp(cam.orthographicSize, targetZoomSize, ref lastZoomSpeed, smoothTime);
cam.orthographicSize = smoothZoomSize;
}
private void FixedUpdate()
{
if (target != null)
{
Move();
Zoom();
}
}
public void Reset()
{
state = State.Idle;
}
public void SetTarget(Transform newTarget, State newState)
{
target = newTarget;
state = newState;
}
}
이 📜CamFollow.cs 스크립트를
Rig
오브젝트에 붙이더라도Main Camera
가Rig
의 자식 오브젝트이기 때문에Main Camera
도 📜CamFollow.cs의 내용에 따라 동작하게 된다.
🚍 변수
- private State state
- 겉으로 봐도 함수 같고 내부도 함수처럼 동작하지만 변수다.
- 이와 같은 C# 문법을
프로퍼티
라고 한다. 이에 대해 추후 포스팅 할 것.
- 이와 같은 C# 문법을
- state 에 대입 되는 값은 state가 아닌 switch문의 value에 대응 될 것이다.
- 3가지 상태에 따라 줌 사이즈를 다르게 설정한다.
- 겉으로 봐도 함수 같고 내부도 함수처럼 동작하지만 변수다.
- private Transform target;
- 카메라가 추적할 오브젝트를 Transform 타입으로서 가져온다.
- private이기 때문에 GetComponent를 사용해 가져올 수도 있지만 setter 함수인
setTarget
함수를 외부에서 호출하여 target 오브젝트를 지정하게 할 것이다.
- public float smoothTime = 0.2f;
Rig
(카메라)오브젝트가 추적할 오브젝트를 따라 붙을 때까지 1프레임당 0.2초 걸리게끔. 바로 붙지 않고 스무스하게 붙기 위하여.
- 추적
- private Vector3 lastMovingVelocity;
SmoothDamp
함수에 매개변수로 넘겨야 하기 때문에 선언.- 바로 전 마지막 프레임에서 어떤 속도로 추적(이동)했는지를 알아야 하기 때문에 선언
- private Vector3 targetPosition;
- 타겟 오브젝트의 위치
- private Vector3 lastMovingVelocity;
- 줌
- private Camera cam;
Main Camera
의 size값, 즉 확대 배율을 가져오기 위해서
- private float targetZoomSize = 5f;
- 줌 사이즈
- 초기값 5. 게임 시작하고 입력전엔 기본 배율 5
- 상태 별 줌 사이즈. const 상수로 선언
- State.Idle 👉 roundReadyZoomSize
- State.Ready 👉 readyShotZoomSize
- State.Tracking 👉 trackingZoomSize
- private float lastZoomSpeed;
SmoothDamp
함수에 매개변수로 넘겨야 하기 때문에 선언.- 바로 전 마지막 프레임에서 어떤 속도로 줌 했는지를 알아야 하기 때문에 선언
- private Camera cam;
🚍 함수
- void Awake()
- Start()보다 더 빨리 실행됨
- cam = GetComponentInChildren<Camera>();
Rig
의 자식인Main Camera
오브젝트에서 Camera 컴포넌트를 가져 온다.- size (확대 배율)값을 알기 위해서
- cam은 private이기 때문에 이렇게 GetComponentInChildren로 가져온다.
- state = State.Idle;
프로퍼티
이기 때문에 state에 State.Idle 값이 그대로 대입되는 것이 아니라 State.Idle은 switch문의 value 값으로 들어가게 된다.- targetZoomSize = roundReadyZoomSize; 가 실행된다.
- private void Move()
- 추적
Rig
오브젝트가 타겟이 되는 오브젝트를 스무스 하게 따라가도록 하는 역할- Update 에서 호출할 것이기 때문에 타겟 오브젝트가 연결되 있는 한 매 프레임 실행될 것.
- targetPosition = target.transform.position;
- 타겟 오브젝트 위치
- 매 프레임마다 타겟의 새로운 위치를 계속 받아 올 것
- Vector3 smoothPosition = Vector3.
SmoothDamp
(transform.position, targetPosition, ref lastMovingVelocity, smoothTime);- Vector3 의 함수
SmoothDamp
- 매개변수 첫번재 벡터를 매개변수 두번째 벡터로 특정 시간에 걸쳐 자연스럽고 스무스하게 변화시키는 역할을 한다.
- 그 변화값 벡터를 smoothPosition에 리턴한다.
- 매개변수
Rig
위치로부터- transform.position
- 도달하려는 타겟 오브젝트 위치까지
- targetPosition
- 이 시간에 걸쳐서
- smoothTime
- 작을 수록 타겟에 빨리 도달
- 스무스 하게 점차적으로 벡터를 변경한다.
- ref lastMovingVelocity
- 바로 전, 마지막 프레임에서의 추적 속도
SmoothDamp
함수는 세번째 매개변수는ref
참조로 받는다.- call by reference
SmoothDamp
함수를 통해서 함수 바깥에서 또한 이 변수의 값이 바뀔 수 있게, 함수를 통해 변경된 lastMovingVelocity 값을 그대로 챙겨 나온다.- update() 함수에 의해 매 프레임마다 실행되면 매 프레임마다 lastMovingVelocity도 계속 변화할 것이다.
- ref lastMovingVelocity
- Vector3 의 함수
- transform.position = smoothPosition;
Rig
의 위치가 스무스하게 점점 변한다.- 타겟 오브젝트의 위치를 향하여.
- 최종적으로
Rig
가 타겟 오브젝트를 부드럽게 따라가므로Rig
의 자식인 카메라도 타겟 오브젝트를 부드럽게 따라가는게 된다.
- 추적
- private void Zoom()
- 줌 사이즈
- 카메라 줌 또한 스무스하게 커지고 작아지게 하기 위하여
- Mathf 의 함수
SmoothDamp
- Mathf에도 있다. float나 int 같은 값이 스무스하게 바뀌게끔
- void Move()와 비슷한 과정
- cam.orthographicSize = smoothZoomSize;
- 카메라의 확대 배율 size 값을 점점 스무스하게 변하는 값으로 설정
- 줌 사이즈
- private void FixedUpdate()
- Update()처럼 매 프레임 실행된다.
- Update()와의 차이점
- Update()는 화면 한번 깜빡일때마다 실행되서 렉걸리거나 환경이 안좋거나 하면 그만큼 Update()도 적게 실행되지만
- Fixedupdate()는 환경에 상관없이 무조건 실행 횟수를 지킨다. 정해진 수만큼 무조건 실행 함
- if (target != null)
- 이 조건이 꼭 있어야 한다. target도 없는데 target.position 이런식으로 사용하면 런타임 에러나니까.
- 타겟 오브젝트가 연결되어 있을 때
Move()
Zoom()
- public void Reset()
- 다음 라운드 때마다 대기 상태로 돌려놓는 함수
- 게임매니저(외부)에서 실행할 것이다.
- public void SetTarget(Transform newTarget, State newState)
- 외부에서 타겟 오브젝트를 지정할 때 사용할 함수
- 타겟과 상태를 지정한다.
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글남기기