콘텐츠 대표 이미지 - Unity 2D 게임 개발을 위한 핵심 소스코드 예제 가이드
🎮 프로그램 / 소스코드

Unity 2D 게임 개발을 위한 핵심 소스코드 예제 가이드

실전에서 바로 쓰는 Unity 2D 기본기 총정리 🕹️

🎯 🧩 💻 🚀 🎲
void Start () { // 게임 시작! rb = GetComponent <Rigidbody2D>(); anim = GetComponent <Animator>(); } 플레이어 이동 Move / Jump 충돌 감지 Collision / Trigger 애니메이션 Animator 연동 Unity 2D 게임 개발 핵심 소스코드 예제 모음
🎮 들어가기 전에 — Unity 2D, 왜 배워야 해?

솔직히 말하면 Unity 2D는 진입장벽이 낮으면서도 퀄리티 높은 게임을 만들 수 있는 최강 엔진이야 ㅋㅋㅋ
인디게임 시장에서 Unity 2D로 만든 게임이 전체의 약 45% 이상을 차지할 정도로 압도적인 점유율을 자랑하고,
모바일·PC·콘솔 멀티플랫폼 빌드도 한 번에 가능해서 진짜 효율 갑이거든 😎

근데 막상 시작하면 "어디서부터 코드를 짜야 하지?" 하고 멘붕 오는 경우가 많잖아?
이 글에서는 Unity 2D 게임 개발에서 실제로 자주 쓰이는 핵심 소스코드 예제들을 카테고리별로 싹 정리해줄게.
복붙해서 바로 쓸 수 있는 실전 코드 위주로 구성했으니까 북마크 필수임 ㅋㅋ


🏃 1. 플레이어 이동 — 2D 게임의 기본 중의 기본

2D 게임에서 플레이어 이동은 Rigidbody2D를 활용하는 방식이 표준이야.
물리 엔진을 그대로 활용하니까 중력, 충돌 처리가 자동으로 되거든. 개꿀 ㅋㅋ

📌 기본 좌우 이동 + 점프 코드
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // 이동 속도와 점프력 설정
    public float moveSpeed = 5f;
    public float jumpForce = 10f;

    private Rigidbody2D rb;
    private bool isGrounded = false;

    void Start()
    {
        // Rigidbody2D 컴포넌트 가져오기
        rb = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        // 좌우 입력 받기 (-1 ~ 1 사이 값)
        float moveInput = Input.GetAxis("Horizontal");

        // 속도 적용 (x축만 변경, y축은 물리 엔진에 맡김)
        rb.linearVelocity = new Vector2(moveInput * moveSpeed, rb.linearVelocity.y);

        // 점프 처리 (땅에 있을 때만 가능)
        if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
        {
            rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce);
            isGrounded = false;
        }
    }

    // 땅 감지 — 태그가 "Ground"인 오브젝트와 충돌 시
    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Ground"))
        {
            isGrounded = true;
        }
    }
}
💡 꿀팁! rb.velocity는 Unity 2022 이후 버전에서 rb.linearVelocity로 변경됐어.
버전 확인하고 맞게 써야 함! 안 그러면 경고 뜨는 거 알지? ㅋㅋ
📌 스프라이트 방향 전환 (좌우 반전)
// Update() 안에 추가
void FlipSprite(float moveInput)
{
    if (moveInput > 0)
    {
        // 오른쪽 이동 시 스프라이트 정방향
        transform.localScale = new Vector3(1f, 1f, 1f);
    }
    else if (moveInput < 0)
    {
        // 왼쪽 이동 시 스프라이트 좌우 반전
        transform.localScale = new Vector3(-1f, 1f, 1f);
    }
}

이렇게 localScale.x를 -1로 바꾸면 스프라이트가 좌우 반전돼서 캐릭터가 방향을 바라보게 돼.
진짜 간단한데 이거 모르면 캐릭터가 항상 한쪽만 보고 있어서 어색하거든 ㅋㅋㅋ


