PlacementObjectRegistry
The PlacementObjectRegistry is a component that manages the central database of all placeable objects in your Grid Placement System. It provides flexible configuration options, runtime management, and seamless integration with UI systems.
Overview
This component acts as the single source of truth for all objects that can be placed in your game.
It supports both design-time configuration (through DataTables) and runtime registration (through PlacementHelper), making it perfect for both static object libraries and dynamic content systems.
Key Features
- ✅ Flexible Configuration - DataTables or runtime registration via PlacementHelper
- ✅ Category Organization - Group objects by type, theme, or functionality
- ✅ Event Integration - React to registry changes and placement triggers
- ✅ UI Data Provider - Direct integration with placement UI widgets
- ✅ Auto-Connection - Automatically finds and connects to PlacementSystemComponent
- ✅ Runtime Management - Add/remove objects dynamically during gameplay
- ✅ Multiple DataTable Support - Load from multiple DataTable assets
Setup
Component Installation
- Add
PlacementObjectRegistry
component to your Player Pawn - Add
PlacementSystemComponent
to the same pawn (for auto-connection) - Configure your preferred registration method in the Details panel
Auto-Connection
The registry automatically connects to the placement system:
// Enable automatic connection (default: true)
bAutoConnectToPlacementSystem = true
Configuration Methods
The registry supports two primary configuration approaches:
Method Priority:
- PlacementHelper Registration - Recommended for most projects
- DataTables - Best for large datasets and designer workflows
Note: Methods can be combined. PlacementHelper adds to whatever is configured in the DataTables.
Method 1: PlacementHelper Registration (Recommended)
Use PlacementHelper::RegisterObject()
for the easiest and most flexible setup.
Usage:
// In your GameMode or PlayerPawn BeginPlay
void AMyGameMode::RegisterAllObjects()
{
// Initialize the placement system first
UPlacementHelper::InitializePlacementSystem(GetWorld()->GetFirstPlayerController()->GetPawn());
// Furniture
UPlacementHelper::RegisterObject(ChairClass, "Office Chair", ChairIcon, "Ergonomic seating", "Furniture");
UPlacementHelper::RegisterObject(DeskClass, "Work Desk", DeskIcon, "Spacious workspace", "Furniture");
UPlacementHelper::RegisterObject(BookshelfClass, "Bookshelf", BookshelfIcon, "Storage for books", "Furniture");
// Lighting
UPlacementHelper::RegisterObject(DeskLampClass, "Desk Lamp", LampIcon, "Adjustable LED lamp", "Lighting");
UPlacementHelper::RegisterObject(CeilingFanClass, "Ceiling Fan", FanIcon, "Cooling and air circulation", "Lighting");
// Decorations
UPlacementHelper::RegisterObject(PlantClass, "Office Plant", PlantIcon, "Green decoration", "Decorations");
UPlacementHelper::RegisterObject(PictureClass, "Wall Picture", PictureIcon, "Decorative artwork", "Decorations");
}
Benefits:
- Simplest implementation - just function calls
- No editor configuration required
- Perfect for conditional registration
- Easy to organize with helper functions
- Integrates seamlessly with PlacementHelper workflow
Method 2: DataTables Configuration
Use DataTables for very large object libraries (50+ objects) or when non-programmers need to manage the object database.

