- 핵심 질문
- 지금 어떤 동작의 그림을 보여 주고, 카메라는 어디를 따라갈까요?
- 학습 방식
- 설정 → 값 관찰 → 한 조건 비교
- 이전 학습
- 1교시 Visual · Animation Clip 5개
- 이어 쓸 장면
- Week06_AnimationCamera
- 수업 시간
- 핵심 설명과 시연 50분
- 도착점
- 상태 전환 · 방향 · 카메라 추적
1교시의 다섯 Clip을 이어 사용합니다. 4주차 PlayerController를 교체하지 않고 Player에 PlayerDash, Visual에 PlayerAnimation을 추가합니다. 대시는 짧은 가로 이동을 만들고, 표현 스크립트는 현재 동작에 맞는 그림을 고릅니다. 5주차 수집 기능은 사용하지 않습니다. 카메라까지 연결한 뒤에는 한 값만 바꿔 차이를 관찰합니다.
50분의 설명과 완성 시연
그림 선택을 먼저 수동으로 확인하고, 대시·바닥 판정·카메라를 차례로 연결합니다. 50분은 강사의 설명과 시연 기준입니다. 단계별 확인을 함께 하고, 개별 구현과 반복 검증은 3교시에서 이어갑니다.
- 재료와 역할
다섯 Clip과 Player·Visual 구조를 확인합니다.
- Animator 연결
State·Motion·전환 조건을 만들고 번호를 직접 바꿉니다.
- 입력과 상태 전달
대시 단독 확인 → Ground Layer → 상태 자동 전환을 확인합니다.
- 카메라 추적
Brain·Tracking Target·Lens를 설정하고 반응을 비교합니다.
- 화면 경계
레벨 외곽과 화면 크기를 비교하고 Confiner를 연결합니다.
- 연결 검증
실제 동작 → State → 그림 → 카메라를 확인합니다.
1교시에서 만든 다섯 Clip을 가져옵니다
Play와 Animation Preview를 끄고 Week06_AnimationCamera Scene을 엽니다.
Window → Package Management → Package Manager에서 Unity Registry의 Cinemachine을 선택합니다. 3.1 계열이 없다면 버전을 선택해 Install하고, 패키지 가져오기와 컴파일이 끝날 때까지 기다립니다. 이미 설치했다면 버전만 확인합니다. Console Error가 0인 상태에서 수업을 시작하고, 카메라 연결은 5단계에서 진행합니다.
| Clip | 그림 수 | Samples | Loop Time |
|---|---|---|---|
| Player_Idle.anim | 4 | 4 | On |
| Player_Move.anim | 8 | 8 | On |
| Player_Jump.anim | 4 | 12 | Off |
| Player_Fall.anim | 4 | 8 | On |
| Player_Dash.anim | 4 | 20 | Off |
Project에서 다섯 .anim 파일이 있는지 확인합니다. Animation 창에서 각 Clip의 Sprite 트랙을 재생해 보고 Preview를 끕니다. Animator Controller는 Visual에 연결된 하나를 계속 사용합니다. 이름이 다르더라도 새 Controller를 여러 개 만들 필요는 없습니다.
이전 실습 자료를 받았다면 최신 실습 ZIP으로 PNG 다섯 개를 갱신하고, 1교시의 자동 정렬 도구를 한 번 실행합니다. 한 프레임 256×256, 공통 PPU 220과 Pivot X 0.5 / Y 0.125로 맞춘 자료입니다. 기존 PNG의 경로와 .meta 파일을 유지하세요.
Visual의 SpriteRenderer에는 Idle_01, Animator에는 기존 Controller가 연결되어 있습니다. Player 루트의 기존 SpriteRenderer만 꺼 두고, Rigidbody2D·Collider2D·PlayerController는 활성 상태로 유지합니다. PlayerAnimation은 대시 확인 뒤 4단계에서 연결합니다. 이미 연결을 마쳤다면 수동 전환을 확인하는 동안만 컴포넌트를 끕니다.
Player 루트는 움직이고 Visual 자식은 보여 줍니다
몸체를 움직이는 일과 어떤 그림을 보여 줄지 정하는 일을 구분합니다.
1교시의 Clip은 한 동작 안에서 그림을 순서대로 바꿉니다. 하지만 점프 버튼을 눌렀다는 이유만으로 Jump Clip이 자동 선택되지는 않습니다. 실제 움직임을 읽어 지금 보여 줄 동작의 번호를 전달해야 합니다.

| 선택 대상 | 역할 | 이번에 할 일 |
|---|---|---|
| Player | Rigidbody2D, Collider2D, PlayerController로 이동·점프 | PlayerDash 추가, Dash Action 연결 |
| Visual | SpriteRenderer, Animator로 그림 표시 | Controller 확인, PlayerAnimation 연결 |
| Main Camera | Game 화면을 실제로 그리기 | Cinemachine Brain 확인 |
| Cinemachine Camera | Player를 화면 어디에 둘지 계산 | Tracking Target·Position Composer·Confiner 2D 설정 |
| CameraBounds | 레벨의 카메라 경계 모양 | PolygonCollider2D와 전용 Layer 설정 |
PlayerAnimation은 속도를 읽기만 합니다. PlayerController는 이동·점프를 계속 처리하고, PlayerDash는 대시 중 가로 속도만 덮어씁니다. Visual에 Rigidbody2D나 Collider2D를 하나 더 붙이지 않습니다.
Animation과 Animator는 서로 다른 창입니다
그림의 재생 순서를 편집하는 창과 동작을 고르는 창을 구분합니다.
Animation 창은 Jump_01~04를 몇 초에 보여 줄지 편집합니다. Animator 창은 Idle을 재생하다가 언제 Jump로 바꿀지 정합니다. 따라서 이번 단계에서는 새 그림 키를 찍지 않고, 이미 만든 Clip을 상태에 연결합니다.

