게임에서 무기는 여러가지 종류가 있기 때문에 확장성과 유연성은 필수적인 요소 입니다. 만약 캐릭터가 검을 쓰다가 활을 쓴다 거나, 지팡이를 쓰는 경우에 애니메이션도 변경해 줘야 하고, 공격 방식도 변경해 줘야 합니다. 전략 패턴을 모르고 이러한 시스템을 구현하다 보면 막히는 부분이 많을 것 입니다. 이번 시간에는 전략 패턴의 개념을 이해하여 무기 시스템에 적용해 보는 시간을 가지겠습니다.

[전략 패턴이란?]

다양한 동작을 클래스로 캡슐화하고, 이러한 동작을 사용하는 클래스에서 필요에 따라 동적으로 교체할 수 있도록 하는 디자인 패턴입니다. 예를 들어서 무기의 공격방식을 클래스화 하고 캡슐화 하면, 캐릭터는 무기의 공격방식을 몰라도 사용이 가능하며, 교체가 용이해 집니다.

이렇게 개념을 설명해도 코드를 보는 게 빠르기 때문에 바로 코드를 구현해 보겠습니다.

[장점]

  • 알고리즘 교체가 쉽습니다.
    • 알고리즘 자체를 클래스화 하기 때문에 쉽게 교체가 가능합니다.
  • 테스트 코드를 작성하기 쉽습니다.
    • 알고리즘이 클래스화 됐다는 것은 모듈화 돼있다는 것이고, 그에 따라 테스트 코드 작성이 용이 하다는 뜻 입니다.

[단점]

  • 클래스 수가 증가합니다
    • 알고리즘을 클래스화 한다는 것은 그만큼 클래스 수가 증가한다는 것 입니다.

[무기 시스템 코드 구현]

[무기 인터페스 구현]

// 무기 인터페이스
public interface IWeapon
{
    void Attack();
}

무기를 정의하는 인터페이스를 만듭니다.

[각 무기의 클래스 구현]

// 검 클래스
public class Sword : IWeapon
{
    public void Attack()
    {
        Debug.Log("검으로 공격합니다!");
    }
}

// 활 클래스
public class Bow : IWeapon
{
    public void Attack()
    {
        Debug.Log("활로 공격합니다!");
    }
}

// 지팡이 클래스
public class Staff : IWeapon
{
    public void Attack()
    {
        Debug.Log("지팡이로 공격합니다!");
    }
}

각 무기에 따라서 공격 방식을 구현 합니다.

[무기를 사용할 유닛]

public class Unit : MonoBehaviour
{
    private IWeapon weapon;

    // 무기를 설정하는 메서드
    public void SetWeapon(IWeapon weapon)
    {
        this.weapon = weapon;
    }

    // 무기를 사용하는 메서드
    public void UseWeapon()
    {
        if (weapon != null)
        {
            weapon.Attack();
        }
        else
        {
            Debug.Log("무기를 착용하지 않았습니다!");
        }
    }
}

사용할 무기를 설정하고 무기를 사용할 때 무기 클래스의 Attack을 실행합니다. 이로써 무기의 공격 방식을 몰라도 유닛은 무기사용이 가능해 집니다.

[원하는 무기 사용하기]

Unit unit = new Unit();

// 검을 착용
unit.SetWeapon(new Sword());
unit.UseWeapon(); // "검으로 공격합니다!"

// 활을 착용
unit.SetWeapon(new Bow());
unit.UseWeapon(); // "활로 공격합니다!"

// 지팡이를 착용
unit.SetWeapon(new Staff());
unit.UseWeapon(); // "지팡이로 공격합니다!"

위와 같이 간단하게 초기화만 해줘도 어떤 무기든 사용이 가능해 집니다.

[구현한 코드의 장점]

무기 변경이 자유로워져 게임에서 무기를 구현하기 편해집니다. 또한 베이스 설계가 잘 돼 있기 때문에 어떤 기능이든 추가가 가능해집니다. 예를 들어서 애니메이션을 각각 다르게 설정도 가능하고, 무기의 속성을 표시해 주거나, 무기를 강화하는 기능 등을 넣을 수 있습니다.

제가 말한 기능들을 추가해 보겠습니다.

[무기 변경 시 애니메이션 전환]

// 무기 인터페이스에 애니메이션 재생 메서드 추가
public interface IWeapon
{
    void Attack();
    void PlayAnimation();
}

// 검 클래스에서 애니메이션 재생 구현
public class Sword : IWeapon
{
    public void Attack()
    {
        Debug.Log("검으로 공격합니다!");
    }

    public void PlayAnimation()
    {
        // 검 애니메이션 재생 코드
    }
}

// Unit 클래스에서 무기 변경 시 애니메이션 전환
public void SetWeapon(IWeapon weapon)
{
    this.weapon = weapon;
    this.weapon.PlayAnimation();
}

인터페이스의 애니메이션을 플레이 해주는 메서드를 정의해 주고, 각 무기에 애니메이션을 플레이하는 코드를 추가해 줍니다. 이로써 어떤 무기든 알맞는 애니메이션이 재생 됩니다.

[무기 속성 표시]

// 무기 인터페이스에 추가 속성 메서드 추가
public interface IWeapon
{
    void Attack();
    void PlayAnimation();
    void DisplayProperties();
}

// 검 클래스에서 추가 속성 구현
public class Sword : IWeapon
{
    public void Attack()
    {
        Debug.Log("검으로 공격합니다!");
    }

    public void PlayAnimation()
    {
        // 검 애니메이션 재생 코드
    }

    public void DisplayProperties()
    {
        Debug.Log("검은 근거리 공격에 특화되어 있습니다.");
    }
}

// Unit 클래스에서 무기 속성 표시
public void DisplayWeaponProperties()
{
    if (weapon != null)
    {
        weapon.DisplayProperties();
    }
    else
    {
        Debug.Log("무기를 착용하지 않았습니다!");
    }
}

[무기 강화]

// 무기 인터페이스에 강화 메서드 추가
public interface IWeapon
{
    void Attack();
    void PlayAnimation();
    void DisplayProperties();
    void Enhance();
}

// 검 클래스에서 강화 구현
public class Sword : IWeapon
{
    public void Attack()
    {
        Debug.Log("검으로 공격합니다!");
    }

    public void PlayAnimation()
    {
        // 검 애니메이션 재생 코드
    }

    public void DisplayProperties()
    {
        Debug.Log("검은 근거리 공격에 특화되어 있습니다.");
    }

    public void Enhance()
    {
        Debug.Log("검이 강화되었습니다!");
    }
}

// Unit 클래스에서 무기 강화
public void EnhanceWeapon()
{
    if (weapon != null)
    {
        weapon.Enhance();
    }
    else
    {
        Debug.Log("무기를 착용하지 않았습니다!");
    }
}

[마무리 글]

전략 패턴을 무기에 사용하듯이 알맞은 시스템에 적절한 디자인 패턴을 사용하면 확장성도 올라가며 프로젝트의 질 또한 올라갑니다. 하지만 디자인 패턴을 만능처럼 믿어서 남용하다 보면 오히려 구현하는데 시간이 오래 걸릴 수 있습니다. 이유는 시스템에 알맞은 패턴을 찾으려고 시간을 많이 쓰는 경우가 있기 때문 입니다. 그렇기에 너무 깊이 생각하여 완벽한 코드 설계를 하려고 하기 보다는 적정선을 지키며 구현하고, 코드를 개선해 나가는 방식이 좋은 것 같습니다. 긴 글 읽어 주셔서 감사합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다