no image
DesignPattern - 중재자(Mediator) 패턴
🧩 Unity 디자인 패턴 - 중재자(Mediator) 패턴1. 중재자 패턴이란?중재자(Mediator) 패턴은 객체 간의 직접적인 소통을 피하고, 모든 통신을 중앙 관리자(중재자)를 통해 진행하도록 설계하는 패턴입니다."A와 B가 서로를 몰라도, 중재자에게만 말하면 필요한 연결이 자동으로 이뤄지는 구조"입니다.2. 언제 사용하나요?UI 요소나 시스템 간 상호작용이 많고 복잡할 때객체 간 결합도를 낮추고 싶을 때Observer 패턴보다 더 넓은 범위의 제어가 필요할 때3. 구조 구성요소역할설명IMediator중재자 인터페이스ConcreteMediator실제 이벤트를 중계하는 클래스Participant중재자에게 메시지를 보내는 구성원 객체4. Unity 예제: 버튼 누르면 여러 시스템에 명령 전파💬 S..
2025.08.06
no image
DesignPattern - 전략(Strategy) 패턴
🧠 Unity 디자인 패턴 - 전략(Strategy) 패턴1. 전략 패턴이란?전략(Strategy) 패턴은 행동(알고리즘)을 객체로 분리하여, 런타임에 자유롭게 교체할 수 있도록 하는 패턴입니다."동일한 행동 인터페이스를 가진 여러 전략 중 하나를 선택해서 실행하는 구조"입니다.게임에서는 주로 공격 방식, 이동 방식, 스킬 사용 방식 등에 다양하게 활용됩니다.2. 언제 사용하나요?캐릭터의 스킬/공격 방식이 상황에 따라 바뀌어야 할 때AI의 행동 방식, 경로 찾기 알고리즘 등 다양한 알고리즘 중 선택적으로 적용해야 할 때if나 switch 문으로 전략 분기를 하고 있을 때 → 리팩토링 시 효과적3. 구조 구성요소역할설명ISkillStrategy전략(알고리즘) 인터페이스FireSkill, IceSkill ..
2025.08.06
no image
DesignPattern - 커맨드(Command) 패턴
🎮 Unity 디자인 패턴 - 커맨드(Command) 패턴1. 커맨드 패턴이란?커맨드(Command) 패턴은 요청(행동)을 하나의 객체로 캡슐화하여,나중에 실행하거나취소하거나저장하거나여러 명령을 큐에 쌓아두었다가 순차적으로 실행할 수 있도록 도와주는 패턴입니다."행동을 직접 수행하지 않고, 명령 쪽지를 만들어서 전달하는 방식"이라고 생각하면 이해가 쉽습니다.2. 언제 사용하나요?행동(명령)을 재사용하거나 기록하고 싶을 때Undo/Redo, 매크로 명령, 리플레이 기능을 만들고 싶을 때UI 버튼이나 키 입력에 대응하는 로직을 유연하게 처리하고 싶을 때3. 구조 구성요소역할설명ICommand명령의 인터페이스 (Execute, Undo 등)ConcreteCommand실제 실행 로직을 담은 명령 객체Invok..
2025.08.06
no image
DesignPattern - 상태 패턴(State Pattern) vs FSM
🔄 Unity 디자인 패턴 - 상태 패턴(State Pattern) vs FSM1. 상태를 관리하는 두 가지 방식게임 캐릭터의 행동이나 UI의 전환처럼 상태에 따라 동작이 달라져야 할 때, 우리는 다음 두 가지 접근 방식을 고려하게 됩니다:FSM (Finite State Machine): 전통적인 조건 분기 기반 구조상태 패턴 (State Pattern): 객체지향 원칙을 적용한 확장 가능한 구조2. FSM이란?FSM(Finite State Machine)은 하나의 상태만 유지하며, 조건에 따라 다른 상태로 전이되는 방식입니다.📦 특징enum, switch-case 구조로 단순 구현 가능빠르고 직관적인 로직 구성에 유리✅ 예제 코드 (Unity 기준)public enum PlayerState { Id..
2025.08.06
no image
DesignPattern - 이벤트 버스(Event Bus) 패턴
📡 Unity 디자인 패턴 - 이벤트 버스(Event Bus) 패턴1. 이벤트 버스란?이벤트 버스(Event Bus) 패턴은 객체 간 직접적인 참조 없이, 전역 이벤트 허브를 통해 메시지를 주고받는 구조입니다."A가 B를 직접 알지 못해도, 이벤트 버스를 통해 소통할 수 있는 구조"입니다.Unity에서는 UI, 게임 로직, 시스템 간 느슨한 연결(Decoupling) 이 필요할 때 매우 유용합니다.2. 언제 사용하나요?시스템 간 결합도를 낮추고 싶을 때여러 UI/오브젝트가 동일한 이벤트를 구독해야 할 때특정 이벤트에 반응하는 대상이 자주 바뀌거나 많을 때3. 구조 설명Publisher (발행자): 이벤트를 전송함Subscriber (구독자): 특정 이벤트를 듣고 반응함EventBus: 중재자 역할. 모..
2025.08.06
no image
DesignPattern - 팩토리(Factory) 패턴
🏭 Unity 디자인 패턴 - 팩토리(Factory) 패턴1. 팩토리 패턴이란?팩토리(Factory) 패턴은 객체의 생성 과정을 감추고, 생성 책임을 별도의 팩토리 클래스에 위임하는 디자인 패턴입니다. Unity에서는 다양한 타입의 오브젝트를 조건에 따라 생성하거나, 유지보수가 쉬운 구조로 확장할 때 자주 사용됩니다."직접 new를 하지 않고, 대신 '공장'에게 주문해서 받아오는 방식"이라고 이해하면 쉽습니다.2. 언제 사용하나요?다양한 타입의 객체를 상황에 따라 생성해야 할 때생성 로직이 자주 바뀌거나, 복잡한 초기화가 필요한 경우결합도를 낮추고, 확장성을 높이고 싶을 때3. Unity 예제: 적(Enemy) 생성기 만들기🎯 목표Enemy 타입에 따라 서로 다른 프리팹을 생성코드 변경 없이 적 종류..
2025.08.06
no image
DesignPattern - 옵저버(Observer) 패턴
🔭 Unity 디자인 패턴 - 옵저버(Observer) 패턴1. 옵저버 패턴이란?옵저버(Observer) 패턴은 어떤 객체의 상태가 변경될 때, 그 객체를 "구독"하고 있는 다른 객체들에게 자동으로 알림을 보내는 구조입니다. Unity에서는 UI나 게임 오브젝트가 데이터나 이벤트의 변경을 실시간으로 반영해야 할 때 자주 사용됩니다."플레이어의 체력이 변하면 체력바가 자동으로 바뀌는 것"이 대표적인 예입니다.2. 언제 사용하나요?플레이어 체력/마나 등의 수치 변화 → UI 업데이트게임 설정 변경 → 여러 오브젝트에 즉시 반영이벤트 중심 구조로 게임 로직을 관리할 때3. 구조 설명Subject (발행자): 상태를 가지고 있는 객체. 변화가 생기면 옵저버에게 알림Observer (구독자): Subject의 ..
2025.08.06
no image
DesignPattern - Unity에서의 Generic Singleton 패턴
❓ 왜 Generic Singleton이 필요할까?Unity에서는 GameManager, SoundManager, UIManager 등 다양한 매니저 클래스에서 싱글톤(Singleton) 패턴을 사용한다.하지만 매번 static instance 선언, Awake()에서 중복 제거 같은 코드를 반복하게 된다.이런 중복 코드를 줄이고 재사용 가능한 형태로 만들기 위해 Generic Singleton 패턴을 도입할 수 있다.🧠 Generic Singleton이란?Singleton처럼 제네릭 타입을 활용하여 싱글톤 로직을 부모 클래스에서 처리하고,필요한 매니저들은 상속만 하면 되는 구조다. 🧩 Singleton 기본 구현 using UnityEngine;public class Singleton : Mono..
2025.07.22
반응형

🧩 Unity 디자인 패턴 - 중재자(Mediator) 패턴

1. 중재자 패턴이란?

중재자(Mediator) 패턴은 객체 간의 직접적인 소통을 피하고, 모든 통신을 중앙 관리자(중재자)를 통해 진행하도록 설계하는 패턴입니다.

"A와 B가 서로를 몰라도, 중재자에게만 말하면 필요한 연결이 자동으로 이뤄지는 구조"입니다.


2. 언제 사용하나요?

  • UI 요소나 시스템 간 상호작용이 많고 복잡할 때
  • 객체 간 결합도를 낮추고 싶을 때
  • Observer 패턴보다 더 넓은 범위의 제어가 필요할 때

3. 구조 구성요소

역할 설명
IMediator 중재자 인터페이스
ConcreteMediator 실제 이벤트를 중계하는 클래스
Participant 중재자에게 메시지를 보내는 구성원 객체

4. Unity 예제: 버튼 누르면 여러 시스템에 명령 전파

💬 Step 1: IMediator 인터페이스

public interface IMediator
{
    void Notify(string eventKey, object data = null);
}

🧠 Step 2: ConcreteMediator 클래스

using System;
using System.Collections.Generic;

public class GameMediator : IMediator
{
    private Dictionary<string, Action<object>> listeners = new();

    public void Register(string eventKey, Action<object> callback)
    {
        if (!listeners.ContainsKey(eventKey))
            listeners[eventKey] = delegate { };

        listeners[eventKey] += callback;
    }

    public void Unregister(string eventKey, Action<object> callback)
    {
        if (listeners.ContainsKey(eventKey))
            listeners[eventKey] -= callback;
    }

    public void Notify(string eventKey, object data = null)
    {
        if (listeners.TryGetValue(eventKey, out var callback))
            callback?.Invoke(data);
    }
}

📦 Step 3: MediatorProvider (싱글톤 등록소)

public static class MediatorProvider
{
    public static GameMediator Mediator = new();
}

🧱 Step 4: Button 클릭 → 중재자에게 알림

using UnityEngine;

public class UIButton : MonoBehaviour
{
    public void OnClick()
    {
        MediatorProvider.Mediator.Notify("PlayClicked", "게임 시작!");
    }
}

🖥️ Step 5: UIManager가 이벤트 수신

using UnityEngine;

public class UIManager : MonoBehaviour
{
    private void OnEnable()
    {
        MediatorProvider.Mediator.Register("PlayClicked", OnPlayClicked);
    }

    private void OnDisable()
    {
        MediatorProvider.Mediator.Unregister("PlayClicked", OnPlayClicked);
    }

    private void OnPlayClicked(object data)
    {
        Debug.Log($"UIManager 수신: {data}");
        // 예: 타이틀 화면 숨기기
    }
}

5. 정리 및 장단점

✅ 장점

  • 객체 간 직접 참조 없이 상호작용 가능 → 결합도 ↓
  • 동적으로 구독/해제 가능
  • 복잡한 시스템 간 통신 구조 단순화

❌ 단점

  • 모든 통신이 중재자를 거치기 때문에, 중재자가 비대해질 수 있음
  • 디버깅 시 흐름 추적이 어려워질 수 있음

✅ 마무리 한 줄 요약

중재자 패턴은 모든 객체가 서로를 몰라도, 하나의 창구만 알면 협력할 수 있는 설계로, 복잡한 시스템 구조를 깔끔하게 정리할 수 있는 강력한 도구입니다.

반응형
반응형

🧠 Unity 디자인 패턴 - 전략(Strategy) 패턴

1. 전략 패턴이란?

전략(Strategy) 패턴은 행동(알고리즘)을 객체로 분리하여, 런타임에 자유롭게 교체할 수 있도록 하는 패턴입니다.

"동일한 행동 인터페이스를 가진 여러 전략 중 하나를 선택해서 실행하는 구조"입니다.

게임에서는 주로 공격 방식, 이동 방식, 스킬 사용 방식 등에 다양하게 활용됩니다.


2. 언제 사용하나요?

  • 캐릭터의 스킬/공격 방식이 상황에 따라 바뀌어야 할 때
  • AI의 행동 방식, 경로 찾기 알고리즘 등 다양한 알고리즘 중 선택적으로 적용해야 할 때
  • ifswitch 문으로 전략 분기를 하고 있을 때 → 리팩토링 시 효과적

3. 구조 구성요소

역할 설명
ISkillStrategy 전략(알고리즘) 인터페이스
FireSkill, IceSkill 실제 전략을 구현한 클래스들
Player 전략을 사용하는 컨텍스트 역할

4. Unity 예제: 플레이어 스킬 시스템

📦 Step 1: 전략 인터페이스 정의

public interface ISkillStrategy
{
    void Use(GameObject caster);
}

🔥 Step 2: 다양한 스킬 전략들

using UnityEngine;

public class FireSkill : ISkillStrategy
{
    public void Use(GameObject caster)
    {
        Debug.Log("🔥 파이어볼 발사!");
    }
}

public class IceSkill : ISkillStrategy
{
    public void Use(GameObject caster)
    {
        Debug.Log("❄️ 아이스 스톰 시전!");
    }
}

public class HealSkill : ISkillStrategy
{
    public void Use(GameObject caster)
    {
        Debug.Log("💚 힐링 시전! HP +30");
    }
}

🧍 Step 3: 전략을 사용하는 플레이어

using UnityEngine;

public class Player : MonoBehaviour
{
    private ISkillStrategy currentSkill;

    public void SetSkill(ISkillStrategy skill)
    {
        currentSkill = skill;
    }

    public void UseSkill()
    {
        if (currentSkill != null)
            currentSkill.Use(gameObject);
        else
            Debug.Log("스킬이 설정되지 않았습니다.");
    }
}

🕹️ Step 4: 전략 전환 예시

public class GameManager : MonoBehaviour
{
    public Player player;

    void Start()
    {
        player.SetSkill(new FireSkill());
        player.UseSkill(); // 🔥

        player.SetSkill(new IceSkill());
        player.UseSkill(); // ❄️

        player.SetSkill(new HealSkill());
        player.UseSkill(); // 💚
    }
}

5. 정리 및 장단점

✅ 장점

  • if/switch 분기 없이 동작을 교체할 수 있음
  • 전략을 독립적으로 구현/유지/확장 가능
  • 런타임에 알고리즘 변경이 용이함

❌ 단점

  • 클래스 수 증가
  • 구조를 이해하는 데 약간의 시간 필요

✅ 마무리 한 줄 요약

전략 패턴은 **"어떻게 실행할지를 나중에 결정할 수 있는 설계"**로, 캐릭터 스킬, AI 전략, UI 전환 방식 등에서 매우 유연하게 사용할 수 있습니다.

반응형
반응형

🎮 Unity 디자인 패턴 - 커맨드(Command) 패턴

1. 커맨드 패턴이란?

커맨드(Command) 패턴은 요청(행동)을 하나의 객체로 캡슐화하여,

  • 나중에 실행하거나
  • 취소하거나
  • 저장하거나
  • 여러 명령을 큐에 쌓아두었다가 순차적으로 실행할 수 있도록 도와주는 패턴입니다.

"행동을 직접 수행하지 않고, 명령 쪽지를 만들어서 전달하는 방식"이라고 생각하면 이해가 쉽습니다.


2. 언제 사용하나요?

  • 행동(명령)을 재사용하거나 기록하고 싶을 때
  • Undo/Redo, 매크로 명령, 리플레이 기능을 만들고 싶을 때
  • UI 버튼이나 키 입력에 대응하는 로직을 유연하게 처리하고 싶을 때

3. 구조 구성요소

역할 설명
ICommand 명령의 인터페이스 (Execute, Undo 등)
ConcreteCommand 실제 실행 로직을 담은 명령 객체
Invoker 명령을 실행하는 주체 (예: InputHandler, 버튼 등)
Receiver 명령의 실제 작업을 수행하는 객체

4. Unity 예제: 플레이어 명령 시스템

🎮 Step 1: ICommand 인터페이스

public interface ICommand
{
    void Execute();
    void Undo();
}

🧱 Step 2: 실제 명령들

public class MoveCommand : ICommand
{
    private Player player;
    public MoveCommand(Player p) => player = p;

    public void Execute() => player.Move();
    public void Undo() => player.Stop();
}

public class JumpCommand : ICommand
{
    private Player player;
    public JumpCommand(Player p) => player = p;

    public void Execute() => player.Jump();
    public void Undo() => Debug.Log("점프 취소");
}

🧍 Step 3: 수신자(Receiver) - Player

using UnityEngine;

public class Player : MonoBehaviour
{
    public void Move() => Debug.Log("플레이어 이동 중...");
    public void Jump() => Debug.Log("플레이어 점프!");
    public void Stop() => Debug.Log("플레이어 정지");
}

🕹️ Step 4: 입력 핸들러 (Invoker)

using UnityEngine;

public class InputHandler : MonoBehaviour
{
    public Player player;

    private ICommand moveCommand;
    private ICommand jumpCommand;

    private void Start()
    {
        moveCommand = new MoveCommand(player);
        jumpCommand = new JumpCommand(player);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
            moveCommand.Execute();

        if (Input.GetKeyDown(KeyCode.Space))
            jumpCommand.Execute();
    }
}

5. 확장: 명령 히스토리로 Undo 구현하기

using System.Collections.Generic;

public class CommandManager
{
    private Stack<ICommand> history = new();

    public void ExecuteCommand(ICommand command)
    {
        command.Execute();
        history.Push(command);
    }

    public void UndoCommand()
    {
        if (history.Count > 0)
        {
            ICommand last = history.Pop();
            last.Undo();
        }
    }
}

6. 정리 및 장단점

✅ 장점

  • 명령을 나중에 실행하거나 취소 가능 (Undo, Replay 등)
  • 명령을 모듈화하여 확장성 ↑
  • 입력 → 행동을 분리하여 관리하기 쉬움

❌ 단점

  • 클래스 수 증가
  • 단순한 작업엔 오버헤드 발생

✅ 마무리 한 줄 요약

커맨드 패턴은 행동을 객체로 만들고, 실행을 제어하는 구조로, UI, 입력 처리, 실행 기록 기능 등에 매우 적합한 패턴입니다.

반응형
반응형

🔄 Unity 디자인 패턴 - 상태 패턴(State Pattern) vs FSM

1. 상태를 관리하는 두 가지 방식

게임 캐릭터의 행동이나 UI의 전환처럼 상태에 따라 동작이 달라져야 할 때, 우리는 다음 두 가지 접근 방식을 고려하게 됩니다:

  • FSM (Finite State Machine): 전통적인 조건 분기 기반 구조
  • 상태 패턴 (State Pattern): 객체지향 원칙을 적용한 확장 가능한 구조

2. FSM이란?

FSM(Finite State Machine)은 하나의 상태만 유지하며, 조건에 따라 다른 상태로 전이되는 방식입니다.

📦 특징

  • enum, switch-case 구조로 단순 구현 가능
  • 빠르고 직관적인 로직 구성에 유리

✅ 예제 코드 (Unity 기준)

public enum PlayerState { Idle, Move, Jump }

public class PlayerFSM : MonoBehaviour
{
    private PlayerState state = PlayerState.Idle;

    void Update()
    {
        switch (state)
        {
            case PlayerState.Idle:
                if (Input.GetKeyDown(KeyCode.W))
                    state = PlayerState.Move;
                break;
            case PlayerState.Move:
                if (Input.GetKeyDown(KeyCode.Space))
                    state = PlayerState.Jump;
                break;
            case PlayerState.Jump:
                if (IsGrounded())
                    state = PlayerState.Idle;
                break;
        }
    }

    private bool IsGrounded() => true; // 착지 판정은 생략
}

👍 장점

  • 구현이 간단하다
  • 로직 흐름이 눈에 잘 보인다

👎 단점

  • 상태가 많아질수록 switch 문이 길어짐
  • 상태별 로직이 섞이기 쉬움 → 유지보수 어려움

3. 상태 패턴이란?

상태 패턴은 각 상태를 독립된 클래스로 분리하여, 상태 전환과 실행 로직을 캡슐화하는 객체지향 방식입니다.

📦 구조

  • 공통 인터페이스 IState
  • 각각의 상태 클래스 (IdleState, MoveState, JumpState 등)
  • 상태를 관리하는 컨텍스트 (PlayerStateMachine 등)

✅ 예제 코드

public interface IState
{
    void Enter();
    void Execute();
    void Exit();
}

public class IdleState : IState
{
    public void Enter() => Debug.Log("Idle 진입");
    public void Execute() => Debug.Log("대기 중...");
    public void Exit() => Debug.Log("Idle 종료");
}

public class MoveState : IState
{
    public void Enter() => Debug.Log("이동 시작");
    public void Execute() => Debug.Log("이동 중...");
    public void Exit() => Debug.Log("이동 종료");
}

public class PlayerStateMachine : MonoBehaviour
{
    private IState currentState;

    void Start()
    {
        ChangeState(new IdleState());
    }

    void Update()
    {
        currentState?.Execute();

        // 임시 예제 전환 로직
        if (Input.GetKeyDown(KeyCode.W)) ChangeState(new MoveState());
    }

    public void ChangeState(IState newState)
    {
        currentState?.Exit();
        currentState = newState;
        currentState.Enter();
    }
}

👍 장점

  • 상태별 로직이 분리되어 깔끔함
  • 새로운 상태를 클래스 추가만으로 확장 가능
  • 상태 전이가 내부적으로 처리되어 유연함

👎 단점

  • 처음 구성할 때 코드 양이 많아 보임
  • 작은 규모의 FSM에는 오히려 과할 수 있음

4. FSM vs 상태 패턴 비교

항목 FSM 상태 패턴
구현 난이도 낮음 중간 이상
코드 구조 절차적 객체지향적
확장성 낮음 높음
테스트 용이성 낮음 높음
권장 대상 간단한 상태 전이 상태가 많거나 복잡한 시스템

✅ 마무리 한 줄 요약

FSM은 빠르게 시작할 수 있는 도구, 상태 패턴은 견고한 구조를 위한 설계입니다.

프로토타입 → FSM → 복잡해질수록 상태 패턴으로 리팩토링하는 흐름이 이상적입니다.

반응형
반응형

📡 Unity 디자인 패턴 - 이벤트 버스(Event Bus) 패턴

1. 이벤트 버스란?

이벤트 버스(Event Bus) 패턴은 객체 간 직접적인 참조 없이, 전역 이벤트 허브를 통해 메시지를 주고받는 구조입니다.

"A가 B를 직접 알지 못해도, 이벤트 버스를 통해 소통할 수 있는 구조"입니다.

Unity에서는 UI, 게임 로직, 시스템 간 느슨한 연결(Decoupling) 이 필요할 때 매우 유용합니다.


2. 언제 사용하나요?

  • 시스템 간 결합도를 낮추고 싶을 때
  • 여러 UI/오브젝트가 동일한 이벤트를 구독해야 할 때
  • 특정 이벤트에 반응하는 대상이 자주 바뀌거나 많을 때

3. 구조 설명

  • Publisher (발행자): 이벤트를 전송함
  • Subscriber (구독자): 특정 이벤트를 듣고 반응함
  • EventBus: 중재자 역할. 모든 이벤트를 관리하고 전달함

4. Unity 예제: 플레이어가 아이템을 먹었을 때 UI 업데이트

🧠 Step 1: EventBus 클래스

using System;
using System.Collections.Generic;

public static class EventBus
{
    private static Dictionary<string, Action<object>> eventTable = new();

    public static void Subscribe(string key, Action<object> callback)
    {
        if (!eventTable.ContainsKey(key))
            eventTable[key] = delegate { };

        eventTable[key] += callback;
    }

    public static void Unsubscribe(string key, Action<object> callback)
    {
        if (eventTable.ContainsKey(key))
            eventTable[key] -= callback;
    }

    public static void Publish(string key, object data = null)
    {
        if (eventTable.TryGetValue(key, out var callback))
            callback.Invoke(data);
    }
}

🎮 Step 2: Player에서 아이템 획득 시 이벤트 발행

using UnityEngine;

public class Player : MonoBehaviour
{
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.E))
        {
            Debug.Log("플레이어가 아이템을 획득!");
            EventBus.Publish("ItemPicked", "HealthPotion");
        }
    }
}

