건물 홀로그램이 무엇입니까?

건물 홀로그램은 건물을 건설할 때 발생하는 논리를 제어하고 플레이어가 건물을 배치할 수 있는 위치를 결정합니다.

FGBuildable은 건설되기 위해 지정된 홀로그램이 필요합니다. 대부분의 경우, 커피 스테인에서 만든 기존 홀로그램 클래스를 재사용할 수 있습니다. 그들은 이미 많은 일반적인 사용 사례를 다루고 있기 때문입니다. 그러나 건물을 업그레이드하거나 다른 건물에 부착하거나 환경의 무언가에 부착할 때와 같은 추가 기능을 위해 자체 로직을 작성하고 싶을 수 있습니다.

자신의 홀로그램 로직을 작성하려면 일부 C‍+‍+ 코드를 작성해야 합니다. 이 페이지는 C++에 대한 기본 지식을 가정합니다.

나만의 홀로그램 클래스 만들기

먼저 FGBuildableHologram 또는 그 서브클래스 중 하나를 확장하는 클래스를 만들어야 합니다. 예를 들어, FGFactoryBuildingHologram(주프를 구현함) 또는 FGSplineHologram(파이프 및 벨트에 사용됨)과 같은 클래스를 사용하여 일부 기능을 재사용할 수 있습니다.

대부분의 기본 게임 홀로그램 헤더는 Source/FactoryGame/Public/Hologram/에서 찾을 수 있습니다.

이 문서 페이지는 여러분의 홀로그램 클래스가 AMyModHologram이라고 가정합니다.

홀로그램 단계

홀로그램 코드는 건설 메뉴에서 건물을 선택할 때 활성화됩니다. 유효한 충돌, 부착, 구성된 기타 조건이나 테스트를 확인하는 것으로 시작합니다. 이러한 검사는 지속적으로 실행되지만, 아래에서 논의된 대로 구성할 수 있습니다.

사용자가 활성 홀로그램으로 "실행" (즉, 클릭)할 때마다 홀로그램을 다음 단계로 나아가게 하는 함수가 호출됩니다. 기본 함수는 bool FGHologram::DoMultiStepPlacement(bool isInputFromARelease)입니다.

인라인 문서에 명시된 대로, 이 함수가 true를 반환할 때 건설이 완료됩니다. 벨트, 빔, 전선 등과 같은 다단계 홀로그램을 만들려면 내부 상태를 추적해야 합니다. 상태를 추적하고 코드를 읽기 쉽게 만들기 위해 열거형을 만드는 것이 좋습니다. FGBeamHologram과 같은 방식으로요. 각 클릭이 발생할 때, 중요한 정보를 저장하고, 내부 상태를 진행시키며, false를 반환합니다. 필요한 모든 정보를 수집한 후 마지막 실행에서 true를 반환합니다. 첫 번째 클릭에서 모든 정보를 수집한 경우 true를 반환해도 되며, 기본 클래스에 따라 이 함수를 재정의할 필요가 없을 수도 있습니다.

건물에 필요한 모든 정보를 적절한 함수로 제공하면 준비가 완료됩니다. 아래는 정수 대신 열거형을 사용하여 5단계 홀로그램이 어떻게 보일 수 있는지에 대한 의사 코드입니다.

AMyModHologram::iMaxStepCount = 5;

void AMyModHologram::DoMultiStepPlacement(bool isInputFromARelease)
{
	myPointArray[iCurrentStep] = HitResult.GetLocation();

	iCurrentStep++;

	return iCurrentStep == iMaxStepCount;
}

건설 모드

건설 모드는 사용자가 건물을 건설할 때 선택할 수 있는 다양한 옵션입니다. 예를 들어, 파이프 및 하이퍼튜브의 경우 곡선과 수직 및 수평 모드 등이 있으며, 벽 및 토대의 경우 기본 또는 주프 모드가 있습니다.

홀로그램은 GetSupportedBuildModes의 구현을 통해 건물에 대해 지원되는 모드를 정의합니다.

다음은 FGHologram.h에서 건설 모드와 관련된 몇 가지 C‍+‍+ 헤더입니다:

/**
* 홀로그램에 대해 구현된 건설 모드를 가져옵니다.
* @param out_buildmodes	 모든 지원되는 건설 모드가 포함된 배열
*/
UFUNCTION( BlueprintNativeEvent, Category = "Hologram" )
void GetSupportedBuildModes( TArray< TSubclassOf<UFGHologramBuildModeDescriptor> >& out_buildmodes ) const;

UFUNCTION( BlueprintPure, Category = "Hologram" )
TSubclassOf<UFGHologramBuildModeDescriptor> GetCurrentBuildMode();

UFUNCTION( BlueprintCallable, Category = "Hologram" )
void SetBuildMode( TSubclassOf<UFGHologramBuildModeDescriptor> mode );

UFUNCTION( BlueprintCallable, Category = "Hologram" )
void CycleBuildMode( int32 deltaIndex );

UFUNCTION( BlueprintPure, Category = "Hologram" )
bool IsCurrentBuildMode( TSubclassOf<UFGHologramBuildModeDescriptor> buildMode ) const;

virtual void OnBuildModeChanged();

아래 예제에서는 맞춤 홀로그램이 상위 클래스에서 상속한 지원되는 건설 모드에 추가 건설 모드 mNewBuildmode를 추가합니다. 건설 모드는 문자열 및 현지화 참조를 포함하는 데이터 객체이며, 실제 로직은 포함하지 않습니다.

void AMyModHologram::GetSupportedBuildModes_Implementation(TArray<TSubclassOf<UFGHologramBuildModeDescriptor>>& out_buildmodes) const
{
	Super::GetSupportedBuildModes_Implementation(out_buildmodes);

	if(mNewBuildmode)
	{
		out_buildmodes.AddUnique(mNewBuildmode);
	}
}

같은 BuildMode 클래스를 두 번 추가하지 말고, 또한 유효하지 않은 BuildMode를 추가하지 마십시오! 이러한 이유 때문에 예제에서 out_buildmodes에 항목을 추가하기 전에 null을 검사하고, AddUnique를 사용하는 것입니다.

건설 모드는 일반적으로 사용자가 빌드건을 꺼낼 때 할당 키를 눌러 변경됩니다(기본값 R). 그러나 SetBuildMode(mNewBuildmode) 또는 CycleBuildMode(1)(다음) / CycleBuildMode(-1)(이전)을 통해 프로그래밍 방식으로도 변경할 수 있습니다.

활성 건설 모드에 따라 홀로그램의 동작을 변경할 수 있습니다. 이를 감지하는 몇 가지 방법이 있습니다. 첫 번째는 IsCurrentBuildMode(mNewBuildmode) 함수를 사용하여 현재 건설 모드가 mNewBuildmode인지 테스트하는 것입니다. 또한 GetCurrentBuildMode()를 통해 활성 건설 모드를 가져오고 이를 mNewBuildmode와 비교할 수 있습니다.

또한 건설 모드가 변경될 때 작업을 수행할 수 있습니다. 예를 들어, 아래 스니펫은 OnBuildModeChanged()를 사용하여 변경된 건설 모드에 따라 MyFloat를 설정합니다.

void AMyModHologram::OnBuildModeChanged()
{
	Super::OnBuildModeChanged();

	if(IsCurrentBuildMode(MyBuildMode))
	{
		MyFloat = 0.0f;
	}
}

건설 모드 참조를 재사용하려면, 일반적으로 건물의 홀로그램에 있는 필드로 찾을 수 있습니다. 이 경우, 여러분의 홀로그램은 해당 건설 모드를 소유하는 클래스를 상속해야 합니다. 그렇지 않으면, 홀로그램의 BeginPlay/Construction에서 클래스의 CDO에서 참조를 읽을 수 있습니다.

세계에서 배치 제한

홀로그램은 물리적 한계(예: "바닥이 너무 가파름"), 건물 유형(전력 케이블과 같은) 또는 고급 로직(예: FicsIt-Network 컴퓨터 구성 요소, 컴퓨터 케이스 내부에만 배치 가능)에 따라 다양한 제약을 가질 수 있습니다.

여러분은 필요와 사용 사례에 맞게 이 로직을 사용자 정의해야 할 수도 있습니다. 예를 들어:

  • 벽에만 건설 허용

  • 자원 노드에 배치 요구

  • 하나 이상의 기존 건물 클래스에 부착

  • 여러분의 창의력이 요구하는 모든 것