🎬 2. 애니메이션 연동 — 움직임에 생명을 불어넣기

Unity의 Animator 컴포넌트와 C# 스크립트를 연결하면 상태에 따라 자동으로 애니메이션이 전환돼.
Animator Controller에서 파라미터를 설정하고, 코드에서 그 값을 바꿔주는 방식이야 😊

using UnityEngine;

public class PlayerAnimation : MonoBehaviour
{
    private Animator anim;
    private Rigidbody2D rb;

    void Start()
    {
        anim = GetComponent<Animator>();
        rb = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        // 이동 속도의 절댓값을 "Speed" 파라미터에 전달
        // Animator에서 Speed > 0.1 이면 Walk 애니메이션 재생
        anim.SetFloat("Speed", Mathf.Abs(rb.linearVelocity.x));

        // 점프 중인지 여부를 "IsJumping" 파라미터에 전달
        anim.SetBool("IsJumping", rb.linearVelocity.y > 0.1f);

        // 낙하 중인지 여부
        anim.SetBool("IsFalling", rb.linearVelocity.y < -0.1f);
    }

    // 공격 애니메이션 트리거 (버튼 누를 때 호출)
    public void TriggerAttack()
    {
        anim.SetTrigger("Attack");
    }
}
💡 Animator Controller에서 파라미터 이름을 정확히 동일하게 설정해야 해.
대소문자도 구분하니까 "speed"랑 "Speed"는 다른 파라미터야! 이거 때문에 삽질하는 사람 진짜 많음 ㅋㅋ

💥 3. 충돌 감지 & 트리거 — 게임 이벤트의 핵심

Unity 2D에서 충돌 처리는 크게 두 가지야.
OnCollision2D — 물리적 충돌 (실제로 부딪히는 것)
OnTrigger2D — 트리거 충돌 (통과하면서 이벤트 발생)

📌 아이템 획득 (Trigger 방식)
using UnityEngine;

public class ItemPickup : MonoBehaviour
{
    public int scoreValue = 10;

    // 트리거 영역에 들어왔을 때
    void OnTriggerEnter2D(Collider2D other)
    {
        // 플레이어 태그를 가진 오브젝트만 처리
        if (other.CompareTag("Player"))
        {
            // GameManager의 점수 추가 메서드 호출
            GameManager.Instance.AddScore(scoreValue);

            // 아이템 획득 효과음 재생
            AudioManager.Instance.PlaySFX("ItemPickup");

            // 아이템 오브젝트 삭제
            Destroy(gameObject);
        }
    }
}
📌 데미지 처리 (Collision 방식)
using UnityEngine;

public class EnemyDamage : MonoBehaviour
{
    public int damageAmount = 10;

    // 물리 충돌 발생 시
    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Player"))
        {
            // 플레이어의 Health 컴포넌트 가져오기
            PlayerHealth playerHealth = 
                collision.gameObject.GetComponent<PlayerHealth>();

            if (playerHealth != null)
            {
                playerHealth.TakeDamage(damageAmount);
            }
        }
    }
}
구분 Collision2D Trigger2D
물리 반응 있음 (튕겨남) 없음 (통과)
Is Trigger 설정 체크 해제 체크 필요
주요 사용처 벽, 바닥, 적 충돌 아이템, 포탈, 감지 영역
콜백 메서드 OnCollisionEnter2D OnTriggerEnter2D

❤️ 4. 체력 시스템 — HP 관리의 정석

RPG든 플랫포머든 체력 시스템은 거의 모든 게임에 들어가잖아?
싱글톤 패턴이나 컴포넌트 방식으로 구현하는 게 일반적이야 ㅋㅋ

using UnityEngine;
using UnityEngine.Events;

public class PlayerHealth : MonoBehaviour
{
    [Header("체력 설정")]
    public int maxHealth = 100;
    private int currentHealth;

