3D 공룡 달리기 - 이동과 카메라 추적

3D 공간에서 Z축 자동 전진과 X축 좌우 이동을 구현하고, LateUpdate로 카메라가 캐릭터를 추적하는 방법을 학습합니다

2D 공룡 달리기를 3D로 확장한 버전입니다. 공룡이 Z축 방향으로 자동으로 전진하고, 좌우 방향키로 X축 이동을 제어합니다. 카메라는 LateUpdate를 이용해 공룡을 매끄럽게 추적하고, UI 텍스트로 씬에 존재하는 공룡 수를 실시간으로 표시합니다.


게임 구조

스크립트 역할
DinoController Z축 자동 전진, 좌우 방향키 X축 이동
DinoFollowCamera LateUpdate로 공룡 Z 위치 추적
DinoCounter 부모 오브젝트의 자식 수를 UI에 표시

DinoController - 3D 이동 제어

전체 코드

using UnityEngine;

public class DinoController : MonoBehaviour
{
    public float zMoveSpeed;   // Z축 자동 전진 속도
    public float xMoveSpeed;   // X축 좌우 이동 속도

    void Update()
    {
        // Z축 방향으로 자동 전진
        transform.position += Vector3.forward * Time.deltaTime * zMoveSpeed;

        // 왼쪽/오른쪽 방향키로 X축 이동
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            transform.Translate(-xMoveSpeed * Time.deltaTime, 0, 0);
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.Translate(xMoveSpeed * Time.deltaTime, 0, 0);
        }
    }
}

핵심 개념 설명

1. Z축 자동 전진 - Vector3.forward

transform.position += Vector3.forward * Time.deltaTime * zMoveSpeed;
  • Vector3.forward: (0, 0, 1) 방향의 단위 벡터입니다. 3D 공간에서 "앞"을 나타냅니다.
  • transform.position +=으로 매 프레임 조금씩 앞으로 이동합니다.
  • Time.deltaTime을 곱하면 프레임 속도와 무관하게 항상 초당 zMoveSpeed 만큼 이동합니다.
  • 유사한 방향 벡터: Vector3.back(뒤), Vector3.left(왼), Vector3.right(오른), Vector3.up(위), Vector3.down(아래)
벡터 방향
Vector3.forward (0, 0, 1) +Z
Vector3.back (0, 0, -1) -Z
Vector3.right (1, 0, 0) +X
Vector3.left (-1, 0, 0) -X
Vector3.up (0, 1, 0) +Y
Vector3.down (0, -1, 0) -Y

2. 좌우 이동 - Transform.Translate

if (Input.GetKey(KeyCode.LeftArrow))
{
    transform.Translate(-xMoveSpeed * Time.deltaTime, 0, 0);
}
  • Input.GetKey: 키를 누르고 있는 동안 매 프레임 true를 반환(return)합니다. (GetKeyDown은 처음 누른 프레임만)
  • transform.Translate(x, y, z): 현재 위치에서 상대적으로 이동합니다. 기본적으로 로컬 좌표계 기준으로 이동합니다.
// transform.position += 방식과 비교
transform.position += new Vector3(-xMoveSpeed * Time.deltaTime, 0, 0); // 월드 좌표 기준

transform.Translate(-xMoveSpeed * Time.deltaTime, 0, 0); // 로컬 좌표 기준 (기본값)
transform.Translate(-xMoveSpeed * Time.deltaTime, 0, 0, Space.World); // 월드 좌표 기준
  • 오브젝트가 회전하지 않은 경우 두 방식의 결과는 같습니다.
  • 오브젝트가 회전된 경우, Translate는 오브젝트의 방향을 따라 이동하고 position +=는 항상 월드 기준으로 이동합니다.

DinoFollowCamera - 카메라 추적

전체 코드

using UnityEngine;

public class DinoFollowCamera : MonoBehaviour
{
    public Transform target;   // 추적할 공룡 오브젝트
    public Vector3 offset;     // 카메라와 공룡 사이의 거리

    void Start()
    {
        // 게임 시작 시 카메라와 공룡의 위치 차이를 오프셋으로 저장
        offset = target.position - transform.position;
    }

    void LateUpdate()
    {
        if (target != null)
        {
            // Z 위치만 공룡을 따라가고, X·Y는 고정
            Vector3 newPosition = new Vector3(
                transform.position.x,
                transform.position.y,
                target.position.z - offset.z
            );
            transform.position = newPosition;
        }
    }
}

핵심 개념 설명

1. LateUpdate - Update 이후에 실행

