개처럼개발한다
[Unity2D] 캐릭터를 따라다니는 카메라 구현 본문
[담은 내용]
- 캐릭터를 따라다니는 카메라 구현
- Mathf.Lerp 선형보간 개념
- 유니티 2D에서의 z축의 의미
- 유니티의 Life Cycle 함수들의 개념
Lerp 함수를 이용하여 캐릭터를 따라다니는 카메라를 구현해보고자 한다.
유니티를 처음 손대는 사람들을 위해 아주 기본적인 내용도 포함되어 있다.
<첫 시도>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class cameraCtrl : MonoBehaviour
{
public GameObject model;
Transform model_t;
void Start()
{
model_t = model.transform;
}
void Update()
{
transform.position = Vector3.Lerp(transform.position, model_t.position, 2f*Time.deltaTime);
transform.Translate(0, 0, -10);
{
}
1. public으로 선언해 카메라가 따라다닐 GameObject인 캐릭터를 유니티에서 받아온다
2. 그리고 위치, 크기, 회전의 정보를 가지고 있는 데이터 구조인 컴포넌트 Transform클래스를 새로 선언해준다.
3. 스크립트 첫 실행시 이 새로운 데이터 구조에 캐릭터 오브젝트 transform 컴포넌트 값인 위치, 크기, 회전 값을 가져온다.
4. Lerp 함수를 이용해 카메라의 위치를 조정한다.
Lerp에 대한 개념 ↓
Lerp(선형보간)은 a,b값이 주어졌을 때 그 사이에 위치한 값을 추정하기 위해 직선 거리에 따라 선형적으로 계산하는 방법이다. Lerp(a,b,c)에서 a는 시작 값, b는 끝값, a와 b사이에서 얻고 싶은 값의 위치(비율) 이다.
즉, 만약 Lerp(new Vector2(0, 0), new Vector2(100, 100), 0.5)라면
Vector2(50, 50)을 반환하게 된다.
그럼 여기서
transform.position = Vector3.Lerp(transform.position, model_t.position, 2f*Time.deltaTime);
를 해석하자면 transform은 이 스크립트 컴포넌트를 가지고 있는 오브젝트의 위치, 크기, 회전 값을 담은 정보를 뜻한다. 즉 이 스크립트는 카메라에 위치할 것이므로 오브젝트는 카메라를 뜻한다.
카메라의 위치를 Vector3.Lerp을 통해 조정해주겠다는 뜻인데
Lerp(transform.position, model_t.position, 2f*Time.deltaTime)이므로
transform.position = (transform.position - model_t.position) * (2f * Time.deltaTime)을 뜻한다.
즉, 카메라 위치와 캐릭터의 위치 사이에서 2f*Time.deltaTime만큼의 비율에 해당하는 값을 카메라의 위치로하겠다는거다.
Time.deltaTime은 이전 프레임과 현재 프레임 사이의 경과 시간이므로 매 프레임 마다 업데이트 되며, 각 프레임에서 호출할 때마다 이전 프레임에서 현재 프레임까지 걸린 시간을 초 단위로 반환한다.
Update에 넣어줬으니 이것을 매 프레임 반복할 것이고, 그렇담 결국 카메라는 Time.deltaTime이 흐른만큼(비율이 커지는만큼) 캐릭터의 위치를 향해 점점 다가가게 된다.
5. transform.Translate는 해당 오브젝트를 이동시킬 때 주로 쓰는 함수이다. 즉 z축을 -10만큼 이동 시켜주겠다라는 뜻이다.
*유니티 2D에서의 z축 의미 알고가기*
유니티 2D에서는 입체가 아닌 평면이므로 x, y축으로 구성된다. 하지만 2D에서 게임오브젝트를 씬에 넣다 보면 Transform 컴포넌트에 z축이 항상 있는걸 볼 수 있는데 왜일까??
게임을 시작하면 여러가지 오브젝트가 화면에 놓여질 텐데 이런 모든 오브젝트들이 하나의 이미지들이고 만들고 싶은 프로젝트 따라 어떤 오브젝트들이 더 맨앞에 오고 가장 뒤에 가야하는지도 정해져야한다.
예를 들면 캐릭터 이미지는 배경 이미지 보다는 앞에 있어야한다는 소리다.
이때 사용되는 것이 z축이다.
유니티에서 2D -> 3D로 바꾸면 각 오브젝트들의 z축 위치를 알 수 있다.

위 사진 처럼 각 오브젝트들은 우선순위대로 앞뒤에 위치한걸 볼 수 있다.
"깊이"라고 이해하면 쉬울 것 같다. 이러한 z축의 값이 작을 수록 뒤에 위치하게 된다. 저 사진대로라면 저 위치 표시가 켜져있는게 카메라 오브젝트인데 그게 가장 작은 값을 가지고 있다.
카메라가 가장 뒤에 있어야 모든 오브젝트를 표현할 수 있을 것이므로 카메라 오브젝트는 가장 작은 z값을 가져야한다.
자 이제 코드를 다 짰으니 저 스크립트를 카메라 오브젝트의 컴포넌트로 넣어주고 실행을 해보자.
아니 근데 이게 어떻게 된 일인지 실행하면 파란화면으로 아무것도 보이지 않게 되었다.
구글링해서 찾아보니, 다른 오브젝트에 제2의 카메라가 있으면 유니티는 이 카메라들을 교차해서 보여주기 때문에 파란화면이 뜰 수도 있다고 한다. 그런데 나는 그런 문제가 아니었다.ㅜㅜㅜ 어디서도 다른 카메라는 찾을 수 없었다.
그럼 대체 뭐가 문제일까...
해서 다른 방법으로 코드를 짜보았다.
<두번째 시도>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class cameraCtrl : MonoBehaviour
{
public GameObject model;
public float CameraZ = -10;
void FixedUpdate()
{
Vector3 targetPos = new Vector3(model.transform.position.x, model.transform.position.y, CameraZ);
transform.position = Vector3.Lerp(transform.position, targetPos, 2f*Time.deltaTime);
}
}
전 코드랑 비슷한데 다른점이라면
1. Update가 아닌 FixedUpdate를 씀
2. Transform클래스로 Start()에 객체 위치 지정 -> Update()에 Vector3 구조체 생성 및 위치 지정
3. Update()로 Lerp함수 사용하고 z축 -10조정 -> Update() 에 위치 지정해줌과 동시에 z축 -10조정하고 Lerp함수 사용
*잠깐의 지식*
C#에서 Vector가 New라고 되어있어 객체를 생성하는 클래스라고 생각할 수 있지만 사실 구조체 타입이다. 그리고 이 구조체 타입은 기본 타입인 int, float, char, enum등과 같이 value type이라서 스택에 저장되며 new를 한다고 힙에 메모리할당이 일어나지 않는다. 즉 반복해서 new를 해도 스택에 저장되는 것이므로 반복으로 인한 메모리릭이나 할당, 해제 문제가 일어나지 않는다.
문제는 Update()였다. Update가 아닌 FixedUpdate()를 사용해봤는데 잘 돌아가게 되었다.
그럼 여기서 Update와 FixedUpdate의 차이가 무엇일까?
기본 함수들부터 차근차근 알아보도록 하자
- Start() : 게임 시작할 때 한 번 실행하는 함수
- Awake() : Start()보다 더 먼저 실행되는 함수
- Update() : 매 프레임 마다 실행되는 함수, 물리효과(Rigidbody)가 적용되지 않은 오브젝트의 움직임이나 단순한 타이머, 키 입력을 받을 때 사용. 프레임마다 전체 시스템에서 수행되는 작업의 양이 매번 다르므로 Update가 호출되는 주기가 일정하지 않음.
- LateUpdate() : 모든 Update함수가 호출된 후, 마지막으로 호출되는 함수
- FixedUpdate() : 프레임에 영향을 받지 않고 호출되는 함수, 물리효과(Rigidbody)가 적용된 오브젝트를 조정할 때 사용
Update는 프레임에 영향을 받는 불규칙한 호출이라 물리엔진 충돌검사 등이 제대로 안될 수 있다.
아직 완벽하게 이해는 안됐으나 혼자 공부해본 바로는
"물리 엔진은 자기 스스로 정확한 시간에 동작한다. 근데 Update의 경우 물리 엔진과 다르게 작동하기 때문에 렌더링 엔진에서 얼마나 많은 그래픽 리소스를 로드하는지에 따라 느려지거나 빨리지기 때문에 일정하지 않은 시간에 물리적 충돌을 일으킬 수 있다. FixedUpdate는 해당 프레임에 정확하게 맞춰 연산하기 때문에 물리, 렌더링 등 모두 고려해서 처리된다. 따라서 정확한 프레임에 맞춰서 계산이 가능하다."
따라서 물리적으로 계산하는 것들이 있는 경우 FixedUpdate에서 처리해주는게 좋다고 이해했다.
단, FixedUpdate의 경우 프레임 저하가 생겼을 때는 계산 오차가 생겨 인게임의 프레임을 고정시켜주는 것이 중요하다고.....
Vector3.Lerp함수 때문에 FixedUpdate()를 사용해야하는 것이 아닐까싶다.
'Unity > 게임개발' 카테고리의 다른 글
[Unity] 방 꾸미기(마이룸) 기능 구현(1) - Mesh 타일 생성 (0) | 2024.09.21 |
---|