[Unity] 타워 디펜스 스킬 시스템 구현(스킬 사용 및 쿨다운, 강화 UI 연동)

 

팩토리 메서드 패턴 (Factory Method Pattern)

  • 정의: 객체를 생성하는 부분을 서브 클래스에 위임하는 패턴. 상위 클래스는 어떤 객체가 생성될지 모르고, 하위 클래스가 실제 생성될 객체를 결정.
  • 적용된 부분: SkillDataSO의 CreateSkill() 추상 메서드와, 이를 상속받은 MagicPoeDataSO의 CreateSkill() 메서드
// SkillDataSO.cs (설계도)
public abstract class SkillDataSO : ScriptableObject
{
    // "스킬 컨트롤러를 생성해야 한다"는 규칙만 정의
    public abstract ISkill CreateSkill(Transform owner);
}

// MagicPoeDataSO.cs (실제 공장)
public class MagicPoeDataSO : SkillDataSO
{
    // "마법의 포댕이 스킬은 MagicPoeController를 생성해야 한다"고 구체적으로 구현
    public override ISkill CreateSkill(Transform owner)
    {
        // ...
        var controller = go.AddComponent<MagicPoeController>();
        // ...
        return controller;
    }
}
  • 결과: TowerSkillSelectionController는 그저 skillSO.CreateSkill()을 호출하기만 하면 됨. 이후 MagicPoeDataSO가 올지, 나중에 만들 FireballDataSO가 올지에 따라 알아서 각자에게 맞는 컨트롤러가 생성됨. 스킬을 사용하는 쪽은 스킬이 어떻게 생성되는지 전혀 신경 쓸 필요가 없어진다.

전략 패턴 (Strategy Pattern)

  • 정의: 유사한 계열의 알고리즘(전략)들을 개별적인 클래스로 캡슐화하고, 이들을 서로 교체해서 사용할 수 있게 만드는 패턴.
  • 적용된 부분: ISkill 인터페이스와, 이를 구현한 MagicPoeController 클래스
// ISkill.cs (전략의 종류)
public interface ISkill
{
    void ExecuteSkill(Vector3 targetPosition); // 싸우는 방법
    bool IsReady(); // 싸울 준비가 됐는지
    // ...
}

// MagicPoeController.cs (ISkill을 상속받은 구체적인 전략 A)
public class MagicPoeController : MonoBehaviour, ISkill
{
    public void ExecuteSkill(Vector3 targetPosition)
    {
        // "마법의 포댕이"를 소환하는 방식으로 싸운다.
    }
    // ...
}
  • 결과: TowerSkillSelectionController는 ISkill 타입의 변수를 통해 ExecuteSkill()을 호출한다. 이때 변수에 MagicPoeController가 담겨있든, 미래에 만들 FireballController가 담겨있든 상관없이 똑같이 "스킬 실행"이라는 명령을 내릴 수 있다. 스킬의 구체적인 실행 방식(전략)을 언제든지 갈아 끼울 수 있는 유연한 구조가 만들어졌다.

OCP: 개방-폐쇄 원칙 (Open-Closed Principle)

  • 정의: 소프트웨어의 구성요소(클래스, 모듈 등)는 확장에는 열려(Open) 있어야 하고, 수정에는 닫혀(Closed) 있어야 한다는 원칙이다.
  • 적용된 부분: 스킬 시스템 아키텍처 전체에 적용되었다.
  • 결과: 만약 우리가 '얼음 벽'이라는 새로운 스킬을 추가하고 싶다고 하면 아래의 파일들만 새로 만들기만 하면 된다.
    • IceWallDataSO.cs
    • IceWallLevelData.cs
    • IceWallController.cs
    새로운 스킬을 추가하기 위해 기존에 잘 동작하던 TowerSkillSelectionController, SkillListItem, SkillInfoView 코드는 단 한 줄도 수정할 필요가 없는 '확장에는 열려있고, 수정에는 닫혀있는' OCP를 만족하는 구조이다.

DIP: 의존성 역전 원칙 (Dependency Inversion Principle)

  • 정의: 상위 모듈이 하위 모듈에 의존해서는 안 되며, 둘 모두 추상화에 의존해야 한다는 원칙이다. 즉, 구체적인 클래스가 아닌 인터페이스나 추상 클래스에 의존하라는 의미이다.
  • 적용된 부분: UI 관련 클래스들(SkillListItem, SkillInfoView 등)과 실제 스킬 로직(MagicPoeController)의 관계.
    • 상위 모듈 (High-level): SkillListItem, SkillInfoView (어떤 스킬이든 표시해야 하므로 더 일반적)
    • 하위 모듈 (Low-level): MagicPoeController ('마법의 포댕이'라는 하나의 구체적인 스킬 구현체)
    • 추상화 (Abstraction): ISkill 인터페이스
  • 결과: SkillListItem은 MagicPoeController의 존재를 전혀 모른다. SkillListItem이 아는 것은 오직 ISkill이라는 '약속'뿐이다. MagicPoeController 역시 ISkill이라는 약속을 지키고 있을 뿐이다.
    • 비유: 노트북(상위 모듈)은 발전소(하위 모듈)가 어떻게 생겼는지 모른다. 노트북과 발전소 둘 다 '220V 두 구멍짜리 콘센트'(추상화)라는 약속에만 의존해 어떤 콘센트에든 노트북을 꽂을 수 있다.
    SkillListItem이 ISkill에만 의존하기 때문에 어떤 종류의 스킬이든 SkillListItem에 표시할 수 있는 유연하고 견고한 시스템을 만들 수 있다.

 

 

 

 

반응형