파이프 시스템의 역할

파이프 시스템에서 관리하는 대상은 세 가지다.

Source = 보일러
Conduit = 파이프
Consumer = 파이프 연결이 필요한 설비

보일러는 동력의 시작점이고, 파이프는 동력을 전달하는 연결부다. 설비는 파이프망에 연결되어 있어야 작동할 수 있는 대상이다.

UKOPipeNetworkSubsystem
= 설치된 보일러, 파이프, 설비를 등록하고
= 보일러에서 시작해 연결된 파이프를 탐색하고
= 연결된 설비만 PipeConnected = true로 바꾸는 시스템


데이터 구조

파이프 역할 구분

UENUM(BlueprintType)
enum class EKOPipeRole : uint8
{
    None,
    Source,    // 보일러
    Conduit,   // 파이프
    Consumer   // 설비
};

FKOFactoryRow에 파이프 관련 정보를 추가한다.

 

데이터 테이블 설정

Boiler
- PipeRole = Source
- bRequiresPipeConnection = false
MiningPipe
- PipeRole = Conduit
- bRequiresPipeConnection = false
UndergroundMiningModule
- PipeRole = Consumer
- bRequiresPipeConnection = true


PipeNetworkSubsystem 구조

내부에 설치된 파이프망 요소들을 저장

USTRUCT()
struct FKOPipeNetworkNode
{
    GENERATED_BODY()

    UPROPERTY()
    TObjectPtr<AKOBaseBuilding> Building = nullptr;

    UPROPERTY()
    FName FactoryId = NAME_None;

    UPROPERTY()
    EKOPipeRole PipeRole = EKOPipeRole::None;

    UPROPERTY()
    FIntPoint Anchor = FIntPoint::ZeroValue;

    UPROPERTY()
    FIntPoint Size = FIntPoint(1, 1);
};

구조체 하나는 파이프 네트워크에 참여하는 설치물 하나를 의미한다.

이 데이터들은 UKOPipeNetworkSubsystem의 Nodes 배열에 저장된다.

UPROPERTY()
TArray<FKOPipeNetworkNode> Nodes;

설치된 보일러, 파이프, 설비 중 파이프 네트워크 계산에 참여하는 대상들의 목록이다.


등록과 등록 해제

건물이 설치되면 RegisterPipeObject()를 호출한다.

PipeNetwork->RegisterPipeObject(
    NewBuilding,
    BuiltFactoryId,
    CurrentFactoryRow->PipeRole,
    CurrentAnchor,
    CurrentBuildingSize
);

등록은 Nodes 배열에 해당 설치물 정보를 추가하는 작업이다.

 

반대로 건물이 파괴되면 UnregisterPipeObject()를 호출한다.

PipeNetwork->UnregisterPipeObject(TargetBuilding);

등록 해제는 Nodes 배열에서 해당 Actor 정보를 제거하는 작업이다.


연결 계산 흐름

파이프 연결 계산은 RebuildNetwork()에서 처리한다.

 

[전체 흐름]

1. Source, Conduit, Consumer 분류
2. Source와 Conduit이 차지하는 그리드 칸을 저장
3. 보일러 Source에서 BFS 탐색 시작
4. 상하좌우로 연결된 파이프를 따라 이동
5. 도달 가능한 파이프 칸을 PoweredPipeCells에 저장
6. Consumer가 PoweredPipeCells와 닿아 있는지 검사
7. 연결된 Consumer는 SetPipeConnected(true)
8. 연결되지 않은 Consumer는 SetPipeConnected(false)

 

PoweredPipeCells
= 보일러와 연결되어 동력이 흐르는 칸


AddOccupiedCells의 역할

Anchor와 Size를 기준으로 실제로 차지하는 모든 그리드 칸을 구한다.

void AddOccupiedCells(
    const FIntPoint& Anchor,
    const FIntPoint& Size,
    TSet<FIntPoint>& OutCells
) const;

보일러가 차지하는 칸 중 어느 면에든 파이프가 닿으면 연결된 것으로 판정된다.

나중에 PipePortOffsets 같은 데이터를 추가해서 특정 포트에만 파이프가 연결되도록 확장할 예정이다.


설비 작동 조건과 연결

최종 결과는 설비 Actor에 저장한다.

Building->SetPipeConnected(true);
Building->SetPipeConnected(false);

이후 실제 생산 컴포넌트에서 이 값을 확인한다.

if (!Building->IsPipeConnected())
{
    return;
}

정리

1. 보일러, 파이프, 설비를 파이프 네트워크에 등록
2. 설치/파괴 시 파이프 네트워크 재계산
3. 보일러에서 연결된 파이프 탐색
4. 파이프와 연결된 설비 판정
5. 연결된 설비는 PipeConnected = true
6. 끊어진 설비는 PipeConnected = false

if (Building->IsPipeConnected())
{
    // 설비 작동 가능
}
else
{
    // 설비 작동 불가
}