    // 체력 변화 시 UI 업데이트를 위한 이벤트
    public UnityEvent<int, int> OnHealthChanged;
    public UnityEvent OnPlayerDied;

    // 무적 시간 관련
    private bool isInvincible = false;
    public float invincibleDuration = 1.5f;

    void Start()
    {
        currentHealth = maxHealth;
        // 시작 시 UI 초기화
        OnHealthChanged?.Invoke(currentHealth, maxHealth);
    }

    // 데미지 받기
    public void TakeDamage(int damage)
    {
        // 무적 상태면 데미지 무시
        if (isInvincible) return;

        currentHealth -= damage;
        currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth);

        // 체력 변화 이벤트 발생
        OnHealthChanged?.Invoke(currentHealth, maxHealth);

        if (currentHealth <= 0)
        {
            Die();
        }
        else
        {
            // 무적 시간 시작
            StartCoroutine(InvincibleCoroutine());
        }
    }

    // 체력 회복
    public void Heal(int amount)
    {
        currentHealth += amount;
        currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth);
        OnHealthChanged?.Invoke(currentHealth, maxHealth);
    }

    // 사망 처리
    private void Die()
    {
        OnPlayerDied?.Invoke();
        // 사망 애니메이션, 게임오버 처리 등
        Debug.Log("플레이어 사망!");
    }

    // 무적 시간 코루틴
    private System.Collections.IEnumerator InvincibleCoroutine()
    {
        isInvincible = true;
        yield return new WaitForSeconds(invincibleDuration);
        isInvincible = false;
    }
}
⚠️ 주의! Mathf.Clamp로 체력 값을 0~maxHealth 사이로 제한하지 않으면
음수 체력이나 최대치 초과 같은 버그가 생길 수 있어. 꼭 넣어줘야 함!

🤖 5. 적 AI — 기본 추적 & 순찰 패턴

간단한 2D 게임에서 적 AI는 순찰(Patrol)추적(Chase) 두 가지 상태로 구현하는 게 기본이야.
State Machine 패턴을 쓰면 나중에 확장도 쉬워서 강추임 ㅋㅋ

using UnityEngine;

public class EnemyAI : MonoBehaviour
{
    // 적 상태 열거형
    public enum EnemyState { Patrol, Chase, Attack }
    public EnemyState currentState = EnemyState.Patrol;

    [Header("이동 설정")]
    public float patrolSpeed = 2f;
    public float chaseSpeed = 4f;
    public float detectionRange = 5f;  // 플레이어 감지 범위
    public float attackRange = 1.5f;   // 공격 범위

    [Header("순찰 설정")]
    public Transform[] patrolPoints;   // 순찰 지점 배열
    private int currentPatrolIndex = 0;

    private Transform player;
    private Rigidbody2D rb;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        // 씬에서 "Player" 태그 오브젝트 찾기
        player = GameObject.FindGameObjectWithTag("Player").transform;
    }

    void Update()
    {
        // 플레이어와의 거리 계산
        float distToPlayer = Vector2.Distance(transform.position, player.position);

        // 상태 전환 로직
        if (distToPlayer <= attackRange)
        {
            currentState = EnemyState.Attack;
        }
        else if (distToPlayer <= detectionRange)
        {
            currentState = EnemyState.Chase;
        }
        else
        {
            currentState = EnemyState.Patrol;
        }

        // 상태별 행동 실행
        switch (currentState)
        {
            case EnemyState.Patrol:
                Patrol();
                break;
            case EnemyState.Chase:
                ChasePlayer();
                break;
            case EnemyState.Attack:
                AttackPlayer();
                break;
        }
    }

    void Patrol()
    {
        if (patrolPoints.Length == 0) return;

        Transform target = patrolPoints[currentPatrolIndex];
        Vector2 direction = (target.position - transform.position).normalized;

        rb.linearVelocity = new Vector2(direction.x * patrolSpeed, rb.linearVelocity.y);

        // 순찰 지점 도달 시 다음 지점으로
        if (Vector2.Distance(transform.position, target.position) < 0.3f)
        {
            currentPatrolIndex = (currentPatrolIndex + 1) % patrolPoints.Length;
        }
    }

    void ChasePlayer()
    {
        Vector2 direction = (player.position - transform.position).normalized;
        rb.linearVelocity = new Vector2(direction.x * chaseSpeed, rb.linearVelocity.y);
    }

    void AttackPlayer()
    {
        // 공격 중엔 이동 멈춤
        rb.linearVelocity = new Vector2(0, rb.linearVelocity.y);
        // 공격 로직 (애니메이션 트리거, 데미지 처리 등)
        Debug.Log("적 공격!");
    }
}
적 AI 상태 전환 다이어그램 순찰 Patrol 추적 Chase 공격 Attack 감지 범위 내 공격 범위 내 범위 벗어남 공격 범위 벗어남