void LateUpdate()
{
    transform.position = newPosition;
}
  • LateUpdate: Update가 모두 끝난 뒤에 호출됩니다. 카메라 이동에 LateUpdate를 사용하는 이유:
    • DinoController.Update에서 공룡이 이동
    • 그 다음 DinoFollowCamera.LateUpdate에서 카메라가 이동
    • 두 Update가 같은 프레임에 동시에 실행되면 카메라가 공룡의 "이전 위치"를 따라가 떨림(jitter)이 발생할 수 있습니다.
  • 카메라, UI 갱신, 후처리 등 다른 오브젝트의 이동이 확정된 뒤 실행해야 하는 로직에 LateUpdate를 사용합니다.

Unity 업데이트 실행 순서:

FixedUpdate (물리 업데이트, 고정 간격)
  ↓
Update (매 프레임)
  ↓
LateUpdate (Update 이후, 매 프레임)

2. 오프셋 계산 - 초기 거리 유지

void Start()
{
    offset = target.position - transform.position;
}
  • 게임 시작 시 카메라와 공룡 사이의 거리(오프셋)를 한 번만 계산해 저장합니다.
  • Inspector에서 카메라를 공룡 뒤쪽 적절한 거리에 배치해 두면, Start에서 그 거리가 자동으로 오프셋으로 설정됩니다.

3. Z축만 추적

Vector3 newPosition = new Vector3(
    transform.position.x,   // X: 고정
    transform.position.y,   // Y: 고정
    target.position.z - offset.z  // Z: 공룡 따라가기
);
  • 이 게임에서 공룡은 Z축으로 전진하므로, 카메라도 Z만 공룡을 따라갑니다.
  • X(좌우)와 Y(높이)는 카메라 원래 위치 그대로 유지해 시점이 흔들리지 않습니다.
  • target.position.z - offset.z: 공룡과 처음에 설정된 거리(offset.z)만큼 뒤에서 따라갑니다.

DinoCounter - 공룡 수 표시

전체 코드

using UnityEngine;
using TMPro;

public class DinoCounter : MonoBehaviour
{
    public TextMeshPro dinoCountText;  // 공룡 수를 표시할 3D 텍스트
    public Transform dinoParent;       // 공룡들이 자식으로 등록된 부모 Transform

    void Update()
    {
        dinoCountText.text = dinoParent.childCount.ToString();
    }
}

핵심 개념 설명

1. childCount - 자식 오브젝트 수 파악

dinoCountText.text = dinoParent.childCount.ToString();
  • transform.childCount: 이 Transform의 직접적인 자식 오브젝트 수를 반환(return)합니다. 손자(자식의 자식)는 포함되지 않습니다.
  • 공룡 오브젝트들을 하나의 부모 오브젝트 아래에 모아두면, 공룡이 생성·삭제될 때마다 childCount가 자동으로 변합니다.
  • UI 텍스트를 매 프레임 갱신하는 단순한 방법입니다. 성능이 중요한 경우 변경 시에만 갱신하는 방식을 고려할 수 있습니다.

2. TextMeshPro vs Text

컴포넌트 특징
TextMeshPro 3D 씬 공간에 배치하는 3D 텍스트 (WorldSpace)
TextMeshProUGUI Canvas 위에 배치하는 2D UI 텍스트
Text (Legacy) 구형 Unity UI, 현재는 사용 비권장
  • 3D 씬에 떠 있는 텍스트(점수판, 이름표 등)는 TextMeshPro, Canvas UI에 표시되는 텍스트는 TextMeshProUGUI를 사용합니다.
  • 사용 시 using TMPro; 네임스페이스(namespace)를 추가해야 합니다.

Unity 씬 설정 요약

항목 권장 설정
공룡 오브젝트 DinoController 부착, zMoveSpeed / xMoveSpeed Inspector에서 설정
카메라 공룡 뒤쪽 위에 배치, DinoFollowCamera 부착 후 target에 공룡 할당
공룡 부모 빈 GameObject "DinoParent", 공룡들을 자식으로 배치
카운터 텍스트 3D TextMeshPro 오브젝트, DinoCounter.dinoParent"DinoParent" 할당

요약

패턴 코드 용도
자동 전진 transform.position += Vector3.forward * Time.deltaTime * speed Z축 무한 달리기
방향키 이동 transform.Translate(x * Time.deltaTime, 0, 0) 좌우 레인 변경
카메라 추적 LateUpdate에서 Z만 target 따라가기 진행 방향 카메라
오브젝트 수 파악 transform.childCount 생존 공룡 수 표시