Spawn and set GeometryCollection initialization fields at runtime

I just figured it out for C++. Create your own Geometry collection component and override the OnCreatePhysicsState virtual function. Invoke the parent function (via Super) at the end of your overridden function. In the Engine code, InitializationFields activity happens in this function, so you want to add your own anchor fields to this array before it’s processed, hence invoking Super at the end of your overridden function.

//
// MyGeometryCollectionComponent.h
//
#pragma once

#include "CoreMinimal.h"
#include "GeometryCollection/GeometryCollectionComponent.h"

#include "MyGeometryCollectionComponent.generated.h"

UCLASS()
class MY_GAME_API UMyGeometryCollectionComponent : public UGeometryCollectionComponent
{
	GENERATED_BODY()

	virtual void OnCreatePhysicsState() override;
};
//
// MyGeometryCollectionComponent.ccp
//

#include "put-the-path-to-MyGeometryCollectionComponent.h here"
#include "Kismet/KismetSystemLibrary.h"

#include "put-the-path-to-MyAnchor.h here"

void UMyGeometryCollectionComponent::OnCreatePhysicsState()
{
	FVector Loc = GetComponentLocation();
	TArray<AActor*> ActorsToIgnore;
	TArray<FHitResult> HitResults;

	UKismetSystemLibrary::SphereTraceMulti(GetWorld(), Loc, Loc, 100.0f,
		UEngineTypes::ConvertToTraceType(ECC_Visibility), false, ActorsToIgnore, EDrawDebugTrace::Persistent, HitResults, true);

	InitializationFields.Empty();

	for (const FHitResult& HitResult : HitResults)
	{
		AActor* HitActor = HitResult.GetActor();
		if (HitActor->IsA<AMyAnchor>())
		{
			AMyAnchor* A = Cast<AMyAnchor>(HitActor);
			if (IsValid(A))
			{
				InitializationFields.Add(A);
			}
		}
	}

	Super::OnCreatePhysicsState();
}

The Sphere trace is just a method for your component to find the Anchor Field. The assumption in this example here is that you’re going to be spawning your actor with your MyGeometryCollectionComponent in the same location as your anchor field.

So according to this…

and the order in which the following functions are called…

it seems that the anchor field has to be fully initialized in the construction script (or prior). OnConstruction is too late, and UserConstructionScript is not a virtual function. Therefore, we’ll use PostActorCreated.

//
// MyAnchor.h
//
#include "CoreMinimal.h"
#include "Field/FieldSystemActor.h"

#include "MyAnchor.generated.h"

class UBoxComponent;
class UBoxFalloff;
class UCullingField;
class UUniformInteger;

UCLASS()
class MY_GAME_API AMyAnchor : public AFieldSystemActor
{
	GENERATED_BODY()

protected:

	UPROPERTY()
	USceneComponent* SceneComp;

	UPROPERTY(EditDefaultsOnly)
	UBoxComponent* BoxComp;

	UPROPERTY()
	UCullingField* CullingFieldComp;

	UPROPERTY()
	UBoxFalloff* BoxFalloffComp;

	UPROPERTY()
	UUniformInteger* UniformIntegerComp;

public:

	AMyAnchor();

protected:

	virtual void PostActorCreated() override;

public:

	void DynamicInit();
};
//
// MyAnchor.cpp
//
#include "put-the-path-to-MyAnchor.h here"
#include "Components/BoxComponent.h"
#include "Field/FieldSystemComponent.h"
#include "Field/FieldSystemObjects.h"

AMyAnchor::AMyAnchor()
{
	SceneComp = CreateDefaultSubobject<USceneComponent>(FName("Scene Component"));
	SetRootComponent(SceneComp);

	FieldSystemComponent->SetupAttachment(SceneComp);

	BoxComp = CreateDefaultSubobject<UBoxComponent>(FName("Box Component"));
	BoxComp->SetupAttachment(SceneComp);

	BoxFalloffComp = CreateDefaultSubobject<UBoxFalloff>(FName("Box Falloff Component"));
	
	UniformIntegerComp = CreateDefaultSubobject<UUniformInteger>(FName("Uniform Integer Component"));
	
	CullingFieldComp = CreateDefaultSubobject<UCullingField>(FName("Culling Field Component"));	
}

void AMyAnchor::PostActorCreated()
{
	Super::PostActorCreated();

	DynamicInit();
}

void AMyAnchor::DynamicInit()
{
	BoxFalloffComp->SetBoxFalloff(1.0f, 0.0f, 1.0f, 0.0f, BoxComp->K2_GetComponentToWorld(), EFieldFalloffType::Field_FallOff_None);
	UniformIntegerComp->SetUniformInteger(3);
	CullingFieldComp->SetCullingField(BoxFalloffComp, UniformIntegerComp, EFieldCullingOperationType::Field_Culling_Outside);
	FieldSystemComponent->AddFieldCommand(true, EFieldPhysicsType::Field_DynamicState, nullptr, CullingFieldComp);
	UE_LOG(LogTemp, Warning, TEXT("DynamicInit()"));
}

Now all you have to do is dynamically spawn the Anchor Field Actor FIRST and then your GeometryCollection Actor afterwards and it should work.

Cheers!

1 Like

Thank you for this buddy, code compiled fine but at the end getting confilcts with Engine code i believe (unresolved external symbol). I did set my own API in settings, any idea? Thank you

Hmmm, did you add the following modules to your project?

image

There might be more that need to be added, but you could probably look them up in the API.

1 Like

thanks will test, did add physicsCore but nor fieldsystem, thank you buddy.