🎯 6. 발사체 & 총알 시스템 — 슈팅 게임의 핵심

2D 슈팅 게임에서 총알 시스템은 오브젝트 풀링(Object Pooling)을 쓰는 게 성능상 훨씬 유리해.
매번 Instantiate/Destroy 하면 GC(가비지 컬렉터)가 난리 나거든 ㅋㅋㅋ
일단 기본 발사 코드부터 보자!

📌 기본 총알 발사 코드
using UnityEngine;

public class PlayerShooter : MonoBehaviour
{
    public GameObject bulletPrefab;    // 총알 프리팹
    public Transform firePoint;        // 발사 위치
    public float fireRate = 0.2f;      // 발사 간격 (초)
    private float nextFireTime = 0f;

    void Update()
    {
        // 마우스 왼쪽 버튼 또는 Z키로 발사
        if ((Input.GetButton("Fire1") || Input.GetKey(KeyCode.Z)) 
            && Time.time >= nextFireTime)
        {
            Shoot();
            nextFireTime = Time.time + fireRate;
        }
    }

    void Shoot()
    {
        // 총알 생성 (발사 위치와 방향)
        GameObject bullet = Instantiate(
            bulletPrefab, 
            firePoint.position, 
            firePoint.rotation
        );
    }
}
📌 총알 이동 & 자동 삭제 코드
using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float speed = 15f;
    public int damage = 10;
    public float lifetime = 3f;  // 3초 후 자동 삭제

    private Rigidbody2D rb;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        // 총알 방향으로 속도 적용
        rb.linearVelocity = transform.right * speed;

        // 일정 시간 후 자동 삭제 (화면 밖으로 나간 경우 대비)
        Destroy(gameObject, lifetime);
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        // 적에게 맞았을 때
        if (other.CompareTag("Enemy"))
        {
            EnemyHealth enemyHealth = other.GetComponent<EnemyHealth>();
            if (enemyHealth != null)
            {
                enemyHealth.TakeDamage(damage);
            }
            // 총알 삭제
            Destroy(gameObject);
        }

        // 벽에 맞았을 때
        if (other.CompareTag("Wall"))
        {
            Destroy(gameObject);
        }
    }
}

🏆 7. GameManager — 싱글톤 패턴으로 게임 전체 관리

GameManager는 게임 전체의 상태(점수, 레벨, 게임오버 등)를 관리하는 중앙 컨트롤러야.
싱글톤 패턴을 써서 어디서든 접근 가능하게 만드는 게 표준이거든 ㅋㅋ
재능넷에서 Unity 관련 재능을 거래하는 분들도 이 패턴은 기본 중의 기본으로 알고 있더라고!

