
전략 패턴(Strategy Pattern)은 객체의 행위를 캡슐화하여 서로 교환 가능한 형태로 정의하고, 이러한 행위들을 객체가 동적으로 사용할 수 있도록 하는 디자인 패턴입니다. 주로 다양한 알고리즘이나 행동을 런타임에 선택하고 교체할 수 있는 상황에서 사용됩니다.
게임 개발에서는 캐릭터의 공격 방식, AI의 행동 패턴, 혹은 특정한 로직을 다양하게 적용해야 하는 상황이 자주 발생합니다. 이럴 때 전략 패턴을 사용하면 코드의 유연성과 재사용성을 높일 수 있으며, 새로운 전략을 추가하거나 기존 전략을 수정할 때도 코드의 변경을 최소화할 수 있습니다.
전략 인터페이스(Strategy Interface): 공통된 행동을 정의하는 인터페이스입니다. 예를 들어, 캐릭터의 공격 방식이라면 IAttackBehavior와 같은 이름으로 정의될 수 있습니다.
public interface IAttackBehavior
{
void Attack();
}
public interface IDefenseBehavior
{
void Defend();
}
그런데 여기서 의문점이 들었던게 이렇게 전에 배웠던 상태 패턴이 생각이 나게 되었다.
이렇게 인터페이스를 두개로 나누어서 개별 행동을 독립적으로 나누어 관리를 할수 있지만 상태패턴으로 모든 행동을 하나의 인터페이스에 포함해서, 여러가지 행동을 동시에 관리하는게 더 좋지 않을까? 라는 의문점을 가지게 되었다.
public interface ICharacterBehavior
{
void Attack();
void Defend();
void Move();
}
이러한 형식으로 말이다.
인터넷 서치를 통해서 이 상태 패턴(State Pattern)과 전략 패턴(Strategy Pattern)은 구조적으로 매우 유사하며, 둘 다 행동을 객체로 캡슐화하여 동적으로 변경할 수 있게 한다는 공통점을 가지고 있다. 그러나 이 둘은 목적과 사용 사례에서 차이가 있다는것을 알게되었다.
public interface ICharacterState
{
void Attack();
void Defend();
void Move();
void HandleStateChange();
}
public class NormalState : ICharacterState
{
private Character character;
public NormalState(Character character)
{
this.character = character;
}
public void Attack()
{
Debug.Log("Normal attack");
}
public void Defend()
{
Debug.Log("Normal defend");
}
public void Move()
{
Debug.Log("Normal move");
}
public void HandleStateChange()
{
if (character.Health < 20)
{
character.SetState(new BerserkState(character));
}
}
}
public class BerserkState : ICharacterState
{
private Character character;
public BerserkState(Character character)
{
this.character = character;
}
public void Attack()
{
Debug.Log("Berserk attack");
}
public void Defend()
{
Debug.Log("Berserk defend");
}
public void Move()
{
Debug.Log("Berserk move");
}
public void HandleStateChange()
{
if (character.Health >= 20)
{
character.SetState(new NormalState(character));
}
}
}
public class Character
{
public int Health { get; set; }
private ICharacterState state;
public Character()
{
state = new NormalState(this);
}
public void SetState(ICharacterState newState)
{
state = newState;
}
public void Attack()
{
state.Attack();
}
public void Defend()
{
state.Defend();
}
public void Move()
{
state.Move();
}
public void Update()
{
state.HandleStateChange();
}
}
public interface IAttackStrategy
{
void Attack();
}
public interface IDefenseStrategy
{
void Defend();
}
public interface IMoveStrategy
{
void Move();
}
public class MeleeAttack : IAttackStrategy
{
public void Attack()
{
Debug.Log("Melee attack");
}
}
public class ShieldDefense : IDefenseStrategy
{
public void Defend()
{
Debug.Log("Shield defense");
}
}
public class WalkMovement : IMoveStrategy
{
public void Move()
{
Debug.Log("Walking");
}
}
public class Character
{
private IAttackStrategy attackStrategy;
private IDefenseStrategy defenseStrategy;
private IMoveStrategy moveStrategy;
public void SetAttackStrategy(IAttackStrategy newAttackStrategy)
{
attackStrategy = newAttackStrategy;
}
public void SetDefenseStrategy(IDefenseStrategy newDefenseStrategy)
{
defenseStrategy = newDefenseStrategy;
}
public void SetMoveStrategy(IMoveStrategy newMoveStrategy)
{
moveStrategy = newMoveStrategy;
}
public void Attack()
{
attackStrategy.Attack();
}
public void Defend()
{
defenseStrategy.Defend();
}
public void Move()
{
moveStrategy.Move();
}
}
이 코드로 예시를 들어보자면 상태 패턴에서는 객체의 상태에 따라 내부적으로 전략이 변경됩니다. 예를 들어, 캐릭터의 체력이 낮아지면 자동으로 "Berserk" 상태로 전환되고, 이 상태에 맞는 행동이 자동으로 선택됩니다. 반면, 전략 패턴에서는 개발자가 특정 행동(예: 공격)을 위해 어떤 전략을 사용할지 직접 설정하고, 필요할 때 이를 변경해야 합니다.
전략패턴에 예를 더 들어보자면, 전략 패턴에서, 캐릭터의 공격 방식(전략)은 SetAttackStrategy 메서드를 통해 명시적으로 설정됩니다. 만약 플레이어가 게임 내에서 무기를 바꾸면, 개발자가 해당 무기에 맞는 전략을 설정해줘야 합니다.
예를 들어, 플레이어가 인벤토리에서 활을 선택하면 원거리공격 전략을 설정하고, 검을 선택하면 근접 공격 전략으로 설정합니다.
그래서 이걸 정리를 해보자면
정리
상태 패턴의 활용:
HP 상태에 따른 행동 변화: 예를 들어, 캐릭터의 체력이 낮아지면 "위기 상태"로 전환되고, 이 상태에서 캐릭터가 더 빠르게 이동하거나 방어 중심의 행동을 자동으로 수행하도록 할 수 있습니다. 상태 패턴은 이런 식으로 캐릭터의 상태에 따라 행동이 자동으로 전환될 때 매우 유용합니다.
예시:
HP가 50% 이상: 캐릭터가 일반적으로 행동합니다.
HP가 50% 미만: 캐릭터가 "위기 상태"로 전환되어 더 빠르게 뛰거나, 방어적인 행동을 자동으로 수행합니다.
전략 패턴의 활용:
공격의 세분화: 플레이어가 선택하는 무기나 기술에 따라 공격 방식이 달라질 때 전략 패턴이 유용합니다. 예를 들어, 플레이어가 원거리 무기를 선택하면 원거리 공격 전략을 적용하고, 근접 무기를 선택하면 근접 공격 전략을 적용할 수 있습니다. 이 과정은 개발자가 명시적으로 전략을 설정하고 변경해야 하므로, 플레이어의 선택이나 상황에 따라 유연하게 적용할 수 있습니다.
예시:
원거리 무기 선택: 원거리 공격 전략 적용 (활, 마법 등).
근접 무기 선택: 근접 공격 전략 적용 (검, 도끼 등).
상태 패턴은 캐릭터의 상태 변화(예: HP에 따른 행동 변화)를 자동으로 관리할 때 효과적입니다.
전략 패턴은 행동의 세분화(예: 다양한 공격 방식)를 유연하게 설정하고 변경할 때 효과적입니다.
따라서, HP 상태에 따라 캐릭터의 전반적인 상태(위기 상태, 정상 상태 등)를 관리하는 데는 상태 패턴을 사용하고, 플레이어가 선택한 무기나 기술에 따른 공격 방식을 세부적으로 관리하는 데는 전략 패턴을 사용하는 것이 이상적인 접근인것같다. 두 패턴을 적절히 조합하면 더 유연하고 확장성 있는 게임 시스템을 설계할 수 있을것같습니다.