| 이름 | 뜻 | 이번 수업의 예 |
|---|---|---|
| Animator 컴포넌트 | 오브젝트에서 Controller를 실행하는 컴포넌트 | Visual의 Inspector에 붙어 있습니다. |
| Animator Controller | 상태와 전환 규칙을 저장한 에셋 | Visual의 Controller 슬롯에 연결합니다. |
| State · 상태 | 현재 실행할 동작을 담는 상자 | Jump 상태의 Motion에 Player_Jump.anim을 넣습니다. |
| Parameter · 매개변수 | 전환 조건이 읽는 값 | Int 타입 State에 2를 전달합니다. |
| Transition · 전환 | 조건이 맞을 때 다른 상태로 가는 화살표 | State Equals 2이면 Jump로 갑니다. |
Animator의 사각형 하나도 상태(State)라고 부릅니다. 이번에 만드는 대문자 State는 직접 붙인 Parameter 이름입니다. 숫자 2 자체에 점프 기능이 있는 것은 아닙니다. 우리가 2를 Jump에 대응시킵니다.
1단계 · 다섯 상태에 Clip을 연결합니다
먼저 상자와 Motion을 확인합니다. 전환 화살표는 다음 단계에서 만듭니다.

Visual을 선택하고 Animator 창 열기
Hierarchy에서 Player의 자식 Visual을 선택합니다. Inspector의 Animator → Controller 슬롯을 확인하고 연결된 Controller를 더블클릭합니다. 또는
Window → Animation → Animator를 엽니다.Animation창과 혼동하지 않습니다.화면에서 확인Animator 창에 Entry·Any State와 기존 Clip의 상태들이 보입니다.기존 상태를 다섯 이름으로 정리하기
1교시에서 Clip을 만들며 생긴 상태가 있으면 그대로 씁니다. 상태를 클릭하고 Inspector 위쪽 이름을
Idle,Move,Jump,Fall,Dash로 정리합니다. 빠진 상태만 Project의 해당.anim을 Animator 빈 공간으로 드래그해 추가합니다.화면에서 확인같은 Clip을 가리키는 중복 상자가 없고, 다섯 상태가 있습니다.상태마다 Motion 확인하기
상태 상자를 하나씩 클릭해 Inspector의 Motion에 해당
Player_*.anim을 넣습니다. State의 Speed는 1로 둡니다. 이 Speed는 재생 배율이며, 나중에 만들 Float Parameter Speed와 다른 설정입니다.화면에서 확인Jump의 Motion은 Player_Jump.anim이고, 각 상태의 재생 배율은 1입니다.시작 상태를 Idle로 지정하기
Idle 상자 우클릭 → Set as Layer Default State를 선택합니다. Entry는 Controller를 시작할 때 들어갈 곳을 뜻합니다. 현재 상태 이름과 Motion 파일 이름은 달라도 되지만, 표에 맞게 연결해야 합니다.
화면에서 확인Idle이 주황색이 되고 Entry의 화살표가 Idle로 향합니다.
2단계 · 번호에 따라 상태를 바꿉니다
현재 동작이 무엇이든, 전달받은 번호에 맞는 상태로 갈 수 있도록 만듭니다.
상태 전환의 Conditions는 Parameters에 만든 값을 읽습니다. 지속되는 상태인지, 한 번의 요청인지, 연속적인 수치인지, 구분용 번호인지에 따라 타입을 고릅니다.
| 타입 | 담는 값과 특징 | 언제 사용하나요? | 전환 조건 예시 |
|---|---|---|---|
Bool | true / false. 값을 바꾸기 전까지 유지됩니다. | 어떤 상태가 계속 유지되는지 표현할 때. 바닥에 닿아 있는지, 웅크리고 있는지 등을 전달합니다. | IsCrouching이 true이면 웅크리기, false이면 서 있는 상태로 전환합니다. |
Trigger | 한 번의 요청 신호. 전환에 사용되면 자동으로 초기화됩니다. | 그 순간 발생한 동작을 한 번 요청할 때. 공격 버튼 누르기, 피격 반응 등에 사용합니다. | Conditions에 Attack Trigger를 넣고, 공격 입력 때 신호를 보내 공격 상태로 전환합니다. |
Float | 0.0, 0.5, 3.2처럼 소수점이 있는 수치입니다. | 연속적으로 변하는 값의 크기를 비교할 때. 이동 속도나 수직 속도가 기준보다 큰지·작은지 판단합니다. | Speed Greater 0.1이면 Move로 전환합니다. Greater는 초과, Less는 미만입니다. |
Int | 0, 1, 2처럼 소수점이 없는 정수입니다. | 여러 종류 중 하나를 번호로 구분할 때. 동작 종류, 무기 종류, 콤보 단계 등을 전달합니다. | 이번 수업처럼 State Equals 2이면 Jump로 전환합니다. Equals는 같은 값인지 확인합니다. |
이번 실습에서는 다섯 동작 중 하나를 고르기 위해 Int 타입 State를 사용합니다. Float 타입 Speed는 실제 가로 속도를 관찰하는 용도입니다. 타입별 기본 동작은 Unity Animation Parameters 공식 문서 ↗에서 확인할 수 있습니다.
Any State는 특정 Clip을 재생하는 상자가 아니라, 어느 상태에서도 확인할 공통 출발점입니다. 예를 들어 지상에서든 공중에서든 대시 중이면 Dash를 선택하도록 연결할 수 있습니다.