FGBuildableHologram 클래스는 원하는 제약을 달성하기 위해 여러분의 홀로그램에서 오버라이드 할 수 있는 다양한 메서드를 노출합니다.

유효한 충돌 확인

빌드건이 게임 세계의 어딘가를 가리킬 때마다, 여러분의 홀로그램 클래스에서 정의된 IsValidHitResult 메서드가 호출됩니다. hitResult 인수는 위치에 대한 정보와 현재 조준하고 있는 Actor를 포함합니다. 이 데이터를 사용하여 특정 객체를 가리킬 때만 배치를 허용할 수 있습니다.

예를 들어, 여러분이 특정 기둥 MyModSpecificPillar를 만들었다고 가정해 보겠습니다. 이 기둥은 미적 이유로 특정 지지대 UMyModSpecificPillarSupport 위에만 배치할 수 있습니다. 이를 위해 다음과 같이 메서드를 재정의할 수 있습니다.

bool UMyModSpecificPillarHologram::IsValidHitResult(const FHitResult& hitResult) const
{
	AActor* Actor = hitResult.GetActor();

	// 특정 건물 클래스로 형변환을 시도합니다.
	const UMyModSpecificPillarSupport* = Cast<UMyModSpecificPillarSupport>(Actor);

	// 형변환 결과가 유효하면, UMyModSpecificPillarSupport를 조준하고 있다는 의미입니다.
	if (IsValid(UMyModSpecificPillarSupport))
	{
		// 이곳에서 더 많은 정보를 확인할 수 있습니다.
		// 예를 들어, 높이, 지지대에서 이미 바인딩된 필드 확인 등.
		// 단순화를 위해 여기서는 간단히 배치를 허용하겠습니다.
		return true;
	}

	return false;
}

이 메서드에서 false를 반환하면 홀로그램이 숨겨집니다. 단순히 빨간 홀로그램 을 표시하고, 조준한 위치에 배치할 수 없는 이유를 설명하는 메시지를 표시하려면, 자격 없는 비활성 홀로그램 표시를 참고하십시오.

다른 건물에 부착

때때로 건물이 하나의 액터에 부착하고 "잠기는" 것을 원하실 수 있습니다. 예를 들자면 송전선이 기존 전신주/연결부에 "부착"하는 경우가 있습니다. 이 상황의 특이점은 부착된 동안에는 홀로그램 위치가 업데이트되지 않는다는 것입니다. 즉, SetHologramLocationAndRotation 메서드가 호출되지 않습니다. 부착을 제어하기 위해서는 AFBuildableHologram::TrySnapToActor 메서드를 구현해야 합니다.

참고로, 기본 게임에서 이 행동은 다음 상황에서 구현됩니다.

  • 공장 연결을 다른 입/출력부에 부착(파이프, 벨트)

  • 지정된 부착 지점에 부착(전광판처럼)

이 동작은 격자에 정렬하는 것과 같은 것에는 의도되지 않았습니다. 그런 경우에는 SetHologramLocationAndRotation 메서드를 오버라이드 하여 위치 조정 로직을 수정하는 것을 고려해야 합니다.

"부착"이라는 용어는 "고정"을 의미하며, 건물을 이동할 수 없도록 하고 부착된 액터에 연결 을 표시하고 싶을 때 사용해야 합니다.

여러분의 반짝반짝한 새 창문 건물(MyModGlassWindow)을 기존의 벽에 부착하고 싶다고 가정하고 예를 들어보겠습니다. 부착했다면, 창문은 더 이상 움직일 수 없도록 벽에 고정되어야 합니다. 따라서 다음과 같이 작성하겠습니다.

// 헤더 파일에서는 Snapped 속성을 정의하여 현재 부착된 오브젝트를 추적합니다.
AFGBuildableWall* Snapped = nullptr;

