Unity Chapter 6-5. 어메이징 볼링 게임 만들기 : 카메라 추적

Date:     Updated:

카테고리:

태그:

인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀
🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!


Chapter 6. 어메이징 볼링 게임 만들기


🔔 카메라 추적

  • Main Camera
    • 회전하는 포신을 쫓아갔으면 좋겠다.
    • 날아가는 Ball을 쫓아갔으면 좋겠다.
    • 줌인 줌아웃 자유롭게 됐으면 좋겠다.

Main Camera 오브젝트

  • Projection 👉 Orthographic 값으로 바꿔 준다.
    • Perpective
      • 하얀색 투영 선이 대각선이며 한 점으로 모인다.
      • 멀리 있는건 작게 보이고 가까이 있는건 크게 보인다. 원근감 O
    • Orthographic
      • 하얀색 투영 선들이 평행하다.
      • 멀리 있으나 가까이 있으나 크기가 똑같이 보인다. 원근감 X
        • 2D 에서 많이 사용된다.
        • 3D 에서 사용하면 ‘길건너 친구들’ 게임 처럼 3D면서도 원근감이 없는 독특한 시점이 연출 된다.
  • 위치 0, 5, 0 / 회전 40, -60, 0 으로 해주자.
  • Main Camera는 기본적으로 비추는 물체에 딱 붙어버린다.
    • 이렇게 딱 붙으면 오히려 물체가 화면 전체를 차지해서 제대로 안보임
    • Main Camera비추려는 오브젝트의 거리를 좀 벌려주어야 한다.

카메라가 비추려는 오브젝트와 카메라의 거리를 벌린 체로 추적

image

문제 점

씬 창 말고 실제 게임 화면을 담는 Main Camera는 기본적으로 촬영하려는 오브젝트에 딱 붙어버린다. 이에 따라 해당 오브젝트가 카메라 화면을 다 차지해버려 오브젝트가 제대로 보이지 않는 현상이 발생 함. 얼굴에 밀착해서 촬영하면 피부만 보이지 오히려 이목구비는 안보이는 것처럼.. 😂 추적을 한다는 것은 카메라 위치를 목적지 위치와 일치하게끔 변화시켜 가는 과정인데 나중에 추적 완료해서 카메라 위치 = 목적지 위치가 되버린다면 타겟 오브젝트가 카메라 화면을 다 차지해버리니까 … !

해결 방안

image

Main Camera 와 추적 촬영하려는 오브젝트는 일정한 거리가 벌려져야 한다.

  1. 우선 Main CameraRig의 자식으로서 넣어준다.
  2. Main CameraRig일정 거리를 벌린 체 자식으로서 부모인Rig을 따라다니고
    • 일정거리는 Main Camera의 좌표를 수정해주면 된다.
    • 이때의 좌표는 부모인 Rig로부터의 Local 좌표
  3. Rig은 카메라가 진짜 비추려는 오브젝트위치를 추적하여 따라가게 한다.
  • 결론적으로 Main Camera는 투명하고 빈 오브젝트인 Rig 따라다니며 진짜 비추려는 오브젝트를 일정한 거리를 유지한 체 추적하는 모양새가 된다. 문제 해결!
  • Rig 이란 배의 추를 말한다. 배 밑에서 배에 끌려 다니는 추. Main Camera는 비추려는 오브젝트를 따라다니는게 아니라 오브젝트(배)를 따라다니는 `Rig` 오브젝트에 일정한 거리를 벌린체로 붙어서 따라다니게 할 것임
  • 그럼 Main Camera를 추적하려는 오브젝트의 자식으로서 넣어주고 로컬 좌표를 수정하여 추적하려는 오브젝트와 일정한 거리를 유지한 체로 추적하면 되는 것 아닌가?
    • 라고 강의를 들으며 생각 했지만 이러면 추적하는 대상이 바뀔 때 마다 부모-자식 관계 해제해야하고 또 새로운 대상의 자식으로 넣어줘야하고 이런 과정을 반복해야하니 번거로워서 그런 것 같다고 추측해본다. 😐

Rig 과 Main Camera 오브젝트 설정

  1. Rig라는 이름의 빈 오브젝트 생성
  2. 위치는 원점, 회전은 40, -60, 0
    • Main Camera와 회전 값을 똑같이 했다.
  3. Main CameraRig의 자식으로 넣어준다.
    • 카메라는 Rig의 자식이므로 Rig가 타겟 오브젝트 위치로 점점 이동할 때마다 카메라도 같이 Rig를 따라다니게 된다.
  4. Main Camera의 위치를 (0, 0, -65)로 바꿔준다. ✨
    • 이때의 위치는 Local 이므로 부오니 Rig오브젝트로부터의 거리를 말한다.
    • Rig로부터 z 방향으로 -65만큼 떨어 뜨려준다는 것.
    • ✨✨ Rig로부터 일정 거리를 유지한 체 따라다니게 된다.✨✨
  5. Rig 오브젝트에 📜CamFollow.cs 스크립트를 추가해준다.

📜CamFollow.cs

Rig 오브젝트가 하는 일

  1. 원하는 오브젝트를 트랙킹
  2. 각각의 상태에 따라 줌아웃 줌인 상태를 다르게 해줄 것.
    • ex) 포탄을 발사할 땐 맵 전체를 보도록 줌 아웃을 해줄 것.

3가지 상태 (카메라 상태 종류)

  1. 라운드를 대기하는 상태 : Idle
    • 줌 크기 14.5로 크게!
  2. 포탄 발사를 대기하는 상태 : Ready
    • 줌 크기 5로 작게!
  3. 발사된 포탄을 추적하는 상태 : 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 CameraRig의 자식 오브젝트이기 때문에 Main Camera도 📜CamFollow.cs의 내용에 따라 동작하게 된다.

🚍 변수

  • private State state
    • 겉으로 봐도 함수 같고 내부도 함수처럼 동작하지만 변수다.
      • 이와 같은 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 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함수에 매개변수로 넘겨야 하기 때문에 선언.
      • 바로 전 마지막 프레임에서 어떤 속도 했는지를 알아야 하기 때문에 선언

🚍 함수

  • 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에 리턴한다.
          • 매개변수
            1. Rig 위치로부터
              • transform.position
            2. 도달하려는 타겟 오브젝트 위치까지
              • targetPosition
            3. 이 시간에 걸쳐서
              • smoothTime
              • 작을 수록 타겟에 빨리 도달
            4. 스무스 하게 점차적으로 벡터를 변경한다.
              • ref lastMovingVelocity
                • 바로 전, 마지막 프레임에서의 추적 속도
                • SmoothDamp 함수는 세번째 매개변수는 ref 참조로 받는다.
                • call by reference
                • SmoothDamp 함수를 통해서 함수 바깥에서 또한 이 변수의 값이 바뀔 수 있게, 함수를 통해 변경된 lastMovingVelocity 값을 그대로 챙겨 나온다.
                • update() 함수에 의해 매 프레임마다 실행되면 매 프레임마다 lastMovingVelocity도 계속 변화할 것이다.
      • 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)
    • 외부에서 타겟 오브젝트를 지정할 때 사용할 함수
    • 타겟과 상태를 지정한다.


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기


Unity Lesson 1 카테고리 내 다른 글 보러가기

댓글 남기기