기본부터 만들기 시작하니 생각보다 힘이든다.
리팩토링을 새로하기엔 시간이 충분한걸까?
오히려 기본적으로 구현된 부분이 있기에 더욱 어려운 것 같다.
✨ 기존에 전략 패턴을 사용할 때 가장 큰 문제점은 모두가 같은 인터페이스만 사용하였기에 비슷한 코드밖에 작성하지 못하였다.
하지만 더 많은 기능을 추가하고 사용하는 방법이 있다.
🔨 기존에 사용하던 방식
public interface IBuildingAbility
{
public void Init(PassiveBuildingData data);
public void Activation();
public void Deactivation();
}
public class AbilityHeroStat : IBuildingAbility
{
private PassiveBuildingData _data;
public void Init(PassiveBuildingData data)
{
_data = data;
}
public void Activation() { }
public void Deactivation() { }
}
상속받는 모두가 같은 초기화 함수를 사용하기에 PassiveBuildingData
만을 사용하여 기능을 만들어 내야한다.
Data
에 더 많은 정보를 추가함으로써도 해결이 되지만 사용하지 않는 클래스들도 그 값을 가지고 있기에 불필요한 메모리 낭비가 되는 건 개선해야하는 부분 중 하나가 될 수 밖에 없다.
🔨 해결 방법
public interface IColliderProcessable
{
public void ColliderInit(List<BaseBuilding> baseBuilding);
}
public class AbilityGroundStat : IBuildingAbility, IColliderProcessable
{
private List<BaseBuilding> _baseBuildings;
private PassiveBuildingData _data;
public void ColliderInit(List<BaseBuilding> baseBuilding)s
{
_baseBuildings = baseBuildings;
}
public void Init(PassiveBuildingData data)
{
_data = data;
}
...
}
너무나도 당연한 인터페이스 역할인 기능 추가
를 활용하는 방법이다.
인터페이스에 대한 명확한 개념이 없었기에 이 방법을 생각하는데 엄청나게 오래 걸렸던 것 같다.
이렇게 함으로써 class AbilityGroundStat
은 Collider
를 가질 수 있는 기능이 추가되었다. 그럼 이 부분을 어떻게 활용할 것인가에 대하여 여러가지 방법이 있지만 현재 구조상 가장 간단한 방법으로 구현해보자.
🔨 인터페이스가 두 개인 구조를 사용해보자
public override void Init(BuildingType type)
{
base.Init(type);
switch (PassiveType)
{
case BuildingPassiveType.Water:
Ability = new AbilityGroundStat();
break;
case BuildingPassiveType.HeroCount:
Ability = new AbilityHeroStat();
break;
default:
break;
}
// 여기서부터가 인터페이스를 체크하는 부분
// is as 캐스팅을 두 번 사용하기에 효율적인 코드는 아니다.
if (Ability is IColliderProcessable)
{
IColliderProcessable cAbility = Ability as IColliderProcessable;
cAbility.ColliderInit(detectivedBuilding);
}
Ability.Init(_passiveData);
Ability.Activation()
}
단순하지만 IColliderProcessable
를 상속받은 클래스라면 ColliderInit
가 실행될 것이며 또한 Init
, Activation
가 공통적으로 실행 될 것이다.
이런 식으로 사용해봄으로써 인터페이스가 왜 객체지향의 꽃이라고도 불리는지 이해할 수 있게되었다.
📌 인터페이스가 꽃이라고 덕지덕지 붙이면 오히려 코드에 방해가 되고 구조를 짜는데 있어 시간이 오래 걸려 개발 시간이 늘어지게 되는 경우도 발생할 수 있다.
✨ 게임을 하다보면 오입력을 방지하기 위해서 키를 일정 시간동안 입력해줘야지 작동되는 방식이있다.
이런 방식은 따로 UI를 띄우지 않아도 되기때문에 플레이어의 몰입을 방해하지않는다고 생각한다.
🔨 입력을 받을 클래스
public Image Ring;
public event Action OnCompletePress; // 실행될 함수
public void InputStart()
{
StartCoroutine(nameof(PressEnter));
}
public void InputCancel()
{
StopCoroutine(nameof(PressEnter));
Ring.fillAmount = 0.0f;
}
private IEnumerator PressEnter()
{
while (Ring.fillAmount < 1.0f)
{
Ring.fillAmount += 0.7f * Time.deltaTime; // 원이 차오르는 속도
yield return null;
}
Ring.fillAmount = 0.0f;
OnCompletePress;
}
구조는 코루틴을 시작할 함수
와 끝내는 함수
그리고 일정 시간 뒤 함수를 실행할 코루틴
만 만들면 된다.
그리고 이 구조를 키입력을 담당하는 클래스에게 맡기면 된다.
🔨 InputSystem - SendMessage
public void OnMap(InputValue value)
{
bool isPressed = value.isPressed;
if (isPressed)
_travelGuideUI.StartEnter();
else
_travelGuideUI.CancelEnter();
}
인풋 시스템에서 키를 입력하면 눌렸을 때, 땔 때를 값으로 받아볼 수 있다.
즉, '누르는 중'일 때를 확인하는 것이 아니므로 성능적으로도 우수하다고 할 수 있다.
을 성공적으로 마무리했다.
굿!