// cpp 파일에서는 부착 메서드를 오버라이드 합니다.
bool UMyModGlassWindowHologram::TrySnapToActor(const FHitResult& hitResult)
{
	const auto Actor = hitResult.Actor.Get();

	if (!IsValid(Actor))
	{
		// 이전 부착에서 벗어났으므로 추적기를 치웁니다
		Snapped = nullptr;
		return false;
	}

	if (Actor->IsA<AFGBuildableWall>())
	{
		Snapped = Cast<AFGBuildableWall>(Actor);
		// 여기에서 맞춤 부착 로직을 추가할 수 있습니다.
		// `SetActorLocationAndRotation`을 사용하여 단일 속성을 설정하는 것보다 성능을 개선시킵니다.
		SetActorLocationAndRotation(Actor->GetActorLocation(), Actor->GetActorRotation());
		// 부착되었습니다. 이제 true를 반환하여 다음 업데이트를 비활성화합니다
		return true;
	}

	Snapped = nullptr;
	return false;
}

만약 메서드가 true를 반환하면, 홀로그램의 위치와 회전은 자동으로 업데이트되지 않을 것입니다. 홀로그램을 움직이기 위해선 맞춤 부착 로직을 작성해야 합니다.

만약 IsValidHitResultfalse를 반환하면 TrySnapToActor호출되지 않을 겁니다. 뿐만 아니라, IsValidHitResult기본 구현이 있음을 고려해야 하며, 문제가 있을 경우, return true; 문장으로 오버라이드 하여 코드가 TrySnapToActor에 도달하게 하십시오.

자격 없는 비활성 홀로그램 표시

건물을 배치하는 동안 빌드건으로 배치를 허용하지 않으면서도 빨간 외곽선과 홀로그램을 띄울 수 있습니다. 기본 게임에서는 "바닥이 너무 가파릅니다" 메시지와 함께 빨간 홀로그램이 뜨는 것을 예로 들 수 있습니다. 망할 철도 같으니!

비슷하게 건물에 구현하고 싶다면, 간단히 IsValidHitResult 메서드에서 true를 반환하면 됩니다 (TrySnapToActor 또는 CheckValidPlacement같은 메서드가 연속적으로 호출되게 합니다). 그리고 유효하지 않은 배치가 발견되었다면, AddConstructDisqualifier()를 사용하여 플레이어에게 오류를 알릴 수 있습니다.

이전의 MyModGlassWindow 예에서, 부착되지 않았다면 자격이 없음을 추가해 보겠습니다. 이번에는 창문 홀로그램이 빨간색으로 강조된 것을 볼 수 있을 겁니다.

void UMyModGlassWindowHologram::CheckValidPlacement() {
	if (!IsValid(Snapped) || !Snapped->IsA<AFGBuildableWall>()) {
		AddConstructDisqualifier(UFGCDMustSnapWall::StaticClass());
	}

	Super::CheckValidPlacement();
}

CheckValidPlacement에서 AddConstructDisqualifier를 호출하는 것이 필수는 아닙니다. 예시로, TrySnapToActor에서도 작동합니다.

기존 게임에 이미 존재하는 수많은 실격자 중 하나를 여러분의 건물을 위해 재사용할 수도 있습니다. 예를 들어 UFGCDMustSnapWall는 벽에 부착되는 것을 요구합니다. 전체 목록은 Source/FactoryGame/Public/FGConstructDisqualifier.h 헤더 파일에서 찾을 수 있습니다.

다음처럼 나만의 맞춤 실격자를 정의할 수도 있습니다.

#define LOCTEXT_NAMESPACE "MyModLocNamespace"

UCLASS()
class UMyModCDMustSnapBeautifulWall : public UFGConstructDisqualifier {
	GENERATED_BODY()

	UMyModCDMustSnapBeautifulWall() {
		mDisqfualifyingText = LOCTEXT( "UMyModCDMustSnapBeautifulWall", "아름다운 벽에 부착해야 합니다!" );
	}
};

#undef LOCTEXT_NAMESPACE

속성의 철자는 mDisqualifyingText가 아닌 mDisqfualifyingText입니다. 이는 단순히 여러분의 파일과 일치해야 하는 게임 헤더의 오타입니다.

건물 구성하기

홀로그램은 또한 건물이 지어짐에 따라 값을 제공하거나 건물에 변화를 줄 수 있게 합니다. 이를 통해 건물의 위치에 따라 메쉬를 변경하거나 구성 요소를 약간 회전시키거나 부착된 건물에 대한 참조를 설정할 수 있습니다.

건물 대해 무엇을 하고 싶은지, 그리고 언제 변경을 원하느냐에 따라 사용할 수 있는 단계가 다릅니다.