Property | Type | Description |
---|---|---|
Object Data Tables | Array<UDataTable*> | Multiple DataTables containing FPlaceableObjectDataTable rows |
Setup:
- Create DataTable assets with
FPlaceableObjectDataTable
as the row structure - Add rows for each placeable object
- Add the DataTables to the Object Data Tables array
Benefits:
- Excellent for very large datasets (100+ objects)
- Can be edited by designers/artists without code
- Supports multiple DataTables for organization
- Automatic loading on BeginPlay
- Shareable across multiple projects
When to use: Large object libraries, designer-managed content,
or when you need multiple DataTables for organization.
Configuration Properties
DataTable Registration Settings
Property | Type | Default | Description |
---|---|---|---|
Object Data Tables | Array<UDataTable*> | Empty | Configure objects using multiple DataTable assets (FPlaceableObjectDataTable structure) |
Auto Setup Settings
Property | Type | Default | Description |
---|---|---|---|
Auto Connect To Placement System | Boolean | true | Automatically find and connect to PlacementSystemComponent on the same actor |
Runtime State (Read-Only)
Property | Type | Description |
---|---|---|
Registered Objects | Array<FPlaceableObjectData> | The actual object database used by the system (populated from DataTables and runtime registration) |
Management Functions
RemoveObject
UFUNCTION(BlueprintCallable, Category = "GPS Management")
static bool RemoveObject(TSubclassOf<AActor> ActorClass);
Removes an object from the registry.
RemoveObjectsByCategory
UFUNCTION(BlueprintCallable, Category = "GPS Management")
static int32 RemoveObjectsByCategory(const FString& Category);
Removes all objects from a specific category.
ClearAllObjects
UFUNCTION(BlueprintCallable, Category = "GPS Management")
static void ClearAllObjects();
Removes all objects from the registry.
Data Access Functions
GetRegisteredObjects
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GPS Data")
static TArray<FPlaceableObjectData> GetRegisteredObjects();
Returns all registered objects. Perfect for UI population.
GetAllCategories
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GPS Data")
static TArray<FString> GetAllCategories();
Returns all unique category names from registered objects.
GetObjectCount
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GPS Data")
static int32 GetObjectCount();
Returns the total number of registered objects.
IsObjectRegistered
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GPS Data")
static bool IsObjectRegistered(TSubclassOf<AActor> ActorClass);
Checks if a specific object class is registered.
Component API (Advanced Use Cases)
For direct component access when you need component-specific functionality:
Core Management Functions
AddObject
void AddObject(
TSubclassOf<AActor> ActorClass,
const FString& DisplayName,
UTexture2D* Icon = nullptr,
const FString& Description = TEXT(""),
const FString& Category = TEXT("General")
);
Adds an object directly to the registry component.
RemoveObject
bool RemoveObject(TSubclassOf<AActor> ActorClass);
Removes an object from the registry.
RemoveObjectsByCategory
int32 RemoveObjectsByCategory(const FString& Category);
Removes all objects from a specific category.
State Check Functions
IsObjectRegistered
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Registry Data")
bool IsObjectRegistered(TSubclassOf<AActor> ActorClass) const;
Checks if a specific object class is registered.
Data Access Functions
GetAllObjects
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Registry Data")
TArray<FPlaceableObjectData> GetAllObjects() const;
Returns all registered objects.
GetAllCategories
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Registry Data")
TArray<FString> GetAllCategories() const;
Returns all unique category names.
GetObjectCount
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Registry Data")
int32 GetObjectCount() const;
Returns the total number of registered objects.
Connection Functions
ConnectToPlacementSystem
void ConnectToPlacementSystem(UPlacementSystemComponent* PlacementSystem);
Manually connect to a specific PlacementSystemComponent (usually automatic).
Events System
OnObjectsRegistryChanged
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnObjectsRegistryChanged OnObjectsRegistryChanged;
Fired whenever the number of registered objects changes.
Parameters:
int32 TotalObjects
- New total count of objects
Usage:
// Get registry component and bind to events
if (APawn* PlayerPawn = GetWorld()->GetFirstPlayerController()->GetPawn())
{
if (UPlacementObjectRegistry* Registry = PlayerPawn->FindComponentByClass<UPlacementObjectRegistry>())
{
Registry->OnObjectsRegistryChanged.AddDynamic(this, &UMyWidget::OnRegistryChanged);
}
}
void UMyWidget::OnRegistryChanged(int32 TotalObjects)
{
ObjectCountLabel->SetText(FText::AsNumber(TotalObjects));
RefreshObjectList();
}
Data Structures
FPlaceableObjectData
USTRUCT(BlueprintType)
struct FPlaceableObjectData
{
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> ActorClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UTexture2D* Icon;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Description;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Category;
};
FPlaceableObjectDataTable
USTRUCT(BlueprintType)
struct FPlaceableObjectDataTable : public FTableRowBase
{
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> ActorClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UTexture2D* Icon;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Description;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Category;
};
The DataTable version that automatically converts to FPlaceableObjectData
when loaded.
Usage Patterns
Static Object Library (Recommended)
// Use PlacementHelper for clean, organized registration
void AMyGameMode::SetupPlacementObjects()
{
// Initialize the system first
UPlacementHelper::InitializePlacementSystem(GetWorld()->GetFirstPlayerController()->GetPawn());
// Register objects by category
RegisterFurniture();
RegisterLighting();
RegisterDecorations();
}
void AMyGameMode::RegisterFurniture()
{
UPlacementHelper::RegisterObject(ChairClass, "Office Chair", ChairIcon, "Comfortable seating", "Furniture");
UPlacementHelper::RegisterObject(DeskClass, "Work Desk", DeskIcon, "Spacious workspace", "Furniture");
UPlacementHelper::RegisterObject(BookshelfClass, "Bookshelf", BookshelfIcon, "Storage for books", "Furniture");
}
void AMyGameMode::RegisterLighting()
{
UPlacementHelper::RegisterObject(DeskLampClass, "Desk Lamp", LampIcon, "Adjustable LED lamp", "Lighting");
UPlacementHelper::RegisterObject(CeilingFanClass, "Ceiling Fan", FanIcon, "Cooling and air circulation", "Lighting");
}
void AMyGameMode::RegisterDecorations()
{
UPlacementHelper::RegisterObject(PlantClass, "Office Plant", PlantIcon, "Green decoration", "Decorations");
UPlacementHelper::RegisterObject(PictureClass, "Wall Picture", PictureIcon, "Decorative artwork", "Decorations");
}
Dynamic Content System
void AContentManager::LoadFurniturePack()
{
// Load object classes from asset bundle
TArray<TSubclassOf<AActor>> FurnitureClasses = LoadFurnitureAssets();
// Use PlacementHelper for dynamic registration
for (TSubclassOf<AActor> FurnitureClass : FurnitureClasses)
{
FString Name = GetLocalizedName(FurnitureClass);
UTexture2D* Icon = GetObjectIcon(FurnitureClass);
UPlacementHelper::RegisterObject(FurnitureClass, Name, Icon, "", "DLC Furniture");
}
}
void AContentManager::UnloadFurniturePack()
{
// Remove DLC objects by category
UPlacementHelper::RemoveObjectsByCategory("DLC Furniture");
}
Category-Based UI System
void UBuildingWidget::CreateCategoryTabs()
{
// Use PlacementHelper for easy access
TArray<FString> Categories = UPlacementHelper::GetAllCategories();
for (const FString& Category : Categories)
{
UButton* CategoryTab = CreateWidget<UButton>(this, CategoryTabClass);
CategoryTab->GetChildAt(0)->SetText(FText::FromString(Category));
CategoryTab->OnClicked.AddDynamic(this, &UBuildingWidget::ShowCategory);
CategoryContainer->AddChild(CategoryTab);
}
}
void UBuildingWidget::ShowCategory(const FString& CategoryName)
{
ObjectGrid->ClearChildren();
// Use PlacementHelper for data access
TArray<FPlaceableObjectData> AllObjects = UPlacementHelper::GetRegisteredObjects();
// Filter by category
for (const FPlaceableObjectData& ObjectData : AllObjects)
{
if (ObjectData.Category == CategoryName)
{
UObjectButton* ObjectButton = CreateWidget<UObjectButton>(this, ObjectButtonClass);
ObjectButton->Setup(ObjectData);
ObjectButton->OnObjectClicked.AddDynamic(this, &UBuildingWidget::OnObjectSelected);
ObjectGrid->AddChild(ObjectButton);
}
}
}
void UBuildingWidget::OnObjectSelected(TSubclassOf<AActor> ActorClass)
{
// Use PlacementHelper for placement
UPlacementHelper::StartPlacing(ActorClass);
SetVisibility(ESlateVisibility::Hidden);
}
DataTable Configuration
Creating Multiple PlaceableObjects DataTables
-
Create DataTable Assets:
- Right-click in Content Browser → "Miscellaneous" → "Data Table"
- Select
FPlaceableObjectDataTable
as Row Structure - Create separate tables for organization:
- "DT_Furniture" - for furniture objects
- "DT_Lighting" - for lighting objects
- "DT_Decorations" - for decorative objects
-
Configure DataTable Rows:
DT_Furniture: Row Name: "chair_office" ActorClass: /Game/Furniture/BP_OfficeChair.BP_OfficeChair_C DisplayName: "Office Chair" Icon: /Game/UI/Icons/T_ChairIcon.T_ChairIcon Description: "Ergonomic office seating with adjustable height" Category: "Office Furniture" DT_Lighting: Row Name: "desk_lamp" ActorClass: /Game/Lighting/BP_DeskLamp.BP_DeskLamp_C DisplayName: "Desk Lamp" Icon: /Game/UI/Icons/T_LampIcon.T_LampIcon Description: "Adjustable LED desk lighting" Category: "Lighting"
-
Assign to Registry:
- Add all DataTables to the "Object Data Tables" array
- The registry will automatically load all objects from all tables on BeginPlay
DataTable Loading Process
The registry automatically:
- Loads all rows from all assigned DataTables on
BeginPlay
- Converts
FPlaceableObjectDataTable
toFPlaceableObjectData
- Checks for duplicate ActorClasses and skips them
- Fires
OnObjectsRegistryChanged
event when loading is complete
Performance Considerations
Optimization Tips
Use Multiple Small DataTables:
// Better: Organize by logical groups
Object Data Tables:
- DT_CoreFurniture (20 objects)
- DT_ElectronicDevices (15 objects)
- DT_Decorations (25 objects)
// Instead of: One massive table
- DT_AllObjects (500+ objects)
Error Handling and Validation
Registry Validation
bool ValidateRegistry()
{
TArray<FPlaceableObjectData> Objects = UPlacementHelper::GetRegisteredObjects();
for (const FPlaceableObjectData& ObjectData : Objects)
{
if (!ObjectData.ActorClass)
{
UE_LOG(LogTemp, Error, TEXT("Object '%s' has null ActorClass"), *ObjectData.DisplayName);
return false;
}
if (!ObjectData.ActorClass->ImplementsInterface(UPlaceableObjectInterface::StaticClass()))
{
UE_LOG(LogTemp, Error, TEXT("Object '%s' doesn't implement IPlaceableObjectInterface"), *ObjectData.DisplayName);
return false;
}
}
return true;
}
Safe Registration
void SafeRegisterObject(TSubclassOf<AActor> ActorClass, const FString& DisplayName)
{
if (!ActorClass)
{
UE_LOG(LogTemp, Warning, TEXT("Cannot register null ActorClass"));
return;
}
// Check if already registered
if (UPlacementHelper::IsObjectRegistered(ActorClass))
{
UE_LOG(LogTemp, Warning, TEXT("ActorClass already registered: %s"), *DisplayName);
return;
}
// Register using PlacementHelper
UPlacementHelper::RegisterObject(ActorClass, DisplayName);
}
Troubleshooting
Common Issues
"No objects showing in UI"
- Ensure
UPlacementHelper::InitializePlacementSystem()
was called first - Check that objects are registered via PlacementHelper or DataTables are assigned
- Verify
UPlacementHelper::GetRegisteredObjects()
returns data - Ensure UI is calling methods after BeginPlay
"Objects not placing"
- Verify objects implement
IPlaceableObjectInterface
- Check that
UPlacementHelper::StartPlacing()
returnstrue
- Ensure PlacementSystemComponent is on the same actor
"Auto-connection failed"
- Verify both PlacementObjectRegistry and PlacementSystemComponent are on the same Actor
- Check that
bAutoConnectToPlacementSystem
is true - Try calling
UPlacementHelper::InitializePlacementSystem()
manually
"DataTables not loading"
- Verify DataTable row structure is
FPlaceableObjectDataTable
- Check for null ActorClass entries in DataTable rows
- Ensure DataTables are assigned to "Object Data Tables" array
- Look for error logs about duplicate ActorClasses
"Categories not showing correctly"
- Check for typos in category names (case-sensitive)
- Use
UPlacementHelper::GetAllCategories()
to debug available categories - Verify objects actually have category assignments
Debug Commands
// Registry debugging using PlacementHelper
void DebugRegistry()
{
UE_LOG(LogTemp, Log, TEXT("=== Registry Debug ==="));
UE_LOG(LogTemp, Log, TEXT("Total Objects: %d"), UPlacementHelper::GetObjectCount());
TArray<FString> Categories = UPlacementHelper::GetAllCategories();
UE_LOG(LogTemp, Log, TEXT("Categories: %d"), Categories.Num());
for (const FString& Category : Categories)
{
int32 CategoryCount = 0;
TArray<FPlaceableObjectData> AllObjects = UPlacementHelper::GetRegisteredObjects();
for (const FPlaceableObjectData& ObjectData : AllObjects)
{
if (ObjectData.Category == Category)
{
CategoryCount++;
}
}
UE_LOG(LogTemp, Log, TEXT(" %s: %d objects"), *Category, CategoryCount);
}
}
The PlacementObjectRegistry provides a robust foundation for managing your placeable objects with flexibility for both simple static configurations through PlacementHelper and complex DataTable-based content systems.