🖼️ Step 3: UI가 이벤트를 구독하여 표시

using UnityEngine;
using TMPro;

public class ItemUI : MonoBehaviour
{
    [SerializeField] private TextMeshProUGUI itemText;

    private void OnEnable()
    {
        EventBus.Subscribe("ItemPicked", OnItemPicked);
    }

    private void OnDisable()
    {
        EventBus.Unsubscribe("ItemPicked", OnItemPicked);
    }

    private void OnItemPicked(object itemName)
    {
        itemText.text = $"획득한 아이템: {itemName}";
    }
}

5. 장점과 단점

✅ 장점

  • 완전한 느슨한 결합
  • 여러 오브젝트가 하나의 이벤트에 반응 가능
  • 발행자와 구독자가 서로 몰라도 됨 → 확장성↑

❌ 단점

  • 실행 흐름이 명확하지 않음 → 디버깅 어려움
  • 잘못된 이벤트 키 입력 시 런타임 오류 유발 가능

✅ 마무리 한 줄 요약

이벤트 버스는 "말만 하면 누구든 들을 수 있는 마이크" 같은 구조로, Unity 프로젝트의 복잡도를 낮추는 강력한 도구입니다.

반응형
반응형

🏭 Unity 디자인 패턴 - 팩토리(Factory) 패턴