Parameters에 State와 Speed 만들기
Animator 창 왼쪽 Parameters 탭을 누릅니다. + → Int로
State를 만들고 기본값을 0으로 둡니다. 이어 + → Float로Speed를 만들고 0으로 둡니다. 같은 이름이 이미 있으면 타입과 대소문자만 확인합니다.화면에서 확인State / Int와 Speed / Float가 하나씩 있습니다.Any State에서 Idle로 첫 화살표 만들기
Any State 우클릭 → Make Transition → Idle 상자를 클릭합니다. 연결된 화살표 선을 클릭하면 Inspector에 전환 설정이 나옵니다. 상태 상자를 선택하면 Motion 설정이 나오므로 선택 대상을 구분합니다.
화면에서 확인Inspector에서 전환의 Conditions와 Settings를 볼 수 있습니다.Conditions에 State Equals 0 넣기
Conditions의 +를 누르고
State→Equals→0을 지정합니다. Has Exit Time을 끄고 Settings에서 Transition Duration 0, Transition Offset 0, Can Transition To Self Off를 확인합니다.화면에서 확인Idle로 가는 조건은 State Equals 0 한 줄입니다.나머지 네 화살표에도 같은 방식 적용하기
Any State에서 Move·Jump·Fall·Dash로 각각 화살표를 만듭니다. 아래 표대로 Equals 값만 바꿉니다. 기존 Idle ↔ Move의 Speed 조건 화살표가 있다면 그 전환을 삭제합니다. 최종 구성은 Entry → Idle과 Any State에서 나가는 다섯 전환입니다.
화면에서 확인다섯 화살표 모두 Has Exit Time Off, Transition Duration 0, Can Transition To Self Off입니다.
| 출발 → 도착 | Condition | Motion | 그림의 역할 |
|---|---|---|---|
| Any State → Idle | State Equals 0 | Player_Idle | 바닥에서 정지 |
| Any State → Move | State Equals 1 | Player_Move | 바닥에서 이동 |
| Any State → Jump | State Equals 2 | Player_Jump | 공중에서 상승 |
| Any State → Fall | State Equals 3 | Player_Fall | 공중에서 정점·낙하 |
| Any State → Dash | State Equals 4 | Player_Dash | 짧은 돌진 동작 |
| 설정 | 값 | 확인할 동작 |
|---|---|---|
| Has Exit Time | Off | 착지하거나 정점에 닿으면 현재 Clip이 끝나기 전에도 바뀝니다. |
| Transition Duration | 0 | 이번 Sprite 교체는 두 상태를 겹치는 시간을 두지 않습니다. |
| Transition Offset | 0 | 다른 상태로 들어가면 그 Clip의 첫 그림부터 시작합니다. |
| Can Transition To Self | Off | 같은 번호가 계속 들어와도 첫 그림으로 매번 되돌아가지 않습니다. |
Loop Time은 한 상태 안에서 Clip을 반복할지, Has Exit Time은 다른 상태로 나갈 때 재생 시점을 기다릴지 정합니다. Jump가 Loop Off여도 정점에서 Fall로 즉시 바뀔 수 있습니다. 설정 이름은 Unity 전환 문서에서도 확인할 수 있습니다.
먼저 손으로 번호를 바꿔 봅니다
코드를 연결하기 전에, 번호 → 활성 상태 → 실제 그림이 맞는지 확인합니다.
아래는 1교시의 실제 Sprite와 Samples·Loop·PPU·Pivot으로 만든 설명용 예제입니다. 재생을 누른 뒤 상태 버튼을 바꿔 보세요. 물리 이동은 고정되어 있으며 Unity의 Animator 실행 화면은 아닙니다.
같은 상태를 다시 눌러도 처음으로 돌아가지 않습니다. Jump·Dash가 끝나면 마지막 그림을 유지합니다. 다른 상태를 선택하면 그 Clip의 첫 그림으로 바뀝니다.
자동으로 State를 쓰는 스크립트 잠시 끄기
아직 PlayerAnimation을 붙이지 않았다면 그대로 진행합니다. 이미 붙였다면 Body·Dash·Ground Layers가 모두 채워져 있는지 확인하고 Visual의 PlayerAnimation 컴포넌트만 비활성화합니다. 참조가 비어 있다면 4단계의 연결을 먼저 마칩니다. 컴포넌트를 꺼도 최초 실행의 Awake 참조 검사는 진행됩니다. Animator는 켜 둡니다. 스크립트가 매 프레임 값을 쓰고 있으면 손으로 입력한 번호가 즉시 되돌아갑니다.
화면에서 확인수동으로 입력한 State 값이 유지될 준비가 되었습니다.Play 중 Visual의 실제 상태 보기
Play를 누른 다음 Hierarchy의 Visual을 선택합니다. Animator 창에서 실행 중인 Visual의 Controller를 보고 있는지 확인합니다. Parameters의 State를
0 → 1 → 2 → 3 → 0 → 4 → 0으로 바꿉니다. 상태 상자의 재생 표시와 Game 화면을 함께 봅니다.화면에서 확인Idle·Move·Fall은 반복하고, Jump·Dash는 마지막 그림에 멈춥니다.Play 종료 후 저장하기
Play를 끄고 Scene과 Controller를 저장합니다. 수동 테스트에서 바꾼 번호는 게임 규칙이 아닙니다. 다음 단계에서 실제 바닥 접촉·속도·대시 여부가 번호를 선택하게 만듭니다.
화면에서 확인다섯 상태의 그림이 맞고, Play·Preview·Record는 꺼져 있습니다.
3단계 · 대시 입력과 몸체 움직임을 연결합니다
PlayerDash만 먼저 연결해 입력과 이동이 되는지 확인합니다. PlayerAnimation은 다음 단계에서 붙입니다.
두 C# 파일을 함께 가져오기
PlayerDash.cs와 PlayerAnimation.cs를
Assets/Scripts에 넣습니다. 이미 있다면 기존 파일을 갱신합니다. 전체 코드를 PlayerController 안에 이어 붙이지 않습니다. 두 파일을 모두 가져온 뒤 Unity의 컴파일이 끝날 때까지 기다립니다.화면에서 확인Console Error가 0이고, 같은 class의 복사본이 없습니다.기존 Input Actions에 Dash 추가하기
PlayerController의 Move Action 또는 Jump Action이 가리키는 기존 Input Actions 에셋을 Project에서 엽니다. 같은 Action Map의 Actions 목록에서 +로 Dash를 추가합니다. Action Type은 Button, Binding의 Path는 Keyboard / Left Shift로 지정하고 Save Asset을 누릅니다.
화면에서 확인같은 Action Map 안에 Move·Jump·Dash가 있습니다.Player에 PlayerDash 연결하기
Hierarchy의 Player 루트를 선택하고 Add Component → PlayerDash를 추가합니다. 이미 있다면 기존 컴포넌트를 사용합니다. Dash Action 슬롯의 선택 버튼으로 방금 만든 기존 Action Map / Dash를 고릅니다. Dash Speed
12, Dash Duration0.2, Dash Cooldown0.6을 확인합니다.Rigidbody2D의 Collision Detection은 Continuous로 확인합니다. 속도가 빠르므로 얇은 벽을 통과하지 않는지는 Play에서 따로 확인해야 합니다.
화면에서 확인Dash Action이 None이 아니고, PlayerController도 그대로 켜져 있습니다.Game View에서 대시만 먼저 확인하기
아직 PlayerAnimation은 붙이지 않습니다. 앞 단계부터 연결되어 있었다면 참조를 모두 채운 상태로 꺼 둡니다. Play를 누르고 Game View 안을 한 번 클릭한 뒤, 좌우로 이동하고 Left Shift를 누릅니다. 왼쪽으로 이동한 뒤 멈춰서 대시하고, 점프 중에도 눌러 봅니다.
그림이 Idle이어도 몸체가 마지막 이동 방향으로 0.2초 동안 빨라지고, 점프·낙하가 이어지면 통과입니다. Shift를 길게 눌러도 한 번만 실행되어야 합니다. 재사용은 대시 종료 후 0.6초를 기다리고 버튼을 떼었다 다시 누릅니다. 확인 뒤 Play를 종료합니다.
다음 단계로 가기 전이동·점프·대시가 작동하고 Console Error가 0입니다.
키를 눌러도 움직이지 않으면 Game View를 다시 클릭합니다. 이동은 되는데 대시만 안 되면 Dash Action·Binding·PlayerDash 활성 상태부터 확인합니다. 이 단계에서는 그림이 Dash로 바뀌는지보다 실제 이동을 먼저 봅니다.
대시 이동과 대시 그림의 시간을 맞춥니다
Clip의 길이와 Rigidbody2D가 빨라지는 시간은 서로 다른 설정입니다.
PlayerDash는 마지막으로 실제 가로 이동한 방향으로 대시합니다. 처음부터 정지해 있었다면 오른쪽입니다. 버튼을 누르고 있어도 한 번만 시작하며, 종료 후 0.6초가 지나 다시 눌러야 합니다. 대시 중에는 방향을 바꾸지 않고, 수직 속도와 중력은 계속 적용합니다.
| 설정 | 시작값 | 의미 |
|---|---|---|
| Dash Speed | 12 | 대시 중 가로 속도의 크기 · Unit/초 |
| Dash Duration | 0.2 | 가로 속도를 덮어쓰는 시간 · 초 |
| Dash Cooldown | 0.6 | 대시 종료 후 다음 대시까지의 대기 · 초 |
Vector2 velocity = body.linearVelocity;
velocity.x = dashDirection * dashSpeed;
body.linearVelocity = velocity;현재 속도를 가져온 뒤 x만 바꾸므로 낙하 중의 y는 남습니다. PlayerController도 FixedUpdate에서 가로 속도를 쓰기 때문에, PlayerDash는 DefaultExecutionOrder(100)으로 기본 PlayerController보다 뒤에서 실행합니다. 별도 Script Execution Order가 있다면 이 순서를 확인합니다.
Dash Clip은 4프레임 ÷ Samples 20 = 0.2초입니다. Dash Duration도 0.2초로 시작합니다. 그림 길이만 늘려도 이동 시간이 늘지는 않습니다. 값 비교는 기본 연결이 끝난 뒤 하나씩 합니다.
전체 코드 확인 · PlayerDash.cs
using UnityEngine;
using UnityEngine.InputSystem;
// The week 4 PlayerController runs first; only an active dash overrides X.
[DefaultExecutionOrder(100)]
[RequireComponent(typeof(Rigidbody2D))]
public class PlayerDash : MonoBehaviour
{
[SerializeField] private InputActionReference dashAction;
[SerializeField, Min(0.01f)] private float dashSpeed = 12f;
[SerializeField, Min(0.01f)] private float dashDuration = 0.2f;
[SerializeField, Min(0f)] private float dashCooldown = 0.6f;
private Rigidbody2D body;
private bool dashRequested;
private float remaining;
private float cooldownRemaining;
private float facing = 1f;
private float dashDirection = 1f;
public bool IsDashing { get; private set; }
private void Awake()
{
body = GetComponent<Rigidbody2D>();
}
private void OnEnable()
{
if (dashAction == null)
{
Debug.LogError("PlayerDash: Dash Action에 Player/Dash를 연결하세요.", this);
enabled = false;
return;
}
dashAction.action.Enable();
}
private void OnDisable()
{
if (dashAction != null) dashAction.action.Disable();
dashRequested = false;
remaining = 0f;
cooldownRemaining = 0f;
IsDashing = false;
}
private void Update()
{
if (!IsDashing && Mathf.Abs(body.linearVelocity.x) > 0.01f)
facing = Mathf.Sign(body.linearVelocity.x);
if (dashAction.action.WasPressedThisFrame()) dashRequested = true;
}
private void FixedUpdate()
{
cooldownRemaining = Mathf.Max(0f, cooldownRemaining - Time.fixedDeltaTime);
if (dashRequested && remaining <= 0f && cooldownRemaining <= 0f)
{
remaining = dashDuration;
cooldownRemaining = dashDuration + dashCooldown;
dashDirection = facing;
}
dashRequested = false;
IsDashing = remaining > 0f;
if (!IsDashing) return;
Vector2 velocity = body.linearVelocity;
velocity.x = dashDirection * dashSpeed;
body.linearVelocity = velocity;
remaining = Mathf.Max(0f, remaining - Time.fixedDeltaTime);
}
}
4단계 · 바닥을 구분하고 상태를 연결합니다
4주차에는 Player와 바닥이 모두 Default Layer여도 이동할 수 있었습니다. 이제 그림의 바닥 판정에 쓸 지지면을 Ground Layer로 구분합니다.
Layer는 오브젝트를 묶는 분류이고, Ground Layers는 PlayerAnimation이 바닥 접촉을 읽을 분류를 고르는 목록입니다. Ground라는 이름을 만드는 것만으로 바닥이 지정되지는 않습니다. 실제 Collider가 있는 오브젝트에도 그 Layer를 적용합니다.
Ground Layer 만들고 지지면에 지정하기
Play가 꺼져 있는지 확인합니다. 바닥 오브젝트의 Inspector 위쪽 Layer → Add Layer에서 빈 User Layer에
Ground를 만듭니다. 이미 있다면 그대로 사용합니다. Hierarchy로 돌아와 바닥·발판·올라설 상자 중 Collider2D가 붙은 오브젝트를 선택하고 Layer를 Ground로 바꿉니다. Collider가 자식에 있다면 그 자식의 Layer도 확인합니다.Player의 Layer는 기존 값을 유지합니다. Player와 바닥이 모두 Default였다면 지지면만 Ground로 변경합니다. Tag와 Sorting Layer는 이 설정과 다릅니다.
화면에서 확인실제 바닥·발판 Collider의 오브젝트는 Ground이고 Player는 Ground가 아닙니다.기존 물리 접촉 유지하기
바닥·발판의 Collider2D는 Is Trigger Off로 둡니다.
Edit → Project Settings → Physics 2D → Layer Collision Matrix에서 Ground와 Player가 현재 사용하는 Layer의 접촉이 켜져 있는지 확인합니다. Player가 Default라면 Ground와 Default 사이를 확인합니다. 이 접촉을 끄면 캐릭터가 바닥을 통과합니다.화면에서 확인그림 판정을 위한 분류를 바꿔도 기존 충돌은 유지됩니다.Visual의 PlayerAnimation 참조 채우기
Visual에 PlayerAnimation을 추가합니다. 이미 있다면 기존 컴포넌트를 사용합니다. Body에는 부모 Player의 Rigidbody2D, Dash에는 부모 Player의 PlayerDash를 연결합니다. Ground Layers는 Ground만 체크하고 Artwork Faces Right는 켭니다. Animator의 Controller도 연결되어 있는지 확인합니다.
Default 전체나 Everything을 Ground Layers로 고르지 않습니다. Player의 Layer와 이후 카메라 경계에 쓸 CameraBoundary도 제외합니다. 참조를 모두 채운 뒤 PlayerAnimation을 켭니다.
화면에서 확인Body·Dash·Ground Layers·Controller가 설정되었고 세 컴포넌트가 켜져 있습니다.
PlayerDash의 입력 확인과 Ground 설정이 끝났습니다. 아래에서 상태 선택 순서를 읽고, 값과 그림 함께 확인에서 실제 조작으로 검증합니다.
실제 동작을 State 번호로 바꿉니다
PlayerAnimation은 입력 버튼보다 실행 결과를 봅니다. 오른쪽 키를 눌러도 벽에 막혀 가로 속도가 거의 0이면 Move가 아닐 수 있습니다.
먼저 대시 중인지 봅니다. 대시가 아니면 바닥에 서 있는지 보고, 공중에서는 수직 속도로 Jump와 Fall을 구분합니다. 바닥에서는 가로 속도로 Idle과 Move를 구분합니다. 한 번의 Update에서 위에서부터 먼저 맞는 가지 하나만 선택합니다.
| 순서 | 실제 상황 | State | 선택 동작 |
|---|---|---|---|
| 1 | PlayerDash.IsDashing이 true | 4 | Dash |
| 2 | 바닥이 아니고 Y 속도 > 0.01 | 2 | Jump |
| 3 | 바닥이 아니고 Y 속도 ≤ 0.01 | 3 | Fall |
| 4 | 바닥이고 |X 속도| > 0.01 | 1 | Move |
| 5 | 나머지 바닥 정지 | 0 | Idle |
조건을 한 단계씩 따라가 봅니다
상황을 고르고 다음 단계를 누릅니다. 한 번의 Update에서 읽은 값을 고정해 두고, 조건 검사 → 번호 저장 → Animator 전달 순서로 확인합니다. 조건에 맞지 않는 가지는 건너뜁니다.
- 대시 중 ·
dash.IsDashing - false
- 바닥 접촉 ·
grounded - false
- 세로 속도 ·
body.linearVelocity.y - 3
- 가로 속도 ·
horizontalSpeed - 0
- 지역 변수
state - 아직 값 없음
- Animator의
State - 아직 전달하지 않음
int state;
if (dash.IsDashing)
{
state = 4; // Dash: 대시 중
}
else if (grounded == false)
{
// 공중에서는 세로 속도로 상승과 낙하를 구분합니다.
if (body.linearVelocity.y > 0.01f)
{
state = 2; // Jump: 위로 올라가는 중
}
else
{
state = 3; // Fall: 정점에 도달했거나 내려오는 중
}
}
else
{
// 바닥에서는 가로 속도로 이동과 정지를 구분합니다.
if (Mathf.Abs(horizontalSpeed) > 0.01f)
{
state = 1; // Move: 좌우로 이동 중
}
else
{
state = 0; // Idle: 서 있는 중
}
}
animator.SetInteger(StateId, state);바깥쪽 if / else if / else는 대시 중 → 공중 → 바닥 순서로 상황을 나눕니다. grounded == false는 바닥에 서 있지 않다는 뜻입니다. 공중과 바닥의 중괄호 안에서는 다시 if / else로 두 동작 중 하나를 고릅니다. Mathf.Abs(horizontalSpeed)는 가로 속도의 부호를 빼고 크기만 보므로 왼쪽과 오른쪽 이동을 모두 판단할 수 있습니다. 0.01f는 아주 작은 속도를 정지에 가깝다고 보기 위한 기준입니다.
SetInteger(StateId, state)가 Animator의 Int Parameter State에 번호를 보냅니다. 예를 들어 공중에서 Y 속도가 양수면 2, 정점에서 0에 가까워지면 3을 보냅니다. Animator는 앞서 만든 Equals 조건으로 Jump 또는 Fall을 선택합니다.
grounded는 Ground Layers에 속한 Collider와 실제로 접촉하면서 아래에서 지지받고 있는지 확인한 결과입니다. 벽이나 천장에 닿은 것을 착지로 세지 않도록 접촉 방향을 검사합니다. 상승 속도도 확인해 점프 직후 잠깐 남은 바닥 접촉을 걸러냅니다. 이 판정은 그림 선택용이며, 4주차 점프 가능 횟수를 바꾸지 않습니다.
float horizontalSpeed = body.linearVelocity.x;
animator.SetFloat(SpeedId, Mathf.Abs(horizontalSpeed));
if (Mathf.Abs(horizontalSpeed) > 0.01f)
{
bool movingLeft = horizontalSpeed < 0f;
if (artworkFacesRight)
{
spriteRenderer.flipX = movingLeft;
}
else
{
spriteRenderer.flipX = !movingLeft;
}
}Float Parameter Speed에는 실제 가로 속도의 절댓값을 보냅니다. 이번 전환은 State로 선택하므로 Speed는 관찰용입니다. 상태 상자의 Speed 1은 Clip 재생 배율이고, 이 Parameter와 자동으로 연결되지 않습니다.
flipX는 그림만 좌우로 뒤집습니다. Player 루트의 Scale을 음수로 바꾸지 않으므로 Collider나 자식 구조에 영향을 주지 않습니다. 가로 속도가 거의 0이면 방향을 갱신하지 않아 마지막으로 본 방향을 유지합니다.
전체 코드 확인 · PlayerAnimation.cs
using UnityEngine;
[RequireComponent(typeof(Animator), typeof(SpriteRenderer))]
public class PlayerAnimation : MonoBehaviour
{
[SerializeField] private Rigidbody2D body;
[SerializeField] private PlayerDash dash;
[SerializeField] private LayerMask groundLayers;
[SerializeField] private bool artworkFacesRight = true;
private Animator animator;
private SpriteRenderer spriteRenderer;
private static readonly int SpeedId = Animator.StringToHash("Speed");
private static readonly int StateId = Animator.StringToHash("State");
private readonly ContactPoint2D[] contacts = new ContactPoint2D[16];
private ContactFilter2D groundFilter;
private void Awake()
{
animator = GetComponent<Animator>();
spriteRenderer = GetComponent<SpriteRenderer>();
if (body == null || dash == null || groundLayers.value == 0)
{
Debug.LogError("PlayerAnimation: Body, Dash, Ground Layers를 연결하세요.", this);
enabled = false;
return;
}
groundFilter.SetLayerMask(groundLayers);
groundFilter.useTriggers = false;
}
private void Update()
{
float horizontalSpeed = body.linearVelocity.x;
animator.SetFloat(SpeedId, Mathf.Abs(horizontalSpeed));
bool grounded = false;
int count = body.GetContacts(groundFilter, contacts);
for (int i = 0; i < count; i++)
{
// A wall or ceiling does not count as support under the player.
if (contacts[i].normal.y > 0.5f && body.linearVelocity.y <= 0.01f)
{
grounded = true;
break;
}
}
// Idle 0, Move 1, Jump 2, Fall 3, Dash 4.
int state;
if (dash.IsDashing)
{
state = 4; // Dash: 대시 중
}
else if (grounded == false)
{
// 공중에서는 세로 속도로 상승과 낙하를 구분합니다.
if (body.linearVelocity.y > 0.01f)
{
state = 2; // Jump: 위로 올라가는 중
}
else
{
state = 3; // Fall: 정점에 도달했거나 내려오는 중
}
}
else
{
// 바닥에서는 가로 속도로 이동과 정지를 구분합니다.
if (Mathf.Abs(horizontalSpeed) > 0.01f)
{
state = 1; // Move: 좌우로 이동 중
}
else
{
state = 0; // Idle: 서 있는 중
}
}
animator.SetInteger(StateId, state);
if (Mathf.Abs(horizontalSpeed) > 0.01f)
{
bool movingLeft = horizontalSpeed < 0f;
if (artworkFacesRight)
{
spriteRenderer.flipX = movingLeft;
}
else
{
spriteRenderer.flipX = !movingLeft;
}
}
}
}
상승이 Jump Clip보다 오래 걸리면 마지막 자세를 유지합니다. 정점에 먼저 닿으면 재생 도중에도 Fall로 바뀝니다. 상승 중 공중 재점프는 State가 계속 2이므로 Clip을 처음부터 재시작하지 않습니다. Fall에서 Jump로 돌아올 때는 새로 시작합니다.
값과 그림이 함께 바뀌는지 확인합니다
여기까지 끝낸 뒤 카메라로 넘어갑니다. 카메라가 고정되어 있어도 상태 전환은 검증할 수 있습니다.
Visual의 Animator와 Game 화면 함께 열기
Play 후 Visual을 선택하고 Animator의 Parameters를 봅니다. Game View 안을 한 번 클릭하고 이동 → 점프 → 정점 → 착지를 실행합니다. State는 직접 편집하지 않습니다. Animator나 Inspector를 클릭한 뒤 조작할 때는 다시 Game View를 클릭합니다. State 값, 활성 상태, Game의 그림이 모두 같은 동작을 가리키는지 봅니다.
화면에서 확인정지 0, 걷기 1, 상승 2, 정점·낙하 3, 대시 4입니다.지상과 공중에서 각각 대시하기
왼쪽으로 이동한 뒤 멈춰 Left Shift를 누릅니다. 이어 점프 중에도 대시합니다. 대시 종료 후 현재 접촉과 속도로 Jump·Fall·Move·Idle 중 하나로 돌아오는지 확인합니다. 버튼을 길게 눌러 연속 발동하지 않는지도 봅니다.
화면에서 확인대시가 끝난 뒤 무조건 Idle로 고정되지 않습니다.그림의 정렬과 기존 물리 확인하기
Jump_01 → 02와 Fall_04 → 01에서 몸통이 갑자기 튀지 않는지 봅니다. 크기나 위치가 바뀌면 1교시 PPU·공통 Pivot을 먼저 확인합니다. 이전과 같은 이동·점프·벽 충돌을 확인하고 Play를 종료합니다.
화면에서 확인그림 때문에 Player의 Collider·Scale을 바꾸지 않았습니다.
5단계 · Cinemachine이 Player를 따라가게 합니다
Cinemachine 3.1 기준입니다. Main Camera가 화면을 그리고, Cinemachine Camera가 추적 구도를 계산합니다.
카메라를 Player의 자식으로 넣으면 부모의 움직임을 그대로 따라가게 됩니다. 이번에는 Player 바깥의 독립된 카메라가 추적 대상의 위치를 읽게 만듭니다. 그림이 흔들려도 추적 기준은 몸체에 남도록 Visual 대신 Player 루트를 연결합니다.
Cinemachine 3.1과 기존 추적 확인하기
시작 전 준비에서 설치한 Cinemachine 3.1 계열을 사용합니다. 아직 설치하지 않았다면 Package Manager의 Unity Registry에서 Cinemachine 3.1을 선택해 Install하고 컴파일 완료를 기다립니다. 2.x의 Virtual Camera·Framing Transposer 설명과 3.1의 속성 이름을 섞지 않습니다.
Main Camera가 Player 자식이면 Hierarchy의 루트로 꺼냅니다. 기존 카메라 추적 스크립트가 있다면 비활성화합니다.
화면에서 확인Main Camera의 Transform을 두 추적 방식이 동시에 제어하지 않습니다.Main Camera에 Brain 확인하기
Main Camera를 선택해 Camera의 Projection을 Orthographic으로 둡니다. 같은 오브젝트에 Cinemachine Brain이 있는지 확인하고, 없으면 Add Component로 추가합니다. Camera와 AudioListener는 그대로 둡니다.
화면에서 확인실제 Camera와 Cinemachine Brain이 같은 Main Camera에 있습니다.Cinemachine Camera 생성하고 대상 연결하기
GameObject → Cinemachine → Cinemachine Camera를 만듭니다. Hierarchy 루트에 두고 Tracking Target에 Player를 드래그합니다. Position Control은 Position Composer, Rotation Control은 None으로 둡니다. Transform Rotation은 (0,0,0)입니다.화면에서 확인추적 대상은 Visual이 아닌 Player이고, 회전은 고정됩니다.Lens와 거리 설정하기
Cinemachine Camera의 Lens → Orthographic Size를 5에서 시작합니다. Position Composer의 Camera Distance는 양수 10으로 둡니다. Z=0 부근의 2D 레벨을 카메라가 앞에서 보게 하는 거리이며, 화면에 보이는 세로 범위는 Orthographic Size로 정합니다.
추적이 연결된 뒤 화면 크기는 활성 Cinemachine Camera의 Lens에서 조정합니다. Main Camera에서만 Size를 바꾸면 Brain이 적용하는 값과 달라질 수 있습니다.
화면에서 확인플레이어와 주변 발판이 Game View에 보입니다.가장 단순한 추적부터 실행하기
Position Composer의 Screen Position (0,0), Damping X/Y/Z 0, Lookahead Off로 시작합니다. Dead Zone과 Hard Limits도 우선 끕니다. Play 후 Game View를 클릭하고 좌우 이동과 점프를 확인합니다. 필요하면 Game View의 Gizmos와 카메라 가이드를 켜 대상 위치를 봅니다.
화면에서 확인경계를 적용하기 전에는 Player가 화면 중앙에 머물며 바로 따라옵니다.
Orthographic Size는 화면 높이의 절반입니다. Size 5이면 높이는 10 Unit입니다. 캐릭터가 너무 작으면 값을 줄이고, 다음 발판이 안 보이면 늘려 비교합니다. Cinemachine Camera의 Lens 설명에서 기준을 확인합니다.
Dead Zone과 Damping을 하나씩 비교합니다
같은 시작 위치에서 같은 짧은 이동을 반복해야 차이를 읽을 수 있습니다.
Dead Zone은 움직임을 허용할 여유 영역입니다
Dead Zone은 Player가 그 안에서 움직여도 카메라가 구도를 다시 맞추지 않는 화면 영역입니다.