구성 함수는 다음 순서로 호출되므로 각자의 단계를 무시할 수 있습니다.

이 목록은 FGBuildableHologram.h의 댓글에서 발췌한 것입니다.

  • PreConfigureActor( 건물 );

  • ConfigureActor( 건물 );

  • ConfigureBuildEffect( 건물 );

  • (세계에 건물 액터의 실제 소환을 수행합니다)

  • ConfigureComponents( 건물 );

  • (건물에서 BeginPlay가 호출됩니다)

이제 조금 더 자세히 알아봅시다.

PreConfigureActor

/**
 * 설정이 발생하기 전에 액터에 대한 사전 초기화를 허용하는 기능입니다. 이 기능은 최종 점검을 허용하고
 * 속성을 설정할 수 있도록 하기 위한 것으로, 여기서부터 모든 컴포넌트를 구성할 때처럼 설정할 수 있습니다
 */
virtual void PreConfigureActor( class AFGBuildable* inBuildable );

특정 경우에는 액터의 구성이 시작되기 전에 속성을 다시 확인해야 할 수도 있으며, 여기서 가능합니다.

ConfigureActor

/**
* 구성 함수: 실행 시 홀로그램에서 생성된 액터를 구성합니다.
* @param inBuildable - 완성되기 전에 구성할 세계에 배치된 건물입니다.
* @note 여기의 컴포넌트는 오브라이드 되므로 만지지 마십시오! ConfigureComponents를 사용하십시오.
*/
virtual void ConfigureActor( class AFGBuildable* inBuildable ) const;

액터 구성은 속성을 설정하는 데만 사용해야 하며, 컴포넌트를 생성하는 데 사용해서는 안 됩니다.

예를 들어, 건물 업그레이드를 수행할 때 업그레이드된 액터에서 새로운 액터로 속성을 이동하는 데 유용합니다.

ConfigureBuildEffect

/** 구축된 액터에 대한 건설 효과를 구성합니다. */
void ConfigureBuildEffect( class AFGBuildable* inBuildable );

ConfigureComponents

/**
* 구성 함수: 실행 시 홀로그램에서 생성된 액터 컴포넌트를 구성합니다.
* @param inBuildable - 완성되기 전에 구성할 세계에 배치된 건물입니다.
* @note 부착된 연결 등을 초기화하기에 좋은 곳입니다.
*/
virtual void ConfigureComponents( class AFGBuildable* inBuildable ) const;

컴포넌트 구성은 컴포넌트의 위치를 변경하거나, 업그레이드된 액터와의 파이프 연결을 재배치하는 데 좋습니다.

건물 업그레이드

홀로그램은 또한 기존 건물의 업그레이드 구현을 가능하게 해줍니다. 이는 건물이 여러 티어를 가졌으며, 기존 요소를 해체하지 않고 업그레이드하길 원할 때 유용합니다.

예를 들어 기본 게임에서는 벨트에 사용됩니다.

다음은 업그레이드와 관련된 FGHologram.h의 C‍+‍+ 코드입니다.

/** 업그레이드된 액터 대상 얻기 */
virtual AActor* GetUpgradedActor() const override;

/** 업그레이드 가능한가? */
virtual bool TryUpgrade(const FHitResult& hitResult) override;

private:
/** 업그레이드된 액터 대상 */
UPROPERTY(Transient)
AActor* mUpgradedActor = nullptr;

부분별로 자세히 살펴보겠습니다.

mUpgradedActor

UPROPERTY(Transient)
AActor* mUpgradedActor = nullptr;

이 필드는 업그레이드를 시도할 때 우리가 보고 있는 액터를 참조합니다. 우리가 새 건물로 옮기고 싶은 정보는 아마도 오래된 건물일 것입니다.

GetUpgradedActor

/** 업그레이드된 액터 대상 얻기 */
virtual AActor* GetUpgradedActor() const override;

여기에서 대상 액터를 반환해야 합니다(이 예에서는 mUpgradedActor).

TryUpgrade

/** 업그레이드 가능한가? */
virtual bool TryUpgrade(const FHitResult& hitResult) override;

