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

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

A 3D take on the dino runner. The dino moves forward along the Z-axis automatically; the player uses the arrow keys to dodge left and right. A follow camera tracks the dino's Z position using LateUpdate, and a counter displays the number of active dinos using childCount.


Game Structure

Script Role
DinoController Auto-forward on Z, left/right movement on X
DinoFollowCamera Tracks the dino's Z position with LateUpdate
DinoCounter Displays the number of child objects as a UI counter

DinoController – 3D Movement

Full Code

using UnityEngine;

public class DinoController : MonoBehaviour
{
    public float zMoveSpeed;   // Auto-forward speed along Z
    public float xMoveSpeed;   // Lateral speed along X

    void Update()
    {
        // Move forward automatically
        transform.position += Vector3.forward * Time.deltaTime * zMoveSpeed;

        // Strafe left/right with arrow keys
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            transform.Translate(-xMoveSpeed * Time.deltaTime, 0, 0);
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.Translate(xMoveSpeed * Time.deltaTime, 0, 0);
        }
    }
}

Key Concepts

1. Auto-forward – Vector3.forward

transform.position += Vector3.forward * Time.deltaTime * zMoveSpeed;
  • Vector3.forward is the shorthand for (0, 0, 1) — the world +Z direction.
  • Adding it each frame with Time.deltaTime * speed advances the dino at a consistent speed regardless of frame rate.
  • Common direction vectors:
Vector Value Direction
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. Lateral movement – Transform.Translate

if (Input.GetKey(KeyCode.LeftArrow))
    transform.Translate(-xMoveSpeed * Time.deltaTime, 0, 0);
  • Input.GetKey: Returns true every frame the key is held down. (Contrast with GetKeyDown, which fires only on the first frame.)
  • transform.Translate(x, y, z): Moves the object relative to its current position. By default, it uses the object's local space.
// Equivalent using position +=  (world space)
transform.position += new Vector3(-xMoveSpeed * Time.deltaTime, 0, 0);

// Translate in local space (default)
transform.Translate(-xMoveSpeed * Time.deltaTime, 0, 0);

// Translate in world space
transform.Translate(-xMoveSpeed * Time.deltaTime, 0, 0, Space.World);
  • If the object has no rotation the results are the same. When the object is rotated, Translate moves along the object's own axes while position += always moves along world axes.

DinoFollowCamera – Camera Follow

Full Code

using UnityEngine;

public class DinoFollowCamera : MonoBehaviour
{
    public Transform target;
    public Vector3 offset;

    void Start()
    {
        // Record the initial offset between camera and target
        offset = target.position - transform.position;
    }

    void LateUpdate()
    {
        if (target != null)
        {
            // Follow only the Z axis; keep X and Y fixed
            Vector3 newPosition = new Vector3(
                transform.position.x,
                transform.position.y,
                target.position.z - offset.z
            );
            transform.position = newPosition;
        }
    }
}

Key Concepts

1. LateUpdate – runs after all Updates

void LateUpdate()
{
    transform.position = newPosition;
}
  • LateUpdate runs after every Update call in the same frame.
  • If you move the camera inside a regular Update, both the dino's Update and the camera's Update run in an unspecified order. The camera might read the dino's position before the dino has moved, causing visible jitter.
  • Using LateUpdate for the camera guarantees it always reads the dino's final position for that frame.

Unity execution order per frame:

FixedUpdate (physics, fixed interval)
  ↓
Update (every frame)
  ↓
LateUpdate (every frame, after all Updates)

2. Offset – preserve the initial gap

void Start()
{
    offset = target.position - transform.position;
}
  • Calculates and stores the gap between the camera and the target once at startup.
  • Place the camera at the desired position in the editor; Start will capture that distance automatically.

3. Follow Z only

Vector3 newPosition = new Vector3(
    transform.position.x,     // X: unchanged
    transform.position.y,     // Y: unchanged
    target.position.z - offset.z  // Z: tracks the dino
);
  • The dino moves along Z, so the camera only needs to track Z.
  • Keeping X and Y fixed prevents the camera from drifting when the dino strafes left/right, maintaining a stable viewpoint.

DinoCounter – Live Object Count

Full Code

using UnityEngine;
using TMPro;

public class DinoCounter : MonoBehaviour
{
    public TextMeshPro dinoCountText;
    public Transform dinoParent;

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

Key Concepts

1. childCount – count active children

dinoCountText.text = dinoParent.childCount.ToString();
  • transform.childCount: Returns the number of direct children (does not count grandchildren).
  • By grouping all dinos under a single parent object, the count updates automatically whenever dinos are created or destroyed.

2. TextMeshPro vs TextMeshProUGUI

Component Use
TextMeshPro 3D text placed in the scene (world space)
TextMeshProUGUI 2D text on a Canvas
Text (Legacy) Old Unity UI — avoid in new projects
  • Use TextMeshPro for floating labels in 3D space; use TextMeshProUGUI for HUD/UI on a Canvas.
  • Both require using TMPro;.

Scene Setup Summary

Item Recommendation
Dino object DinoController attached; set zMoveSpeed and xMoveSpeed in Inspector
Camera Place behind and above the dino; attach DinoFollowCamera; assign target
Dino parent Empty GameObject "DinoParent"; all dinos are children of this object
Counter text 3D TextMeshPro object; assign "DinoParent" to DinoCounter.dinoParent

Summary

Pattern Code Purpose
Auto-forward transform.position += Vector3.forward * Time.deltaTime * speed Infinite forward run
Strafe transform.Translate(x * Time.deltaTime, 0, 0) Lane switching
Camera follow Track Z in LateUpdate Smooth chase camera
Object count transform.childCount Live dino counter