1. 팩토리 패턴이란?

팩토리(Factory) 패턴은 객체의 생성 과정을 감추고, 생성 책임을 별도의 팩토리 클래스에 위임하는 디자인 패턴입니다. Unity에서는 다양한 타입의 오브젝트를 조건에 따라 생성하거나, 유지보수가 쉬운 구조로 확장할 때 자주 사용됩니다.

"직접 new를 하지 않고, 대신 '공장'에게 주문해서 받아오는 방식"이라고 이해하면 쉽습니다.


2. 언제 사용하나요?

  • 다양한 타입의 객체를 상황에 따라 생성해야 할 때
  • 생성 로직이 자주 바뀌거나, 복잡한 초기화가 필요한 경우
  • 결합도를 낮추고, 확장성을 높이고 싶을 때

3. Unity 예제: 적(Enemy) 생성기 만들기

🎯 목표

  • Enemy 타입에 따라 서로 다른 프리팹을 생성
  • 코드 변경 없이 적 종류를 확장 가능하도록 구조 설계

4. 예제 코드

🧱 Step 1: Enemy 타입 정의

public enum EnemyType
{
    Melee,
    Ranged,
    Boss
}

🎮 Step 2: Enemy 베이스 클래스

using UnityEngine;

public abstract class Enemy : MonoBehaviour
{
    public abstract void Initialize();
}

