게임 개발/디자인 패턴

[디자인패턴] 7. 유니티에서 데코레이터 패턴

Heesuk Lee 2021. 8. 22. 17:38

꾸미기에 따라서 완전하게 다른 케이크가 된다는 점이 같다는 생각이 들었습니다.

오늘의 디자인 패턴은 데코레이터 패턴입니다.

 

데코레이터 패턴의 정의는 객체에 추가적인 요건을 동적으로 첨가한다는 것입니다. 그리고 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공해줍니다.

 

이번에 핵심적으로 다루는 객체지향 디자인 원칙은 OCP(Open-Closed Principle)으로써

클래스는 확장에 대해서는 열려있어야하며, 코드변경에 대해서는 닫혀있어야 한다는 원칙입니다.

 

위의 두가지를 고려해보면 어떤 클래스의 추가적인 요건을 동적으로 첨가할때 코드변경에 대해 닫혀있는 방식이 바로

데코레이터 패턴이라고 할수있습니다.

 

게임을 구현하는데 있어서 그렇게 자주 볼 수 있는 패턴은 아닌 것 같지만 해당패턴을 적용시켜 아이템을 꾸며보았습니다.

 

// 데코레이터 패턴으로 꾸며질 객체 - 추상클래스로 구현합니다.
public abstract class Weapon
{
	// 기본적으로 가져야할 멤버변수들 - 추가되는 변수가 있다면 여기서 추가합니다.
    protected string title = string.Empty;
    protected string description = string.Empty;
    protected float damage = 0f;
    
	// 해당 객체, 데코레이터에서 구현될 추상 메소드
    public abstract string GetTitle();
    public abstract string GetDescription();
    public abstract float GetDamage();
}


// 객체를 꾸며줄 데코레이터 입니다.
public abstract class Decorator : Weapon
{
    protected Weapon weapon = null;
}

 

데코레이터와 앞으로 다양한 옵션이 붙을 객체를 구현했습니다.

 

// 꾸며질 객체중의 하나를 '검'으로 만들었습니다.
public class Sword : Weapon
{
	// 생성자에서 기본정보를 초기화해줍니다.
    public Sword()
    {
        this.title = $"칼";
        this.description = $"일반적인 칼이다.";
        this.damage = 10f;
    }
    // 추상메소드를 구현해줍시다.
    public override string GetTitle()
    {
        return this.title;
    }
    public override string GetDescription()
    {
        return this.description;
    }

    public override float GetDamage()
    {
        return this.damage;
    }
}

 

앞으로 꾸며질 객체는 '검'으로 정해진 것 같습니다. 원한다면 더 다양한 무기들을 생성할 수 있습니다.

 

// 데코레이터 개념을 게임에서 무기 마법부여 개념으로 만들었습니다.

// 얼음 속성 부여
public class EnchantIce : Decorator
{
	// 생성자에서 추가될 능력들을 초기화 해줍니다.
    public EnchantIce(Weapon _weapon)
    {
        this.weapon = _weapon;
        this.description = $"일정시간 동안 상대를 얼립니다.";
        this.damage = 3f;
        this.title = $"얼리는";
    }
    
    // 추상 메소드를 구현해줍니다.
    public override string GetTitle()
    {
        return $"{this.title} {this.weapon.GetTitle()}";
    }
    public override string GetDescription()
    {
        return $"{this.weapon.GetDescription()}\n{this.description}";
    }

    public override float GetDamage()
    {
        return this.weapon.GetDamage() + this.damage;
    }
}

// 얼음 속성 부여
public class EnchantFire : Decorator
{
	// 생성자에서 추가될 능력들을 초기화 해줍니다.
    public EnchantFire(Weapon _weapon)
    {
        this.weapon = _weapon;
        this.description = $"추가 화염 데미지가 있습니다.";
        this.damage = 6f;
        this.title = $"폭발";
    }
    
    // 추상 메소드를 구현해줍니다.
    public override string GetTitle()
    {
        return $"{this.title} {this.weapon.GetTitle()}";
    }
    
    public override string GetDescription()
    {
        return $"{this.weapon.GetDescription()}\r\n{this.description}";
    }

    public override float GetDamage()
    {
        return this.weapon.GetDamage() + this.damage;
    }
}

 

이렇게 데코레이터들을 마법부여 '인첸트' 개념을 추가했습니다. 마찬가지로 원한다면 '강화', '무기 개조'등의 개념으로 데코레이터를 생성해 확장시킬 수 있습니다.

 

구현하면서 느낀점은 엄청나게 강력한 확장성을 가진 구조가 될 수 있다는 것을 느꼈습니다.

 

// 실행부를 구현했습니다.
public class ItemPanel : MonoBehaviour
{
    private Weapon item1 = null;
    private Weapon item2 = null;
    private Weapon item3 = null;
    private Weapon item4 = null;
	
    // 아이템 정보를 보여줄 창입니다.
    [SerializeField]
    private ItemInfo itemInfo = null;
    
    private void Awake()
    {
        this.itemInfo.Finish();
        // item1 - 일반검 초기화
        this.item1 = new Sword();
        
        // item2 - 얼음 속성 부여된 검 초기화
        this.item2 = new Sword();
        this.item2 = new EnchantFire(this.item2);

        // item3 - 화염 속성 부여된 검 초기화
        this.item3 = new Sword();
        this.item3 = new EnchantIce(this.item3);

        // item4 - 얼음, 화염 속성 부여된 검 초기화
        this.item4 = new Sword();
        this.item4 = new EnchantFire(this.item4);
        this.item4 = new EnchantIce(this.item4);
    }
	
    // 각 버튼을 누르면 아이템 정보가 출력됩니다.
    public void OnButtonItem1()
    {
        this.itemInfo.Init(this.item1);
    }
    public void OnButtonItem2()
    {
        this.itemInfo.Init(this.item2);
    }
    public void OnButtonItem3()
    {
        this.itemInfo.Init(this.item3);
    }
    public void OnButtonItem4()
    {
        this.itemInfo.Init(this.item4);
    }
}

 

실행부도 구현이 끝났으니 한번 무기 개념과 인첸트 개념으로 정리한 데코레이터 패턴을 보여드리겠습니다.

 

버튼 4개가 각각 "일반 검", "얼음 부여 검", "화염 부여 검", "얼음 화염 부여 검" 순서로 마법 부여시킨 아이템들을 출력해줍니다.

 

인첸트, 강화, 개조 등 엄청난 확장성을 가진 것 같습니다.

 

이렇게 데코레이터 패턴을 게임안에서 구현해봤습니다. 하지만 억지로 끼워넣은 감이 없잖아 존재한다는 느낌을 받았으며, 좀더 적절한 적용 파트를 찾을 수 있으면 좋겠다는 생각을 했습니다.

 

그리고 엄청난 확장성을 가진것은 확실하지만 스크립트 갯수가 너무 많이 필요하다는 느낌을 지울수가 없었습니다.

별기능 없는 속성 하나에 스크립트 하나가 존재하다보니 이게 단점이라면 단점이 될수 있을 것 같다는 생각을 했습니다.

 

 

오늘의 짤은 버그를 대응하는 저의 모습을 가져와봤습니다.

모든 힘을 끌어모아 버그를 막아야합니다! 그와중에 가장 힘이 약한 나의 모습...ㅋㅋ

반응형