본문으로 건너뛰기
Unity C# 기초집LESSON 24

최종 실습: 2D 택배 분류 게임

난이도초급
예상 시간120분
선수지식1~23강 결과물

24강. 최종 실습: 2D 택배 분류 게임

1. 이번 강의에서 해결할 문제

각 기능은 따로 동작하지만 택배 생성부터 분류, 점수·목숨, 게임 오버와 결과 화면까지 하나의 플레이 흐름으로 연결하고 경계 상황을 검증해야 합니다.

2. 학습 목표

3. 핵심 개념

최종 구조는 한 거대한 GameManager가 아니라 다음 흐름으로 연결됩니다.

PackageSpawner --Get--> PackagePool --Activate--> PackageItem
│ │
└─ PackageTypeSelector └─ Trigger 도착

SortZone / MissZone ──결과 요청──> GameSession ──event──> HUD / Feedback / SceneFlow
  • Inspector: Prefab, 데이터 에셋, Zone 위치, 기본 수치 연결
  • C#: 생성 제한, 이동, 판정, 점수·목숨과 상태 전환
  • 이벤트: GameSession과 UI·피드백의 결합 제거
  • 멀티플레이, 서버 저장, 광고 SDK는 이 과정 범위 밖입니다.

4. 단계별 실습

4-1. 최종 폴더와 Scene 구성

Assets/DeliverySorter/
├─ Data/PackageTypes/{Blue,Orange}.asset
├─ Prefabs/Package.prefab
├─ Scenes/{Game,Result}.unity
└─ Scripts/
├─ Data/PackageTypeData.cs
├─ Packages/{PackageItem,PackageMotor2D}.cs
├─ Spawning/{PackagePool,PackageSpawner,PackageTypeSelector}.cs
├─ Zones/{SortZone,MissZone}.cs
├─ Game/{GameSession,SceneFlow}.cs
├─ UI/HUDPresenter.cs
└─ Feedback/SortFeedback.cs

Unity Editor·Inspector 작업

  1. Package Prefab에 SpriteRenderer, Rigidbody2D, BoxCollider2D, PackageItem과 PackageMotor2D를 둡니다.
  2. Rigidbody2D는 Dynamic, Gravity Scale 0, Freeze Rotation Z로 설정합니다.
  3. BlueZone·OrangeZone·MissZone의 Collider2D는 Is Trigger로 둡니다.
  4. PackagePool에 Prefab, PackageSpawner에 Pool·SpawnPoint·두 데이터 에셋을 연결합니다.
  5. GameSession과 HUDPresenter, SortFeedback 참조를 연결합니다.

4-2. 최종 분류와 놓침 판정

C# · 파일 경로: Assets/DeliverySorter/Scripts/Zones/SortZone.cs

SortZone.cs
using UnityEngine;

public sealed class SortZone : MonoBehaviour
{
[SerializeField] private PackageTypeData acceptedType;
[SerializeField] private GameSession session;

private void Awake()
{
if (acceptedType == null || session == null)
Debug.LogError("SortZone reference is missing.", this);
}

private void OnTriggerEnter2D(Collider2D other)
{
if (!other.TryGetComponent(out PackageItem package)) return;
if (package.IsResolved || package.Type == null) return;

session.RegisterSort(package.Type, acceptedType);
package.ResolveAndRelease();
}
}

C# · 파일 경로: Assets/DeliverySorter/Scripts/Zones/MissZone.cs

MissZone.cs
using UnityEngine;

public sealed class MissZone : MonoBehaviour
{
[SerializeField] private GameSession session;

private void OnTriggerEnter2D(Collider2D other)
{
if (!other.TryGetComponent(out PackageItem package)) return;
if (package.IsResolved) return;
session.RegisterFailure();
package.ResolveAndRelease();
}
}

4-3. 최종 생성 루프

C# · 파일 경로: Assets/DeliverySorter/Scripts/Spawning/PackageSpawner.cs

PackageSpawner.cs
using System.Collections;
using UnityEngine;

public sealed class PackageSpawner : MonoBehaviour
{
[SerializeField] private PackagePool pool;
[SerializeField] private Transform spawnPoint;
[SerializeField] private PackageTypeSelector selector;
[SerializeField, Min(0.1f)] private float startInterval = 1.5f;
[SerializeField, Min(0.1f)] private float minInterval = 0.45f;

private Coroutine routine;
private float startedAt;

public void StartRun()
{
if (routine != null) return;
startedAt = Time.time;
routine = StartCoroutine(SpawnLoop());
}

public void StopRun()
{
if (routine == null) return;
StopCoroutine(routine);
routine = null;
}

private IEnumerator SpawnLoop()
{
while (true)
{
PackageTypeData type = selector.Next();
if (type != null) pool.Get(type, spawnPoint.position);

float minutes = (Time.time - startedAt) / 60f;
float interval = Mathf.Max(minInterval, startInterval - minutes * 0.2f);
yield return new WaitForSeconds(interval);
}
}
}

StateChanged를 구독하는 Binder가 Playing에서 StartRun, Paused·GameOver에서 StopRun을 호출합니다. GameOver에서는 입력도 비활성화하고 최고 점수를 저장한 뒤 결과 화면 버튼을 제공합니다.

4-4. 실행

PowerShell · 실행 위치: DeliverySorter2D 프로젝트 루트

Get-ChildItem .\Assets\DeliverySorter\Scripts -Recurse -Filter *.cs
Get-Content .\ProjectSettings\ProjectVersion.txt

Unity Editor에서 Console을 비우고 Game Scene을 Play합니다. C# 컴파일과 실제 Play Mode 검증은 반드시 에디터에서 수행합니다.

5. 코드가 동작하는 이유

Spawner는 Coroutine으로 생성 시점만 관리하고 selector가 실제 타입을 결정해 같은 종류가 최대 세 번까지만 이어집니다. Pool은 인스턴스를 재사용하고 PackageItem은 한 번만 해결됩니다. Zone은 결과만 GameSession에 전달하며, GameSession 이벤트를 HUD와 피드백이 구독하므로 UI가 점수 규칙에 섞이지 않습니다.

구조 선택 근거

구조선택 이유
Update·FixedUpdate입력은 프레임, Rigidbody2D 이동은 물리 주기마다 필요
Coroutine생성 간격과 난이도 대기를 순서대로 표현
Object Pool빠르게 반복되는 택배 생성·제거 비용과 GC 할당 완화
ScriptableObject타입별 색상·점수를 Prefab 복제 없이 공유
event게임 규칙이 HUD와 사운드의 구체 구현을 모르게 함

6. 자주 하는 실수와 해결법

7. 직접 실습

8. 이해 점검 질문 3개

9. 핵심 요약

MINI QUIZ

최종 실습: 2D 택배 분류 게임 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1“최종 실습: 2D 택배 분류 게임” 기능을 확장하기 좋은 구조로 설명한 것은 무엇인가요?
  2. 문제 2“최종 실습: 2D 택배 분류 게임” 실습 중 ‘Update에서 Find 반복’ 상황을 발견했습니다. 본문과 일치하는 설명은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

24강. 최종 실습: 2D 택배 분류 게임 미완료 상태