using UnityEngine;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    // 싱글톤 인스턴스
    public static GameManager Instance { get; private set; }

    [Header("게임 상태")]
    public int currentScore = 0;
    public int highScore = 0;
    public int currentLevel = 1;
    public bool isGameOver = false;
    public bool isGamePaused = false;

    void Awake()
    {
        // 싱글톤 패턴: 인스턴스가 이미 있으면 이 오브젝트 삭제
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        // 씬 전환 시에도 파괴되지 않도록 설정
        DontDestroyOnLoad(gameObject);

        // 저장된 최고 점수 불러오기
        highScore = PlayerPrefs.GetInt("HighScore", 0);
    }

    // 점수 추가
    public void AddScore(int amount)
    {
        if (isGameOver) return;
        currentScore += amount;

        // 최고 점수 갱신
        if (currentScore > highScore)
        {
            highScore = currentScore;
            PlayerPrefs.SetInt("HighScore", highScore);
        }

        // UI 업데이트 이벤트 (UIManager에서 구독)
        UIManager.Instance?.UpdateScoreUI(currentScore);
    }

    // 게임 오버 처리
    public void GameOver()
    {
        isGameOver = true;
        Time.timeScale = 0f;  // 게임 일시 정지
        UIManager.Instance?.ShowGameOverPanel();
    }

    // 게임 재시작
    public void RestartGame()
    {
        isGameOver = false;
        currentScore = 0;
        Time.timeScale = 1f;
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

    // 일시정지 토글
    public void TogglePause()
    {
        isGamePaused = !isGamePaused;
        Time.timeScale = isGamePaused ? 0f : 1f;
        UIManager.Instance?.ShowPausePanel(isGamePaused);
    }

    // 다음 레벨 로드
    public void LoadNextLevel()
    {
        currentLevel++;
        SceneManager.LoadScene(currentLevel);
    }
}
💡 DontDestroyOnLoad를 쓰면 씬이 바뀌어도 GameManager가 유지돼.
근데 씬을 다시 로드할 때 중복 생성되는 문제가 있어서 Awake()에서 중복 체크가 필수야!

💾 8. 데이터 저장 — PlayerPrefs & JSON 저장

게임 데이터 저장은 크게 두 가지 방법이 있어.
간단한 값은 PlayerPrefs, 복잡한 데이터는 JSON 직렬화를 써!

📌 PlayerPrefs 기본 사용법
// 데이터 저장
PlayerPrefs.SetInt("Score", 1000);
PlayerPrefs.SetFloat("Volume", 0.8f);
PlayerPrefs.SetString("PlayerName", "홍길동");
PlayerPrefs.Save();  // 명시적 저장 (선택사항, 앱 종료 시 자동 저장됨)

// 데이터 불러오기 (두 번째 인자: 기본값)
int score = PlayerPrefs.GetInt("Score", 0);
float volume = PlayerPrefs.GetFloat("Volume", 1.0f);
string name = PlayerPrefs.GetString("PlayerName", "플레이어");

// 데이터 존재 여부 확인
if (PlayerPrefs.HasKey("Score"))
{
    Debug.Log("저장된 점수 있음!");
}

// 특정 키 삭제
PlayerPrefs.DeleteKey("Score");

// 전체 삭제
PlayerPrefs.DeleteAll();
📌 JSON 직렬화로 복잡한 데이터 저장
using UnityEngine;
using System.IO;

// 저장할 데이터 구조체
[System.Serializable]
public class GameSaveData
{
    public int level;
    public int score;
    public float playerX;
    public float playerY;
    public int[] collectedItems;
    public string saveTime;
}

public class SaveSystem : MonoBehaviour
{
    private string savePath;

    void Awake()
    {
        // 저장 경로 설정 (플랫폼별 적절한 경로)
        savePath = Application.persistentDataPath + "/savedata.json";
    }

    // 게임 저장
    public void SaveGame(GameSaveData data)
    {
        data.saveTime = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

        // 데이터를 JSON 문자열로 변환
        string json = JsonUtility.ToJson(data, true);  // true = 보기 좋게 포맷

        // 파일에 쓰기
        File.WriteAllText(savePath, json);
        Debug.Log("게임 저장 완료: " + savePath);
    }