이 함수는 액터를 업그레이드할 수 있는지 확인하기 위해 호출됩니다. 여기서 충돌 결과를 바탕으로 mUpgradedActor를 설정해야 합니다. 그렇지 않으면 이상한 일이 발생할 수 있습니다. 홀로그램의 위치를 충돌 액터의 위치로 설정해야 합니다. true를 반환한다는 것은 업그레이드가 허용된다는 것을 의미합니다.

업그레이드 홀로그램 예

C‍+‍+ 부분의 매우 기본적인 예입니다:

액터 업그레이드를 위한 기본 게임 로직은 연결 지점이 동일한 위치와 이름을 사용하는 한 벨트, 파이프, 전원 연결을 자동으로 처리합니다.

그러나, 기계에서 선택한 제작법과 같은 필드처럼, 인벤토리 또한 수동으로 전송해야 합니다.

이에 대한 예로는 ConfigureComponents 단계에서 NewBuildingInventory→CopyFromOtherComponent(OldBuildingInventory);를 사용할 수 있습니다.

AActor* AMyModHologram::GetUpgradedActor() const
{
	// 대상 액터를 반환하여 게임 내에서 숨김!
	return mUpgradedActor;
}

bool AMyModHologram::TryUpgrade(const FHitResult& hitResult)
{
	if(hitResult.GetActor())
	{
		const TSubclassOf<AActor> ActorClass = GetActorClass();

		// 같은 액터를 업그레이드하진 않는지 확인. 클래스가 달라야 함!
		if(hitResult.GetActor()->GetClass() != ActorClass)
		{
			// 중요: 우리의 홀로그램의 위치를 대상 액터에 설정해야 함
			SetActorTransform(hitResult.GetActor()->GetActorTransform());

			// UpgradedActor를 설정하고 유효하다면 true를 반환함(반드시 확인해야 함)
			mUpgradedActor = hitResult.GetActor();

			return mUpgradedActor != nullptr;
		}
	}

	// 그렇지 않으면 UpgradedActor를 nullptr로 설정
	mUpgradedActor = nullptr;
	return Super::TryUpgrade(hitResult);
}

추가 시각화 표시

블루프린트 기계 홀로그램은 사용자가 건물을 배치하는 데 도움이 되는 맞춤 시각 요소를 생성할 수 있습니다. 맞춤 시각화를 사용하면 기본 시각화가 대체됩니다. 이 접근 방식은 맞춤 건물이 올바르게 보이기 위해 추가 설정과 구성이 필요할 때 일반적으로 사용됩니다. 다음 예시를 고려해 보십시오:

void FMyModModule::StartupModule() {
	AFGBlueprintHologram::RegisterCustomBuildableVisualization(
		AABCurvedDecorBuildable::StaticClass(),
		AFGBlueprintHologram::FCreateBuildableVisualizationDelegate::CreateLambda([](AFGBlueprintHologram* blueprintHologram, AFGBuildable* buildable, USceneComponent* buildableRootComponent) {
			// 건물 얻기
			AMyBuildable* myBuildable = Cast<AMyBuildable>(buildable);

			// - 모든 관련 컴포넌트에 대한 쩌는 코드
			// 아마도 myBuildable을 참조하는 것과 관련 있음
			// 건물 참조는 이 람다에만 적용되므로 계속 지속된다고 생각하지 말 것

			// 예를 들어, 보여줄 맞춤 스플라인 메쉬 컴포넌트가 하나 있다면 다음과 같이 보일 수 있음:
			USplineMeshComponent* splineMesh = Cast<USplineMeshComponent>(
				// 내장된 설정 컴포넌트를 사용하므로 액터에 부착, 변환, 이동성, 커스터마이저 데이터, 홀로그램FX, 충돌 채널 등을 걱정할 필요가 없음
				blueprintHologram->SetupComponent(
					buildableRootComponent,
					buildable->GetComponentByClass<USplineMeshComponent>(),
					buildable->GetFName(),
					FName()
				)
			);

			// 예: 이제 맞춤 구성을 사용하고 있으므로 기본 구성에서는 적용되지 않는 구성을 수행
			splineMesh->SetStartPosition(curve->StartPosition, false);
			splineMesh->SetEndPosition(curve->EndPosition, false);
			splineMesh->SetStartTangent(curve->StartTangent, false);
			splineMesh->SetEndTangent(curve->EndTangent, false);
			splineMesh->UpdateMesh_Concurrent();
		})
	);
}