🧟 Step 3: 실제 적 클래스들

public class MeleeEnemy : Enemy
{
    public override void Initialize() => Debug.Log("근접 적 생성 완료");
}

public class RangedEnemy : Enemy
{
    public override void Initialize() => Debug.Log("원거리 적 생성 완료");
}

public class BossEnemy : Enemy
{
    public override void Initialize() => Debug.Log("보스 생성 완료");
}

🏭 Step 4: EnemyFactory

using UnityEngine;

public class EnemyFactory : MonoBehaviour
{
    [Header("Enemy Prefabs")]
    public GameObject meleePrefab;
    public GameObject rangedPrefab;
    public GameObject bossPrefab;

    public Enemy CreateEnemy(EnemyType type, Vector3 spawnPosition)
    {
        GameObject prefab = type switch
        {
            EnemyType.Melee => meleePrefab,
            EnemyType.Ranged => rangedPrefab,
            EnemyType.Boss => bossPrefab,
            _ => null
        };

        if (prefab == null)
        {
            Debug.LogWarning("해당 EnemyType에 대한 프리팹이 없습니다.");
            return null;
        }

        GameObject enemyObj = Instantiate(prefab, spawnPosition, Quaternion.identity);
        Enemy enemy = enemyObj.GetComponent<Enemy>();
        enemy?.Initialize();
        return enemy;
    }
}