    // 게임 불러오기
    public GameSaveData LoadGame()
    {
        if (!File.Exists(savePath))
        {
            Debug.Log("저장 파일 없음, 새 게임 시작");
            return null;
        }

        string json = File.ReadAllText(savePath);
        GameSaveData data = JsonUtility.FromJson<GameSaveData>(json);
        return data;
    }
}

🎵 9. 사운드 매니저 — BGM & 효과음 관리

게임에서 사운드는 몰입감의 핵심이잖아?
AudioManager도 싱글톤으로 만들어서 어디서든 호출 가능하게 하는 게 국룰이야 ㅋㅋ

using UnityEngine;
using System.Collections.Generic;

public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }

    [Header("오디오 소스")]
    public AudioSource bgmSource;   // BGM 전용
    public AudioSource sfxSource;   // 효과음 전용

    [Header("사운드 클립")]
    public AudioClip[] bgmClips;
    public AudioClip[] sfxClips;

    // 효과음 이름으로 빠르게 찾기 위한 딕셔너리
    private Dictionary<string, AudioClip> sfxDictionary;

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);

        // 딕셔너리 초기화
        sfxDictionary = new Dictionary<string, AudioClip>();
        foreach (AudioClip clip in sfxClips)
        {
            if (clip != null)
                sfxDictionary[clip.name] = clip;
        }
    }

    // BGM 재생
    public void PlayBGM(int index)
    {
        if (index >= bgmClips.Length) return;
        bgmSource.clip = bgmClips[index];
        bgmSource.loop = true;
        bgmSource.Play();
    }

    // 효과음 재생 (이름으로 호출)
    public void PlaySFX(string clipName)
    {
        if (sfxDictionary.TryGetValue(clipName, out AudioClip clip))
        {
            sfxSource.PlayOneShot(clip);
        }
        else
        {
            Debug.LogWarning("효과음을 찾을 수 없음: " + clipName);
        }
    }

    // 볼륨 조절
    public void SetBGMVolume(float volume)
    {
        bgmSource.volume = Mathf.Clamp01(volume);
        PlayerPrefs.SetFloat("BGMVolume", volume);
    }

    public void SetSFXVolume(float volume)
    {
        sfxSource.volume = Mathf.Clamp01(volume);
        PlayerPrefs.SetFloat("SFXVolume", volume);
    }
}

🖥️ 10. UI 연동 — 체력바 & 점수 표시

Unity UI(uGUI)와 코드를 연결하는 건 진짜 자주 쓰이는 패턴이야.
TextMeshProSlider를 활용한 체력바 예제를 보자!

using UnityEngine;
using UnityEngine.UI;
using TMPro;  // TextMeshPro 네임스페이스

public class UIManager : MonoBehaviour
{
    public static UIManager Instance { get; private set; }

    [Header("체력 UI")]
    public Slider healthBar;
    public TextMeshProUGUI healthText;

    [Header("점수 UI")]
    public TextMeshProUGUI scoreText;
    public TextMeshProUGUI highScoreText;

    [Header("패널")]
    public GameObject gameOverPanel;
    public GameObject pausePanel;

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
    }

    // 체력 UI 업데이트
    public void UpdateHealthUI(int current, int max)
    {
        // 슬라이더 값 업데이트 (0~1 사이 값)
        healthBar.value = (float)current / max;

        // 텍스트 업데이트
        healthText.text = $"{current} / {max}";

        // 체력에 따라 색상 변경
        if (current / (float)max > 0.5f)
            healthBar.fillRect.GetComponent<Image>().color = Color.green;
        else if (current / (float)max > 0.25f)
            healthBar.fillRect.GetComponent<Image>().color = Color.yellow;
        else
            healthBar.fillRect.GetComponent<Image>().color = Color.red;
    }

    // 점수 UI 업데이트
    public void UpdateScoreUI(int score)
    {
        scoreText.text = $"점수: {score:N0}";  // 천 단위 구분자 포함
    }

    // 게임오버 패널 표시
    public void ShowGameOverPanel()
    {
        gameOverPanel.SetActive(true);
        highScoreText.text = $"최고 점수: {GameManager.Instance.highScore:N0}";
    }

    // 일시정지 패널 토글
    public void ShowPausePanel(bool show)
    {
        pausePanel.SetActive(show);
    }
}

