Facility간 Link시 함수 호출 순서 문제

1. 문제


기존 On~방식의 함수만 사용했을때, 오버라이딩된 함수들에게 사용될 로직들의 순서를 적절하게 구성하는 것이 어려움. 이후 새로운 로직을 추가하거나 버그를 수정할 때 빠르게 파악하기 어려운 문제가 있다.

bool UPowerProviderComponent::TryLinkPowerConsumerFacility(APowerConsumerFacility* PowerConsumerFacility)
{
	if (!IsLinked(PowerConsumerFacility))
	{
		LinkedPowerConsumerFacilities.AddUnique(PowerConsumerFacility);
		// Provider와 Consumer가 연결되었을 때, Consumer측 로직은 OnLinkToPowerProvider를 통해 실행된다.
		PowerConsumerFacility->OnLinkToPowerProvider(GetOwner());
		return true;
	}
	return false;
}

구체적인 상황을 설명하자면, A->B->C 구조로 상속된 클래스의 On이라는 함수가 있다고 생각해 보자. 이 함수는 오버라이딩 되며 a, b, c라는 로직을 실행해야 하고 a->c->b의 순서로 실행되어야 한다. 이를 구현하면 아래와 같이 할 수 있다.

void A::On()
{
	a;
}

void B::On()
{
	b;
}

void C::On()
{
	A::On();
	c;
	B::On();
}

구현 자체는 성공하였지만, 여러가지 문제가 있다.

  1. B클래스는 부모 클래스의 함수를 호출할 수 없다
  2. 언리얼 엔진 코드컨벤션에 적절한 Super키워드를 사용할 수 없다.
  3. 이후 유지보수할 때 코드를 파악하기 어렵다

2. 시도


이를 해결하기 위해 On~이외에, Pre~, Post~ 함수를 추가하였다.
이 구조는 AActor클래스에 있는 함수방식을 차용했다.
4. Archive/GSB/__Attachments/Pasted image 20251016203959.png

PowerProviderComponent.cpp

bool UPowerProviderComponent::TryLinkPowerConsumerFacility(APowerConsumerFacility* PowerConsumerFacility)
{
	if (!IsLinked(PowerConsumerFacility))
	{
		PowerConsumerFacility->PreLinkToPowerProvider();
		LinkedPowerConsumerFacilities.AddUnique(PowerConsumerFacility);
		PowerConsumerFacility->OnLinkToPowerProvider(GetOwner());
		PowerConsumerFacility->PostLinkToPowerProvider();
		return true;
	}
	return false;
}

PowerConsumerFacility.cpp

void APowerConsumerFacility::PreLinkToPowerProvider_Implementation()
{
}

void APowerConsumerFacility::OnLinkToPowerProvider_Implementation(AActor* PowerProviderActor)
{
	LinkedPowerProvider = PowerProviderActor;
	LinkedPowerProviderInterface = Cast<IPowerProviderFacility>(LinkedPowerProvider);
	check(LinkedPowerProviderInterface);
	LinkedPowerProviderInterface->UpdatePowerUsage(PowerConsumption);
	for (AFacilityAddon* Addon : ConnectedAddons)
	{
		Addon->OnFacilityLinkedToPowerProvider();
	}
	UpdatePowerWidgets();

	// PowerWire 연결
	TryConnectPowerWire(PowerProviderActor);
}

void APowerConsumerFacility::PostLinkToPowerProvider_Implementation()
{
}

void APowerConsumerFacility::PreTurnOn_Implementation()
{
}

void APowerConsumerFacility::PostTurnOn_Implementation()
{
	TryLinkToNearByPowerProvider();
	UpdatePowerWidgets();
}

void APowerConsumerFacility::PreTurnOff_Implementation()
{
}

void APowerConsumerFacility::PostTurnOff_Implementation()
{
	UnlinkFromPowerProvider();
	UpdatePowerWidgets();
}

PowerRelayFacility.cpp(APowerConsumerFacility의 하위 클래스)

void APowerRelayFacility::OnLinkToPowerProvider_Implementation(AActor* PowerProviderActor)
{
	Super::OnLinkToPowerProvider_Implementation(PowerProviderActor);
	check(IsLinkedToPowerProvider())
	LinkedPowerProviderInterface->UpdatePowerUsage(PowerProviderComponent->GetCurrentPowerUsage());
	PowerProviderComponent->LinkFacilitiesInPowerInfluenceArea();
}

void APowerRelayFacility::OnUnlinkFromPowerProvider_Implementation()
{
	check(IsLinkedToPowerProvider());
	LinkedPowerProviderInterface->UpdatePowerUsage(-PowerProviderComponent->GetCurrentPowerUsage());

	Super::OnUnlinkFromPowerProvider_Implementation();
}

void APowerRelayFacility::PostUnlinkFromPowerProvider_Implementation()
{
	PowerProviderComponent->UnlinkAllPowerConsumerFacility();

	Super::PostUnlinkFromPowerProvider_Implementation();
}

void APowerRelayFacility::PreTurnOn_Implementation()
{
	Super::PreTurnOn_Implementation();
}

void APowerRelayFacility::PostTurnOn_Implementation()
{
	Super::PostTurnOn_Implementation();
	PowerProviderComponent->LinkFacilitiesInPowerInfluenceArea();
}

void APowerRelayFacility::PreTurnOff_Implementation()
{
	Super::PreTurnOff_Implementation();
}

void APowerRelayFacility::PostTurnOff_Implementation()
{
	Super::PostTurnOff_Implementation();
}

3. 결과