🧪 Step 5: 사용 예시

public class GameManager : MonoBehaviour
{
    public EnemyFactory enemyFactory;

    private void Start()
    {
        enemyFactory.CreateEnemy(EnemyType.Melee, new Vector3(0, 0, 0));
        enemyFactory.CreateEnemy(EnemyType.Ranged, new Vector3(2, 0, 0));
        enemyFactory.CreateEnemy(EnemyType.Boss, new Vector3(4, 0, 0));
    }
}

5. 정리 및 장단점

✅ 장점

  • 객체 생성 로직 분리로 책임이 명확해짐
  • 새로운 Enemy 타입 추가 시, Factory만 수정하면 됨
  • 의존성 최소화, 테스트와 유지보수 쉬움

❌ 단점

  • 구조가 조금 복잡해질 수 있음
  • Factory에 너무 많은 책임이 집중되면 God Object가 될 수 있음

✅ 마무리 한 줄 요약

팩토리 패턴은 **"직접 만들지 말고, 공장에 맡겨라"**는 원칙으로, 유지보수성과 확장성이 뛰어난 객체 생성 구조를 제공합니다.


 

반응형
반응형

🔭 Unity 디자인 패턴 - 옵저버(Observer) 패턴

1. 옵저버 패턴이란?

옵저버(Observer) 패턴은 어떤 객체의 상태가 변경될 때, 그 객체를 "구독"하고 있는 다른 객체들에게 자동으로 알림을 보내는 구조입니다. Unity에서는 UI나 게임 오브젝트가 데이터나 이벤트의 변경을 실시간으로 반영해야 할 때 자주 사용됩니다.