⚡ 11. 코루틴 활용 — 시간 기반 이벤트 처리

Unity에서 시간 지연, 반복 처리, 페이드 효과 등을 구현할 때 코루틴(Coroutine)은 진짜 필수야.
async/await보다 Unity 환경에서 더 자연스럽게 동작하거든 ㅋㅋ

using UnityEngine;
using System.Collections;

public class CoroutineExamples : MonoBehaviour
{
    // 예시 1: 일정 시간 후 실행
    IEnumerator DelayedAction(float delay)
    {
        yield return new WaitForSeconds(delay);
        Debug.Log(delay + "초 후 실행!");
    }

    // 예시 2: 화면 페이드 인/아웃
    public CanvasGroup canvasGroup;

    IEnumerator FadeIn(float duration)
    {
        canvasGroup.alpha = 0f;
        float elapsed = 0f;

        while (elapsed < duration)
        {
            elapsed += Time.deltaTime;
            canvasGroup.alpha = Mathf.Clamp01(elapsed / duration);
            yield return null;  // 다음 프레임까지 대기
        }
        canvasGroup.alpha = 1f;
    }

    IEnumerator FadeOut(float duration)
    {
        canvasGroup.alpha = 1f;
        float elapsed = 0f;

        while (elapsed < duration)
        {
            elapsed += Time.deltaTime;
            canvasGroup.alpha = 1f - Mathf.Clamp01(elapsed / duration);
            yield return null;
        }
        canvasGroup.alpha = 0f;
    }

    // 예시 3: 깜빡임 효과 (무적 시간 표현)
    IEnumerator BlinkEffect(SpriteRenderer spriteRenderer, float duration)
    {
        float elapsed = 0f;
        while (elapsed < duration)
        {
            spriteRenderer.enabled = !spriteRenderer.enabled;
            yield return new WaitForSeconds(0.1f);
            elapsed += 0.1f;
        }
        spriteRenderer.enabled = true;  // 마지막엔 반드시 보이게
    }

    // 예시 4: 카메라 흔들기 효과
    IEnumerator CameraShake(float duration, float magnitude)
    {
        Vector3 originalPos = Camera.main.transform.position;
        float elapsed = 0f;

        while (elapsed < duration)
        {
            float x = Random.Range(-1f, 1f) * magnitude;
            float y = Random.Range(-1f, 1f) * magnitude;

            Camera.main.transform.position = 
                new Vector3(originalPos.x + x, originalPos.y + y, originalPos.z);

            elapsed += Time.deltaTime;
            yield return null;
        }

        Camera.main.transform.position = originalPos;
    }

    void Start()
    {
        // 코루틴 시작 방법
        StartCoroutine(DelayedAction(2f));
        StartCoroutine(FadeIn(1.5f));
        StartCoroutine(CameraShake(0.5f, 0.1f));
    }
}

🗺️ 12. 카메라 추적 — 플레이어 따라다니는 카메라

2D 게임에서 카메라가 플레이어를 부드럽게 따라다니는 건 기본 중의 기본이지!
Cinemachine을 쓰면 더 쉽지만, 직접 구현하는 방법도 알아두면 좋아 ㅋㅋ

using UnityEngine;

public class CameraFollow : MonoBehaviour
{
    public Transform target;          // 따라갈 대상 (플레이어)
    public float smoothSpeed = 5f;    // 부드러움 정도
    public Vector3 offset = new Vector3(0, 2f, -10f);  // 카메라 오프셋

