2교시의 도착점
교수자의 완성 과정을 따라 관찰하며, 3교시 실습에서 스스로 판단할 연결 규칙과 시작 수치를 확보합니다.
- 01
Animation Clip, Animator Controller, Animator, Parameter의 책임을 구분합니다.
- 02
Speed가 0에 가까운지에 따라 Idle ↔ Move가 즉시 전환되는 이유를 설명합니다. - 03
Dead Zone과 Damping이 카메라의 추적 시작점과 따라가는 감각을 각각 어떻게 바꾸는지 설명합니다.
- 04
Cinemachine Confiner 2D가 레벨 밖 화면 노출을 막는 원리를 설명합니다.
60분의 설명과 시연 흐름
애니메이션의 상태 결정부터 카메라의 최종 화면 경계까지, 신호가 흐르는 순서로 진행합니다.
상태와 화면을 연결하기
교수자 설명과 화면 시연
- 1교시 회수
Sprite와 Clip을 구분하고, “어떤 Clip을 재생할지 누가 정하는가?”를 묻습니다.
- Animator 네 요소
Clip, Controller, Animator와 Parameter를 데이터, 규칙, 실행기와 증거로 나눕니다.
- Idle ↔ Move 전환
Speed 조건, Has Exit Time과 Transition Duration을 바꾸며 반응 차이를 관찰합니다.
- 코드 연결
이동 입력의 크기를 SetFloat로 전달하고 flipX로 좌우 방향을 표현합니다.
- Cinemachine 추적
Tracking Target, Position Composer, Dead Zone과 Damping을 하나씩 시연합니다.
- Confiner와 회수
화면 모서리가 레벨 경계 밖으로 나가지 않게 만들고 오류를 역추적합니다.
물리 몸체와 보이는 그림을 부모·자식으로 분리합니다
Player 루트는 게임의 위치와 충돌을 책임지고, Visual 자식은 그림과 애니메이션만 책임집니다. Animation Clip에서 루트 Transform을 기록하지 않는 것이 핵심입니다.
Animator는 조건이 있는 재생 지도입니다
Animation Clip이 하나의 움직임이라면 Animator Controller는 여러 움직임 사이의 길과 교통 규칙입니다.
- 01Code value
PlayerController가 이동 입력의 크기를 계산합니다.
- 02Parameter
Speedfloat에 현재 이동 의도의 크기를 전달합니다. - 03Transition condition
Speed가 기준보다 큰지 작은지 비교합니다.
- 04State
조건에 맞는 Idle 또는 Move 상태가 활성화됩니다.
- 05Motion
활성 상태에 연결된 Animation Clip이 반복 재생됩니다.
| 전환 | Condition | Has Exit Time | Duration 시작값 | 기대 결과 |
|---|---|---|---|---|
| Idle → Move | Speed Greater 0.01 | Off | 0-0.05 s | 입력을 시작하면 기다리지 않고 Move |
| Move → Idle | Speed Less 0.01 | Off | 0-0.05 s | 입력을 놓으면 기다리지 않고 Idle |
Has Exit Time = Off현재 Clip의 끝을 기다리지 않고 조건이 참이 되는 즉시 전환을 시작합니다. 이동 입력처럼 즉각 반응해야 하는 상태에 적합합니다.
Transition Duration두 상태를 섞는 시간입니다. Sprite 교체 애니메이션은 긴 혼합보다 0에 가깝게 시작하고 화면 반응으로 조절합니다.
이미 가진 이동값을 Animator의 증거로 보냅니다
새 이동 시스템을 만들지 않습니다. 4주차 PlayerController가 읽은 moveInput을 시각 표현에도 전달합니다.
[Header("Presentation")]
[SerializeField] private Animator animator;
[SerializeField] private SpriteRenderer spriteRenderer;
private static readonly int SpeedId = Animator.StringToHash("Speed");
private void Update()
{
moveInput = moveAction.action.ReadValue<Vector2>();
moveInput = Vector2.ClampMagnitude(moveInput, 1f);
animator.SetFloat(SpeedId, moveInput.sqrMagnitude);
if (Mathf.Abs(moveInput.x) > 0.01f)
{
spriteRenderer.flipX = moveInput.x < 0f;
}
}
Animator.StringToHash("Speed")문자열 이름을 Animator가 빠르게 비교할 수 있는 정수 ID로 한 번 변환해 보관합니다. Controller의 Parameter 이름과 철자가 같아야 합니다.
moveInput.sqrMagnitude입력이 없으면 0, 있으면 0보다 큰 값입니다. 방향을 버리고 “움직임의 크기”만 상태 판단에 사용합니다.
animator.SetFloat(...)계산한 값을 Animator의 Float Parameter에 전달합니다. 이 줄은 Clip을 직접 재생하지 않고 전환 조건의 증거만 갱신합니다.
spriteRenderer.flipX마지막 수평 방향에 따라 렌더링만 좌우 반전합니다. Transform이나 Collider2D는 뒤집지 않습니다.
카메라는 플레이어를 가운데에 붙이는 장치가 아닙니다
좋은 추적은 플레이어가 어디로 움직이는지 읽을 여유를 남기고, 작은 흔들림에는 반응하지 않으며, 필요한 순간에 부드럽게 따라갑니다.
- 01
Create
Cinemachine Camera - 02
Track
Player root - 03
Compose
Position Composer - 04
Tune
Dead Zone · Damping - 05
Confine
Level boundary
| 설정 | 질문 | 작게 하면 | 크게 하면 | 시작 범위 |
|---|---|---|---|---|
| Dead Zone / Size | 얼마나 움직여야 카메라가 반응할까? | 작은 이동에도 곧바로 추적 | 화면 안에서 더 자유롭게 이동 | 0.12-0.20 |
| Damping X / Y | 얼마나 빨리 따라잡을까? | 즉각적이고 단단함 | 느리고 무거우며 지연됨 | 0.3-0.7 |
| Screen Position | 화면의 어디에 두고 싶은가? | 중앙에서 시작하고 게임의 진행 방향에 따라 이동 | (0, 0) | |
카메라 점이 아니라 화면 전체를 경계 안에 둡니다
Cinemachine Confiner 2D는 PolygonCollider2D의 모양, 카메라의 보이는 크기와 화면 비율을 함께 고려해 화면 가장자리가 레벨 밖으로 나가지 않게 합니다.
Player가 벽을 통과하지 않게
게임 오브젝트의 물리 충돌을 계산합니다. 크기는 Player 몸체에 맞춥니다.
화면이 레벨 밖을 보지 않게
보이는 사각형 전체를 제한합니다. 레벨 외곽을 따라 훨씬 크게 만듭니다.
교수자 시연은 다섯 문장으로 확인합니다
각 단계 뒤 Play Mode에서 한 가지 결과만 확인합니다. 마지막에 모든 기능을 한꺼번에 확인하지 않습니다.
- AClip
Idle과 Move를 각각 선택했을 때 Preview에서 반복 재생됩니다.
- BAnimator
입력을 누르면 Speed가 커지고 Move 상태가 즉시 활성화됩니다.
- CFacing
왼쪽·오른쪽에서 Sprite만 반전되고 Collider2D 모양은 유지됩니다.
- DFollow
작은 이동은 Dead Zone 안에서 허용되고, 벗어나면 카메라가 부드럽게 따라갑니다.
- EBoundary
Player가 네 모서리로 가도 Game View에 레벨 밖 배경이 드러나지 않습니다.
증상과 책임을 먼저 연결합니다
오류가 보인 위치와 원인이 있는 위치는 다를 수 있습니다. 가장 가까운 신호부터 확인합니다.
이동하지만 계속 Idle입니다
Animator 창에서 Speed 값이 바뀌는지 봅니다. 0이면 SetFloat 호출과 참조를, 값이 바뀌면 Transition Condition과 Parameter 철자를 확인합니다.
키를 놓아도 Move가 끝까지 재생됩니다
Move → Idle 전환의 Has Exit Time이 꺼져 있는지, Condition이 Speed Less 0.01인지 확인합니다. 긴 Transition Duration도 반응을 늦출 수 있습니다.
좌우 반전 때 충돌 경계도 뒤집힙니다
Player Transform의 Scale X를 음수로 바꿨는지 확인합니다. 이번 구조는 Visual의 SpriteRenderer.flipX만 사용합니다.
카메라가 전혀 따라오지 않습니다
Cinemachine Camera가 활성화되어 있는지, Main Camera에 Cinemachine Brain이 있는지, Tracking Target이 Player root인지 확인합니다.
카메라가 매 프레임 떨립니다
Camera가 Player의 자식인지 먼저 봅니다. 자식이면 분리합니다. Position Composer의 Lookahead가 켜져 있다면 0으로 낮춘 뒤 Pivot 떨림과 Damping을 각각 확인합니다.
끝에 가면 레벨 밖이 보입니다
Confiner 2D의 Bounding Shape 2D 연결, PolygonCollider2D 외곽선, Orthographic Size를 순서대로 확인합니다. Scene View가 아니라 Game View에서 판정합니다.
3교시 전에 연결을 말로 복원합니다
도구 이름을 나열하지 말고, 입력에서 최종 화면까지의 원인과 결과를 한 문장으로 이어 봅니다.
Q1. Speed Parameter는 Clip입니까?
아닙니다. Speed는 코드가 전달하는 숫자이며 Transition이 어떤 State로 갈지 판단하는 증거입니다. State가 선택한 Motion이 Animation Clip입니다.
Q2. Has Exit Time을 이동 전환에서 끄는 이유는?
입력 변화가 생겼을 때 현재 Clip의 특정 재생 시점까지 기다리지 않고 즉시 상태를 바꾸기 위해서입니다.
Q3. Dead Zone과 Damping의 차이는?
Dead Zone은 카메라가 반응하지 않는 화면 안의 여유이고, Damping은 반응을 시작한 뒤 목표를 따라가는 빠르기입니다.
Q4. Player Collider와 CameraBounds는 같은 경계입니까?
아닙니다. Player Collider는 캐릭터의 물리 접촉을, CameraBounds는 카메라 화면 전체가 머물 수 있는 레벨 외곽을 정의합니다.
코드는 Speed ______만 바꾸고, 전환 조건이 재생할 State를 선택합니다.
Dead Zone은 카메라가 ______ 않는 영역이고, Damping은 따라가는 ______입니다.
Confiner 2D는 카메라의 ______ 가장자리가 Polygon 경계 안에 머물도록 계산합니다.
Unity 공식 자료
Animator는 Unity 6.6, 카메라는 Cinemachine 3.1의 현재 용어를 기준으로 정리했습니다.
- Animation transitions ↗Condition, Has Exit Time과 Transition Duration의 공식 동작
- Animator.SetFloat ↗Float Parameter를 Script에서 갱신하는 API
- SpriteRenderer.flipX ↗다른 Component에 영향을 주지 않고 렌더링만 X축 반전
- Follow and frame a character ↗Cinemachine Camera, Tracking Target와 Cinemachine Brain
- Position Composer ↗2D·Orthographic Camera의 Dead Zone, Damping과 구도
- Cinemachine Confiner 2D ↗2D Polygon과 화면 크기를 고려한 카메라 경계