Damping은 새 구도를 따라잡는 반응입니다
Damping은 구도를 맞춰야 할 때 얼마나 늦게 따라잡는지 정합니다. 영역의 크기와 추적 반응은 서로 다른 조절입니다.

| 비교 | 고정할 설정 | 바꿀 설정 | 볼 것 |
|---|---|---|---|
| 기준 | Screen Position (0,0), Lookahead Off | Dead Zone Off, Damping 0 | 짧은 이동에도 즉시 추적합니다. |
| Dead Zone | Damping X/Y/Z 0 | Dead Zone On, Size (0.15,0.15) | 중앙 영역 안에서는 배경이 고정되고 밖으로 나가면 추적합니다. |
| Damping | Dead Zone Size (0.15,0.15) | Damping X/Y 0 → 0.5, Z 0 유지 | 영역 밖으로 나간 뒤 구도를 따라잡는 반응이 느려집니다. |
Dead Zone의 0.15는 월드 0.15 Unit이 아니라 화면 폭·높이의 비율입니다. Screen Position (0,0)은 화면 중앙이며 ±0.5가 가장자리입니다. 여기서는 중앙을 유지하고 Dead Zone과 Damping만 비교합니다. Position Composer 속성 설명도 같은 단위를 사용합니다.
Inspector에서 값을 바꾼 뒤에는 Game View를 다시 클릭하고 이동합니다. Play에서 0과 0.5의 차이를 비교했다면 값을 기록합니다. Play를 종료한 뒤 선택한 값을 다시 입력하고 Scene을 저장합니다. 세로 반응이 너무 느려 다음 착지 지점이 늦게 보이지 않는지도 확인합니다.
6단계 · 화면 전체를 경계 안에 둡니다
Confiner 2D는 카메라 중심뿐 아니라 화면 크기를 고려합니다. 레벨의 물리 벽과 카메라 경계는 따로 만듭니다.