"플레이어의 체력이 변하면 체력바가 자동으로 바뀌는 것"이 대표적인 예입니다.


2. 언제 사용하나요?

  • 플레이어 체력/마나 등의 수치 변화 → UI 업데이트
  • 게임 설정 변경 → 여러 오브젝트에 즉시 반영
  • 이벤트 중심 구조로 게임 로직을 관리할 때

3. 구조 설명

  • Subject (발행자): 상태를 가지고 있는 객체. 변화가 생기면 옵저버에게 알림
  • Observer (구독자): Subject의 변화를 감지하고 반응하는 객체들

4. Unity 예제: 플레이어 체력 변화 → 체력바 UI 갱신

📦 인터페이스 정의 (옵저버 공통)

public interface IHealthObserver
{
    void OnHealthChanged(int current, int max);
}

🎮 Subject: Player

using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    private int maxHp = 100;
    private int currentHp;

    private List<IHealthObserver> observers = new();

    private void Start()
    {
        currentHp = maxHp;
    }

    public void TakeDamage(int damage)
    {
        currentHp = Mathf.Max(currentHp - damage, 0);
        NotifyObservers();
    }

    public void Heal(int amount)
    {
        currentHp = Mathf.Min(currentHp + amount, maxHp);
        NotifyObservers();
    }

    public void AddObserver(IHealthObserver observer)
    {
        if (!observers.Contains(observer)) observers.Add(observer);
    }

    public void RemoveObserver(IHealthObserver observer)
    {
        if (observers.Contains(observer)) observers.Remove(observer);
    }

    private void NotifyObservers()
    {
        foreach (var observer in observers)
        {
            observer.OnHealthChanged(currentHp, maxHp);
        }
    }
}

