№ 49실전
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;
public float xMoveSpeed;
void Update()
{
transform.position += Vector3.forward * Time.deltaTime * zMoveSpeed;
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.
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);
- 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()
{
offset = target.position - transform.position;
}
void LateUpdate()
{
if (target != null)
{
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,
transform.position.y,
target.position.z - offset.z
);
- 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 |