CameraBounds의 모양 만들기
Hierarchy 루트에 Create Empty로
CameraBounds를 만듭니다. Transform Rotation (0,0,0), Scale (1,1,1)로 두고 PolygonCollider2D를 추가합니다. Edit Collider로 레벨을 감싸는 직사각형을 만듭니다. Rigidbody2D는 추가하지 않고 Is Trigger는 켭니다.화면에서 확인캐릭터 둘레가 아니라 레벨 전체를 감싼 평면 경계입니다.물리 접촉에서 카메라 경계 제외하기
CameraBounds의 Layer → Add Layer에서
CameraBoundary를 만들고 CameraBounds에 지정합니다.Edit → Project Settings → Physics 2D → Layer Collision Matrix에서 CameraBoundary와 모든 Layer의 접촉을 끕니다. Visual의 Ground Layers에서도 제외합니다.화면에서 확인계산용 경계가 벽이나 바닥으로 취급되지 않습니다.Cinemachine Camera에 경계 연결하기
Cinemachine Camera를 선택합니다. Add Extension에서 Cinemachine Confiner 2D를 추가하고 Bounding Shape 2D에 CameraBounds의 PolygonCollider2D를 드래그합니다. 경계를 바로 확인할 때는 Confiner의 Damping과 Slowing Distance를 0에서 시작합니다.
화면에서 확인Main Camera가 아닌 Cinemachine Camera에 Confiner와 경계 참조가 있습니다.같은 화면 비율로 네 방향 확인하기
Game View의 비율을 16:9로 맞추고 Play한 뒤 Game View를 클릭합니다. 왼쪽·오른쪽·위·아래 끝에서 화면 밖 빈 공간이 보이는지 확인합니다. 닿기 어려운 위쪽은 Play 중 Scene View에서 Player를 옮겨 확인한 뒤 Play를 종료합니다.
화면에서 확인경계에 가까워지면 카메라가 먼저 멈추고 Player가 화면 가장자리 쪽으로 움직입니다.
Orthographic Size 5이면 화면 높이는 10 Unit, 16:9에서 폭은 약 17.78 Unit입니다. 폭 12 Unit의 경계에는 그 화면을 넣을 수 없습니다. Lens Size를 줄이거나 경계를 넓힌 뒤 다시 확인합니다. 화면 비율을 바꾸면 가로 폭도 다시 검사합니다.
경계나 렌즈를 바꿨는데 이전 결과가 남는다면
먼저 Play를 종료한 뒤 수정한 설정으로 다시 확인합니다. Polygon의 점을 편집했다면 Confiner Inspector의 Invalidate Bounding Shape Cache로 경계 캐시를 갱신할 수 있습니다. 실행 중 Lens를 바꾸는 확장 구현에서는 InvalidateLensCache()가 필요합니다. Confiner 2D 공식 문서의 캐시 갱신 조건을 참고합니다.
연결이 끝나면 같은 장면을 다시 실행합니다
Play를 껐다 켜도 같은 결과인지 확인하고 3교시 개인 실습으로 넘깁니다.
| 대상 | 조작 | 기대 결과 |
|---|---|---|
| Animator | 정지 → 걷기 → 상승 → 낙하 → 대시 | State와 활성 상태, Sprite가 같은 동작을 가리킵니다. |
| 방향 | 왼쪽 이동 후 정지 | 그림이 왼쪽을 유지하고 Collider는 그대로입니다. |
| PlayerDash | 지상·공중 대시, 버튼 길게 누르기 | 0.2초 대시, 수직 운동 유지, 누른 채 자동 재발동하지 않습니다. |
| PlayerController | 기존 이동·점프·벽 충돌 | 4주차 동작이 유지됩니다. |
| Cinemachine | 짧게 이동 후 계속 이동 | Dead Zone 안에서 머물고 밖에서 따라옵니다. |
| Confiner | 레벨의 왼쪽·오른쪽·위·아래 | 화면 전체가 경계 안에 머뭅니다. |
Play·Preview·Record를 끄고, 선택한 Damping과 Dead Zone 값을 편집 상태에서 입력합니다. Scene과 Controller를 저장합니다. 3교시에는 이 장면에서 필수 테스트 8개와 설정 변경 비교를 진행합니다.
값이 끊기는 지점부터 찾습니다
입력·접촉·속도 → State → 활성 상태 → Sprite → 카메라 순서로 좁혀 봅니다.
수동으로 바꾼 State가 바로 원래 번호로 돌아옵니다
Visual의 PlayerAnimation이 값을 계속 쓰고 있는지 확인합니다. 수동 확인 중에만 이 컴포넌트를 끄고 Animator는 켜 둡니다. 수동 확인이 끝나면 다시 켭니다.
State는 바뀌는데 그림이 그대로입니다
전환 화살표의 State / Int / Equals 값과 목적지 상태의 Motion을 확인합니다. Visual에 연결된 Controller를 보고 있는지도 봅니다. 첫 프레임만 반복되면 다섯 전환의 Can Transition To Self가 꺼졌는지 확인합니다.
계속 Fall이고, 바닥에서 Idle로 돌아오지 않습니다
Ground 설정 순서로 돌아갑니다. Collider가 붙은 실제 바닥·발판의 Layer가 Ground이고 PlayerAnimation의 Ground Layers에도 Ground가 선택되었는지 봅니다. Tag를 바꾸지 않았는지, 바닥 Collider의 Is Trigger가 꺼져 있고 Ground와 Player Layer의 물리 접촉이 켜져 있는지 확인합니다. 벽이나 천장 접촉은 착지로 세지 않습니다.
Left Shift를 눌러도 대시하지 않습니다
먼저 Game View를 클릭하고 다시 누릅니다. 그래도 안 되면 Console의 첫 오류, Dash Action 참조, Input Actions의 저장 여부와 Binding을 확인합니다. Cooldown이 지난 뒤 버튼을 떼었다 다시 누릅니다. 기본 이동은 되는데 대시만 안 되면 PlayerDash의 활성 상태와 실행 순서도 봅니다.
Body·Dash·Ground Layers 연결 오류가 나옵니다
Play를 종료하고 Visual의 PlayerAnimation 참조를 모두 채운 뒤 컴포넌트를 켜고 다시 실행합니다. 단순히 꺼 두어도 최초 Awake의 참조 검사는 실행됩니다. PlayerDash와 PlayerAnimation 파일을 모두 가져왔는지, 같은 class가 중복되지는 않았는지도 확인합니다.
점프로 바뀔 때 그림만 커지거나 튑니다
Player의 Scale을 고치기 전에 1교시 최신 PNG와 자동 정렬 도구의 적용 여부를 확인합니다. Clip에 Transform 트랙이 기록되어 있는지도 봅니다.
카메라가 따라오지 않거나 화면이 비어 있습니다
Main Camera의 Brain, Cinemachine Camera 활성 상태, Tracking Target, Position Composer, Camera Distance와 Lens를 순서대로 확인합니다. 다른 Cinemachine Camera가 있다면 어느 카메라가 Live인지 보고, 기존 추적 코드가 동시에 켜져 있는지도 확인합니다.
경계에 닿자 Player가 화면 중앙에서 벗어납니다
Confiner가 레벨 밖을 보여 주지 않도록 카메라를 멈춘 결과일 수 있습니다. 화면이 경계 안에 남아 있고 Player가 보인다면 정상입니다. 경계가 화면보다 작은 경우는 Size와 경계 크기부터 맞춥니다.
갑자기 점프나 착지가 달라졌습니다
Player 루트의 Scale·Collider·PlayerController를 바꾸지 않았는지 확인합니다. CameraBoundary의 물리 접촉이 모두 꺼져 있고 Ground Layers에서 제외되었는지도 확인합니다.
선택과 추적을 말로 설명합니다
설정 이름만 외우지 않고 어떤 화면을 만드는지 연결합니다.
- 공중에서 대시하면 State가 4인 이유는 ______입니다.
- Jump의 Loop Time Off와 Has Exit Time Off는 각각 ______, ______를 뜻합니다.
- 카메라의 추적 대상이 Visual이 아니라 Player인 이유는 ______입니다.
- Dead Zone과 Damping 중 추적을 시작하지 않는 영역을 바꾸는 것은 ______입니다.
공식 자료
Unity 6.6과 Cinemachine 3.1의 실제 속성 이름을 확인합니다.
- Animator.SetInteger ↗State 번호 전달
- Rigidbody2D.GetContacts ↗실제 바닥 접촉 읽기
- DefaultExecutionOrder ↗이동과 대시의 실행 순서
- InputAction ↗Dash 입력과 WasPressedThisFrame
- Game View 입력 포커스 ↗Play 중 키보드 입력 확인
- MonoBehaviour.Awake ↗초기 참조 검사 시점
- Animator 전환 ↗조건과 Has Exit Time
- Animator.SetFloat ↗Float Parameter 갱신
- SpriteRenderer.flipX ↗렌더링만 좌우 반전
- Cinemachine 추적 설정 ↗Main Camera와 Cinemachine Camera
- Position Composer ↗Dead Zone과 Damping
- Confiner 2D ↗카메라 경계와 Collider 설정