    [Header("카메라 경계 설정")]
    public bool useBounds = true;
    public float minX, maxX, minY, maxY;

    void LateUpdate()  // Update 대신 LateUpdate 사용 (플레이어 이동 후 처리)
    {
        if (target == null) return;

        // 목표 위치 계산
        Vector3 desiredPosition = target.position + offset;

        // 경계 제한 적용
        if (useBounds)
        {
            desiredPosition.x = Mathf.Clamp(desiredPosition.x, minX, maxX);
            desiredPosition.y = Mathf.Clamp(desiredPosition.y, minY, maxY);
        }

        // 부드러운 이동 (Lerp 사용)
        Vector3 smoothedPosition = Vector3.Lerp(
            transform.position, 
            desiredPosition, 
            smoothSpeed * Time.deltaTime
        );

        transform.position = smoothedPosition;
    }
}
💡 카메라 스크립트는 반드시 LateUpdate()에서 처리해야 해!
Update()에서 하면 플레이어 이동 전에 카메라가 먼저 움직여서 떨림 현상이 생길 수 있거든 ㅋㅋ

📋 핵심 컴포넌트 & 메서드 빠른 참조표
기능 주요 컴포넌트/클래스 핵심 메서드
물리 이동 Rigidbody2D linearVelocity, AddForce()
충돌 감지 Collider2D OnCollisionEnter2D, OnTriggerEnter2D
애니메이션 Animator SetFloat, SetBool, SetTrigger
오브젝트 생성 MonoBehaviour Instantiate(), Destroy()
씬 관리 SceneManager LoadScene(), GetActiveScene()
데이터 저장 PlayerPrefs SetInt, GetInt, Save()
시간 처리 Time deltaTime, timeScale, time
사운드 AudioSource Play(), PlayOneShot(), Stop()
UI 텍스트 TextMeshProUGUI .text 프로퍼티
코루틴 MonoBehaviour StartCoroutine(), StopCoroutine()

🔧 자주 발생하는 에러 & 해결법
🚨
NullReferenceException
컴포넌트 참조 전
null 체크 필수!
성능 저하
Update에서
GetComponent 금지!
🔄
무한 루프
코루틴에
yield 반드시 포함!
📐
물리 떨림
카메라는
LateUpdate 사용!
⚠️ 성능 최적화 꿀팁!
GetComponent<T>()Start() 또는 Awake()에서 한 번만 호출하고 변수에 캐싱해서 써야 해.
Update()에서 매 프레임 호출하면 성능이 급격히 떨어지거든 ㅋㅋ 이거 진짜 중요함!

🌟 마무리 — 이제 뭘 만들어볼까?

여기까지 Unity 2D 게임 개발의 핵심 소스코드들을 쭉 살펴봤어!
플레이어 이동부터 시작해서 애니메이션, 충돌 감지, 체력 시스템, 적 AI, 발사체, GameManager, 데이터 저장, 사운드, UI, 코루틴, 카메라까지 — 이 정도면 간단한 2D 게임 하나는 충분히 만들 수 있는 기반이 갖춰진 거야 😊

이 코드들을 기반으로 플랫포머 게임, 탑다운 슈팅 게임, 퍼즐 게임 등 다양한 장르에 응용해봐!
처음엔 복붙해서 쓰더라도 점점 코드를 이해하고 수정하다 보면 실력이 쑥쑥 늘거든 ㅋㅋㅋ

Unity 개발 관련해서 더 배우고 싶거나, 직접 만든 게임 소스를 공유하고 싶다면
재능넷(https://www.jaenung.net)에서 Unity 개발 관련 재능을 찾아보는 것도 좋은 방법이야.
다양한 개발자들이 자신의 노하우를 공유하고 있거든 😎

댓글 작성

이 글에 대한 여러분의 생각을 들려주세요

댓글 0