🖼️ Observer: HealthBar

using UnityEngine;
using UnityEngine.UI;

public class HealthBar : MonoBehaviour, IHealthObserver
{
    [SerializeField] private Slider slider;
    private Player player;

    private void Start()
    {
        player = FindObjectOfType<Player>();
        player?.AddObserver(this);
        OnHealthChanged(playerHp: 100, maxHp: 100); // 초기화
    }

    public void OnHealthChanged(int playerHp, int maxHp)
    {
        slider.value = (float)playerHp / maxHp;
    }

    private void OnDestroy()
    {
        player?.RemoveObserver(this);
    }
}

5. 정리 및 장단점

✅ 장점

  • 느슨한 결합 (Player와 UI는 서로 직접 몰라도 됨)
  • 실시간 반응 구조
  • 여러 객체에 동일한 변화 전파 가능

❌ 단점

  • Observer 등록/해제 누락 시 예기치 않은 동작 발생
  • 규모가 커지면 디버깅이 어려워질 수 있음

✅ 마무리 한 줄 요약

옵저버 패턴은 "어떤 일이 생기면 알려줄게" 라는 약속으로, 게임 내 실시간 반응형 구조를 만드는 데 매우 유용한 패턴입니다.

 

반응형
반응형

❓ 왜 Generic Singleton이 필요할까?

Unity에서는 GameManager, SoundManager, UIManager 등 다양한 매니저 클래스에서 싱글톤(Singleton) 패턴을 사용한다.
하지만 매번 static instance 선언, Awake()에서 중복 제거 같은 코드를 반복하게 된다.

이런 중복 코드를 줄이고 재사용 가능한 형태로 만들기 위해 Generic Singleton 패턴을 도입할 수 있다.


🧠 Generic Singleton이란?

Singleton<T>처럼 제네릭 타입을 활용하여 싱글톤 로직을 부모 클래스에서 처리하고,
필요한 매니저들은 상속만 하면 되는 구조다.

 

🧩 Singleton<T> 기본 구현

using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;

    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<T>();

                if (_instance == null)
                {
                    Debug.LogError(typeof(T) + " 싱글톤 인스턴스를 찾을 수 없습니다!");
                }
            }

            return _instance;
        }
    }

    protected virtual void Awake()
    {
        if (_instance == null)
        {
            _instance = this as T;
        }
        else if (_instance != this)
        {
            Destroy(gameObject); // 중복 방지
        }
    }
}

 

🧩 사용법: GameManager 예시

public class GameManager : Singleton<GameManager>
{
    public void GameOver()
    {
        Debug.Log("게임 오버!");
    }
}

이제 다른 스크립트에서는 다음처럼 사용 가능하다:

GameManager.Instance.GameOver();

 

✅ 장점 정리

  • ✅ 중복 코드 제거
  • ✅ 유지보수 용이
  • ✅ 확장성과 재사용성 증가
  • ✅ 상속만으로 싱글톤 구조 구현

⚠️ 주의사항

  • Unity는 다중 상속이 불가하므로, 이미 다른 클래스를 상속 중이라면 사용할 수 없다.
  • DontDestroyOnLoad(this) 등의 추가 처리는 자식 클래스에서 해야 한다.

📝 마무리

Generic Singleton은 프로젝트 규모가 커질수록 코드 정리와 유지보수에 큰 힘이 된다.
공통적인 싱글톤 구조를 반복 작성하지 않고, 한 번의 설계로 여러 매니저에 적용할 수 있는 패턴으로 적극 활용해보자!

 

사실 이걸보면서 잘 사용할까 싶었다..ㅎㅎ.. 나중에 천천히 또 공부해봐야지..

반응형