Fixes for placeable objects and inventory tabs

This commit is contained in:
2026-09-11 21:46:30 +02:00
parent a6c6ddcc63
commit d72a9c957a
61 changed files with 381 additions and 315 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -11,6 +11,7 @@
#include "../Public/EleriPlayerController.h" #include "../Public/EleriPlayerController.h"
#include "../Public/MyCharacter.h" #include "../Public/MyCharacter.h"
#include "Engine/OverlapResult.h" #include "Engine/OverlapResult.h"
#include "LandscapeProxy.h"
// Sets default values for this component's properties // Sets default values for this component's properties
UMoveActorsComponent::UMoveActorsComponent() UMoveActorsComponent::UMoveActorsComponent()
@@ -18,8 +19,6 @@ UMoveActorsComponent::UMoveActorsComponent()
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them. // off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.bCanEverTick = true;
// ...
} }
@@ -31,13 +30,19 @@ void UMoveActorsComponent::BeginPlay()
PlayerController = Cast<AEleriPlayerController>(UGameplayStatics::GetPlayerController(GetWorld(), 0)); PlayerController = Cast<AEleriPlayerController>(UGameplayStatics::GetPlayerController(GetWorld(), 0));
PlayerCharacter = Cast<AMyCharacter>(GetOwner()); PlayerCharacter = Cast<AMyCharacter>(GetOwner());
if (!PlayerCharacter)
{
return;
}
TArray<AActor*> OutActors; TArray<AActor*> OutActors;
PlayerCharacter->GetOverlappingActors(OutActors); PlayerCharacter->GetOverlappingActors(OutActors);
for (auto Actor : OutActors) for (const AActor* Actor : OutActors)
{ {
if(PlantableAreaActorClass == Actor->GetClass()) if (Actor && PlantableAreaActorClass == Actor->GetClass())
{ {
bRemovalModePossible = true; bRemovalModePossible = true;
break;
} }
} }
} }
@@ -48,115 +53,122 @@ void UMoveActorsComponent::TickComponent(float DeltaTime, ELevelTick TickType, F
{ {
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (!PlayerCharacter || !MovingActor) return; if (!PlayerCharacter || !IsValid(MovingActor) || MovingActor->bPlaced) return;
if (!MovingActor->bPlaced) FVector ForwardVec = PlayerCharacter->GetActorForwardVector();
if (PlayerController && PlayerController->PlayerCameraManager)
{ {
FVector ForwardVec; ForwardVec = PlayerController->PlayerCameraManager->GetCameraRotation().Vector();
if (APlayerCameraManager* CameraManager = UGameplayStatics::GetPlayerCameraManager(GetWorld(), 0)) ForwardVec.Z = 0.f;
{
ForwardVec = CameraManager->GetCameraRotation().Vector();
ForwardVec.Z = 0;
} }
else
{
ForwardVec = PlayerCharacter->GetActorForwardVector();
}
//FVector PosVector = UKismetMathLibrary::Vector_SnappedToGrid(PlayerCharacter->GetActorLocation() + ((ForwardVec * MovingActor->DistanceFromActor) + FVector(0.f, 0.f, 100.f)), MovingActor->GridScale);
FVector PosVector = PlayerCharacter->GetActorLocation() + ((ForwardVec * 800.f) + FVector(0.f, 0.f, 100.f)); // Prefer the per-actor tuning values, falling back to the component defaults.
const float Distance = MovingActor->DistanceFromActor > 0.f ? MovingActor->DistanceFromActor : PlacementDistance;
const float GridSnap = MovingActor->GridScale > 0.f ? MovingActor->GridScale : GridSnapSize;
FVector PosVector = PlayerCharacter->GetActorLocation() + (ForwardVec * Distance) + FVector(0.f, 0.f, PlacementHeightOffset);
if (bGridMovementActive) if (bGridMovementActive)
{ {
PosVector = UKismetMathLibrary::Vector_SnappedToGrid(PosVector, 250.f); PosVector = UKismetMathLibrary::Vector_SnappedToGrid(PosVector, GridSnap);
} }
FTransform NewTransform = FTransform(MovingActor->GetActorRotation(), PosVector, (MovingActor->ScaleOverride != FVector::ZeroVector) ? MovingActor->ScaleOverride : MovingActor->GetActorScale()); const FVector TargetScale = (MovingActor->ScaleOverride != FVector::ZeroVector) ? MovingActor->ScaleOverride : MovingActor->GetActorScale();
MovingActor->SetActorTransform(NewTransform); MovingActor->SetActorTransform(FTransform(MovingActor->GetActorRotation(), PosVector, TargetScale));
// Snap the object down onto the ground beneath it.
TArray<FHitResult> OutHits;
FCollisionObjectQueryParams QueryParams; FCollisionObjectQueryParams QueryParams;
QueryParams.AddObjectTypesToQuery(ECollisionChannel::ECC_WorldStatic); QueryParams.AddObjectTypesToQuery(ECC_WorldStatic);
FVector TraceEnd = (MovingActor->GetActorLocation() + FVector(0.f, 0.f, -1000.f));
const FVector TraceEnd = MovingActor->GetActorLocation() + FVector(0.f, 0.f, -GroundTraceLength);
TArray<FHitResult> OutHits;
GetWorld()->LineTraceMultiByObjectType(OutHits, MovingActor->GetActorLocation(), TraceEnd, QueryParams); GetWorld()->LineTraceMultiByObjectType(OutHits, MovingActor->GetActorLocation(), TraceEnd, QueryParams);
MovingActor->bPlaceable = true; MovingActor->bPlaceable = true;
MovingActor->IntersectingActors.Empty(); MovingActor->IntersectingActors.Empty();
for (const FHitResult& HitResult : OutHits) for (const FHitResult& HitResult : OutHits)
{ {
if (HitResult.GetActor() == MovingActor) continue; AActor* HitActor = HitResult.GetActor();
if (!HitActor || HitActor == MovingActor) continue;
MovingActor->IntersectingActors.Add(HitResult.GetActor()); MovingActor->IntersectingActors.Add(HitActor);
if (HitResult.GetActor()->GetClass()->GetName() == FString("Landscape")) if (HitActor->IsA(ALandscapeProxy::StaticClass()))
{ {
MovingActor->SetActorLocation(HitResult.ImpactPoint); MovingActor->SetActorLocation(HitResult.ImpactPoint);
} }
} }
//Check if moving object is overlapping // Check whether the object overlaps anything that should block placement.
TArray<AActor*> OverlappingActors;
FVector Center, BoxExtent; FVector Center, BoxExtent;
MovingActor->GetActorBounds(false, Center, BoxExtent); MovingActor->GetActorBounds(false, Center, BoxExtent);
BoxExtent *= 0.9f; BoxExtent *= OverlapBoxExtentMultiplier;
if (!MovingActor->BoxExtentOverride.IsNearlyZero()) if (!MovingActor->BoxExtentOverride.IsNearlyZero())
{ {
BoxExtent = MovingActor->BoxExtentOverride; BoxExtent = MovingActor->BoxExtentOverride;
} }
TArray<FOverlapResult> OverlapResults; FCollisionQueryParams OverlapQueryParams;
FCollisionQueryParams QueryParams1; OverlapQueryParams.bTraceComplex = true;
QueryParams1.bTraceComplex = true; OverlapQueryParams.AddIgnoredActor(MovingActor);
QueryParams1.AddIgnoredActor(MovingActor); OverlapQueryParams.AddIgnoredActor(PlayerCharacter);
QueryParams1.AddIgnoredActor(PlayerCharacter);
FCollisionObjectQueryParams ObjectQueryParams; FCollisionObjectQueryParams ObjectQueryParams;
ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldStatic); ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldStatic);
ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldDynamic); ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldDynamic);
ObjectQueryParams.AddObjectTypesToQuery(ECC_GameTraceChannel1); ObjectQueryParams.AddObjectTypesToQuery(ECC_GameTraceChannel1);
ObjectQueryParams.AddObjectTypesToQuery(ECC_GameTraceChannel2); ObjectQueryParams.AddObjectTypesToQuery(ECC_GameTraceChannel2);
bool bHit = GetWorld()->OverlapMultiByObjectType( TArray<FOverlapResult> OverlapResults;
GetWorld()->OverlapMultiByObjectType(
OverlapResults, OverlapResults,
MovingActor->GetActorLocation(), MovingActor->GetActorLocation(),
FQuat::Identity, MovingActor->GetActorQuat(),
ObjectQueryParams, ObjectQueryParams,
FCollisionShape::MakeBox(BoxExtent), // Or MakeSphere, MakeCapsule FCollisionShape::MakeBox(BoxExtent),
QueryParams1 OverlapQueryParams
); );
bool PlantableAreaFound = false;
for (const FOverlapResult& OverlapResult : OverlapResults) for (const FOverlapResult& OverlapResult : OverlapResults)
{ {
if (OverlapResult.GetActor()) AActor* OverlappingActor = OverlapResult.GetActor();
OverlappingActors.Add(OverlapResult.GetActor()); if (!OverlappingActor) continue;
}
//DrawDebugBox(GetWorld(), MovingActor->GetActorLocation(), BoxExtent, FColor::Red); if (PlantableAreaActorClass == OverlappingActor->GetClass())
bool PlantableAreaFound = false;
for (auto OverlappingActor : OverlappingActors)
{
if (OverlappingActor->GetClass() == PlantableAreaActorClass)
{ {
PlantableAreaFound = true; PlantableAreaFound = true;
} }
if (OverlappingActor->GetClass()->GetName() == FString("Landscape") || // Landscape and non-static-mesh actors don't block placement.
!OverlappingActor->GetClass()->IsChildOf(AStaticMeshActor::StaticClass())) continue; if (OverlappingActor->IsA(ALandscapeProxy::StaticClass()) || !OverlappingActor->GetClass()->IsChildOf(AStaticMeshActor::StaticClass()))
{
continue;
}
if (!ActorsToIgnore.FindByPredicate([&OverlappingActor](const TSubclassOf<AActor>& ActorClass) { return OverlappingActor->GetClass() == ActorClass; })) const bool bIsIgnored = ActorsToIgnore.ContainsByPredicate([&OverlappingActor](const TSubclassOf<AActor>& ActorClass)
{
return OverlappingActor->GetClass() == ActorClass;
});
if (!bIsIgnored)
{ {
MovingActor->bPlaceable = false; MovingActor->bPlaceable = false;
break; break;
} }
} }
if(!PlantableAreaFound)
MovingActor->bPlaceable = false;
if (MovingActor) if (!PlantableAreaFound)
{ {
MovingActor->GetStaticMeshComponent()->SetOverlayMaterial((!MovingActor->bPlaceable) ? MovingActor->CantPlaceMaterial : MovingActor->CanPlaceMaterial); MovingActor->bPlaceable = false;
}
if (UStaticMeshComponent* MeshComponent = MovingActor->GetStaticMeshComponent())
{
MeshComponent->SetOverlayMaterial((!MovingActor->bPlaceable) ? MovingActor->CantPlaceMaterial : MovingActor->CanPlaceMaterial);
}
MovingActor->K2_TogglePlaceableEffects(MovingActor->bPlaceable); MovingActor->K2_TogglePlaceableEffects(MovingActor->bPlaceable);
} }
}
}
void UMoveActorsComponent::ToggleGridMovement() void UMoveActorsComponent::ToggleGridMovement()
{ {
@@ -172,20 +184,21 @@ bool UMoveActorsComponent::PlaceableActionUsed(ERemovalAction ActionUsed)
switch (ActionUsed) switch (ActionUsed)
{ {
case ERemovalAction::NONE: case ERemovalAction::NONE:
case ERemovalAction::MAX:
default:
break; break;
case ERemovalAction::START_MOVE: case ERemovalAction::START_MOVE:
if (AEleriPlayerController* PC = Cast<AEleriPlayerController>(UGameplayStatics::GetPlayerController(GetWorld(), 0))) return PickUpObjectForPlacing(HighligtedMovingActor);
{
PickUpObjectForPlacing(HighligtedMovingActor);
return true;
}
break;
case ERemovalAction::PLACE: case ERemovalAction::PLACE:
if (PlayerController && MovingActor && MovingActor->IsPlaceable()) if (PlayerController && MovingActor->IsPlaceable())
{ {
FPrimaryAssetId ItemId = MovingActor->ItemStack.ItemId; FPrimaryAssetId ItemId = MovingActor->ItemStack.ItemId;
if (!ItemId.IsValid()) if (!ItemId.IsValid())
{
ItemId = MovingActor->DataAssetId; ItemId = MovingActor->DataAssetId;
}
if (ItemId.IsValid()) if (ItemId.IsValid())
{ {
if (UInventoryComponent* Inventory = UMainBlueprintFunctionLibrary::GetPlayerInventory(GetWorld())) if (UInventoryComponent* Inventory = UMainBlueprintFunctionLibrary::GetPlayerInventory(GetWorld()))
@@ -197,15 +210,10 @@ bool UMoveActorsComponent::PlaceableActionUsed(ERemovalAction ActionUsed)
return true; return true;
} }
break; break;
//Will place it in inventory
case ERemovalAction::REMOVE: case ERemovalAction::REMOVE:
case ERemovalAction::EXIT:
if (UGameInstance* GI = UGameplayStatics::GetGameInstance(GetWorld()))
{
if (UObjectPersistenceSubsystem* ObjectPersistenceSubsystem = GI->GetSubsystem<UObjectPersistenceSubsystem>())
{
if (MovingActor)
{ {
// A world object is returned to the player's inventory before it is removed.
if (MovingActor->DataAssetId.IsValid() && !MovingActor->bFromInventory) if (MovingActor->DataAssetId.IsValid() && !MovingActor->bFromInventory)
{ {
if (UInventoryComponent* Inventory = UMainBlueprintFunctionLibrary::GetPlayerInventory(GetWorld())) if (UInventoryComponent* Inventory = UMainBlueprintFunctionLibrary::GetPlayerInventory(GetWorld()))
@@ -213,36 +221,39 @@ bool UMoveActorsComponent::PlaceableActionUsed(ERemovalAction ActionUsed)
Inventory->AddItemToInventory(MovingActor->DataAssetId, 1, false, false); Inventory->AddItemToInventory(MovingActor->DataAssetId, 1, false, false);
} }
} }
}
if (UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(GetWorld()))
{
if (UObjectPersistenceSubsystem* ObjectPersistenceSubsystem = GameInstance->GetSubsystem<UObjectPersistenceSubsystem>())
{
ObjectPersistenceSubsystem->RemoveActorFromLevel(MovingActor); ObjectPersistenceSubsystem->RemoveActorFromLevel(MovingActor);
return true; return true;
} }
} }
break;
/*if (MovingActor->InventoryRef) // Fallback if the persistence subsystem is unavailable.
MovingActor->Destroy();
return true;
}
case ERemovalAction::EXIT:
{ {
PlaceableActionUsed(ERemovalAction::REMOVE); // Cancel the current placement/move without committing it.
if (MovingActor->bFromInventory)
{
// A preview spawned from the inventory: return the item and destroy the preview.
MovingActor->ReturnToInventory();
MovingActor->Destroy();
} }
else else
{ {
// An existing world object: put it back where it was picked up from.
MovingActor->SetActorTransform(MovingActor->CurrentMemorizedTransform); MovingActor->SetActorTransform(MovingActor->CurrentMemorizedTransform);
for (int32 i = 0; i < MovingActor->MeshMaterials.Num(); i++)
{
MovingActor->GetStaticMeshComponent()->SetMaterial(i, MovingActor->MeshMaterials[i]);
}
MovingActor->GetStaticMeshComponent()->SetOverlayMaterial(nullptr);
if (PlayerController)
{
PlaceObjectForPlacing();
} }
return true; return true;
}*/
break;
case ERemovalAction::MAX:
break;
default:
break;
} }
}
return false; return false;
} }
@@ -253,7 +264,7 @@ void UMoveActorsComponent::ToggleRemovalModeOption(bool bNewRemoveModePossible)
bool UMoveActorsComponent::PickUpObjectForPlacing(ARemovableStaticMeshActor* Placable) bool UMoveActorsComponent::PickUpObjectForPlacing(ARemovableStaticMeshActor* Placable)
{ {
if (MovingActor || !Placable || !PlayerController) return false; if (MovingActor || !IsValid(Placable) || !PlayerController) return false;
// Get the Enhanced Input Local Player Subsystem from the Local Player related to our Player Controller. // Get the Enhanced Input Local Player Subsystem from the Local Player related to our Player Controller.
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer())) if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
@@ -268,16 +279,14 @@ bool UMoveActorsComponent::PickUpObjectForPlacing(ARemovableStaticMeshActor* Pla
bool UMoveActorsComponent::PlaceObjectForPlacing() bool UMoveActorsComponent::PlaceObjectForPlacing()
{ {
if (!MovingActor) return false; if (!IsValid(MovingActor)) return false;
if (MovingActor) if (UStaticMeshComponent* MeshComponent = MovingActor->GetStaticMeshComponent())
{ {
if (MovingActor->GetStaticMeshComponent()) MeshComponent->SetOverlayMaterial(nullptr);
{
MovingActor->GetStaticMeshComponent()->SetOverlayMaterial(nullptr);
} }
MovingActor->Place(); MovingActor->Place();
}
RemovalModePlacingStack--; RemovalModePlacingStack--;
return RemoveObjectForPlacing(); return RemoveObjectForPlacing();
} }
@@ -290,8 +299,10 @@ bool UMoveActorsComponent::RemoveObjectForPlacing()
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer())) if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{ {
if (RemovalModePlacingStack <= 0) if (RemovalModePlacingStack <= 0)
{
Subsystem->RemoveMappingContext(PlayerController->RemoveModeContext); Subsystem->RemoveMappingContext(PlayerController->RemoveModeContext);
} }
}
MovingActor = nullptr; MovingActor = nullptr;
return true; return true;
@@ -300,9 +311,11 @@ bool UMoveActorsComponent::RemoveObjectForPlacing()
void UMoveActorsComponent::StartObjectRemoval(bool bStart, int32 Stack) void UMoveActorsComponent::StartObjectRemoval(bool bStart, int32 Stack)
{ {
if (!PlayerCharacter) return;
if (!bStart) if (!bStart)
{ {
PlayerCharacter->bAlreadyInteracting = bStart; PlayerCharacter->bAlreadyInteracting = false;
} }
PlayerCharacter->CurrentlyDoing = bStart ? ECurrentlyDoing::OBJECT_PLACING : ECurrentlyDoing::NOTHING; PlayerCharacter->CurrentlyDoing = bStart ? ECurrentlyDoing::OBJECT_PLACING : ECurrentlyDoing::NOTHING;
@@ -312,11 +325,10 @@ void UMoveActorsComponent::StartObjectRemoval(bool bStart, int32 Stack)
if (!bStart) if (!bStart)
{ {
PlayerCharacter->ZoomBack(0.5f); PlayerCharacter->ZoomBack(0.5f);
} }
else if (bStart) else
{ {
PlayerCharacter->ZoomCamera(-500.f, 0.5f); PlayerCharacter->ZoomCamera(500.f, 0.5f);
} }
bRemovalMode = bStart; bRemovalMode = bStart;
@@ -326,10 +338,14 @@ void UMoveActorsComponent::StartObjectRemoval(bool bStart, int32 Stack)
bStart ? PlayerCharacter->CameraPitchMin - 50 : PlayerCharacter->CameraPitchMin, bStart ? PlayerCharacter->CameraPitchMin - 50 : PlayerCharacter->CameraPitchMin,
bStart ? PlayerCharacter->CameraPitchMax + 5 : PlayerCharacter->CameraPitchMax); bStart ? PlayerCharacter->CameraPitchMax + 5 : PlayerCharacter->CameraPitchMax);
if (PlayerController->GetMainGameWidget()) if (PlayerController && PlayerController->GetMainGameWidget())
{
PlayerController->GetMainGameWidget()->SwitchKeybindsDisplay(bRemovalMode ? 4 : 1); PlayerController->GetMainGameWidget()->SwitchKeybindsDisplay(bRemovalMode ? 4 : 1);
}
// Get the Enhanced Input Local Player Subsystem from the Local Player related to our Player Controller. // Get the Enhanced Input Local Player Subsystem from the Local Player related to our Player Controller.
if (PlayerController)
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer())) if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{ {
if (!bStart) if (!bStart)
@@ -337,44 +353,28 @@ void UMoveActorsComponent::StartObjectRemoval(bool bStart, int32 Stack)
Subsystem->RemoveMappingContext(PlayerController->RemoveModeContext); Subsystem->RemoveMappingContext(PlayerController->RemoveModeContext);
} }
} }
}
bGridMovementActive = true; bGridMovementActive = true;
} }
bool UMoveActorsComponent::HighlightRemovableObject(ARemovableStaticMeshActor* Placable) bool UMoveActorsComponent::HighlightRemovableObject(ARemovableStaticMeshActor* Placable)
{ {
if (!bRemovalModePossible || !bRemovalMode) return false; if (!bRemovalModePossible || !bRemovalMode || !IsValid(Placable)) return false;
if (!HighligtedMovingActor) if (HighligtedMovingActor == Placable)
{
HighligtedMovingActor = Placable;
return true;
}
else
{
if (HighligtedMovingActor != Placable)
{
if (HighligtedMovingActor)
{
HighligtedMovingActor = Placable;
}
}
else
{ {
return true; return true;
} }
}
return false; HighligtedMovingActor = Placable;
return true;
} }
void UMoveActorsComponent::UnhighlightRemovableObject(ARemovableStaticMeshActor* Placable) void UMoveActorsComponent::UnhighlightRemovableObject(ARemovableStaticMeshActor* Placable)
{ {
if (Placable) if (Placable && HighligtedMovingActor == Placable)
{
if (HighligtedMovingActor == Placable)
{ {
HighligtedMovingActor = nullptr; HighligtedMovingActor = nullptr;
} }
} }
}

View File

@@ -47,6 +47,21 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite) UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> PlantableAreaActorClass; TSubclassOf<AActor> PlantableAreaActorClass;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Placement")
float PlacementDistance = 800.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Placement")
float PlacementHeightOffset = 100.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Placement")
float GridSnapSize = 250.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Placement")
float GroundTraceLength = 1000.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Placement", meta = (ClampMin = "0.0", ClampMax = "1.0"))
float OverlapBoxExtentMultiplier = 0.9f;
UPROPERTY(BlueprintAssignable) UPROPERTY(BlueprintAssignable)
FOnRemovalModeToggled OnRemovalModeToggled; FOnRemovalModeToggled OnRemovalModeToggled;
@@ -84,9 +99,9 @@ public:
FORCEINLINE bool IsGridMovementActive() const { return bGridMovementActive; } FORCEINLINE bool IsGridMovementActive() const { return bGridMovementActive; }
UFUNCTION(BlueprintCallable, BlueprintPure) UFUNCTION(BlueprintCallable, BlueprintPure)
inline bool IsRemovingModePossible() { return bRemovalModePossible; } bool IsRemovingModePossible() const { return bRemovalModePossible; }
UFUNCTION(BlueprintCallable, BlueprintPure) UFUNCTION(BlueprintCallable, BlueprintPure)
inline bool IsRemovingMode() { return bRemovalMode; } bool IsRemovingMode() const { return bRemovalMode; }
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)

View File

@@ -14,17 +14,60 @@ ARemovableStaticMeshActor::ARemovableStaticMeshActor()
{ {
PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bCanEverTick = true;
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
SetRootComponent(RootComponent);
WidgetComponent = CreateDefaultSubobject<UInteractionWidgetComponent>(TEXT("WidgetComponent")); WidgetComponent = CreateDefaultSubobject<UInteractionWidgetComponent>(TEXT("WidgetComponent"));
WidgetComponent->SetupAttachment(RootComponent); WidgetComponent->SetupAttachment(RootComponent);
OverlapBoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("OverlapBoxComponent"));
OverlapBoxComponent->SetupAttachment(RootComponent);
// Detection only: the box never blocks anything, it only reports overlaps.
OverlapBoxComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
OverlapBoxComponent->SetCollisionObjectType(OverlapBoxObjectType);
OverlapBoxComponent->SetCollisionResponseToAllChannels(ECR_Overlap);
OverlapBoxComponent->SetCollisionResponseToChannel(ECC_Visibility, ECR_Ignore);
OverlapBoxComponent->SetGenerateOverlapEvents(true);
OverlapBoxComponent->SetCanEverAffectNavigation(false);
}
void ARemovableStaticMeshActor::OnConstruction(const FTransform& Transform)
{
Super::OnConstruction(Transform);
UpdateOverlapBoxExtent();
}
void ARemovableStaticMeshActor::UpdateOverlapBoxExtent()
{
if (!OverlapBoxComponent)
{
return;
}
OverlapBoxComponent->SetCollisionObjectType(OverlapBoxObjectType);
UStaticMeshComponent* MeshComponent = GetStaticMeshComponent();
if (!MeshComponent || !MeshComponent->GetStaticMesh())
{
OverlapBoxComponent->SetBoxExtent(FVector::ZeroVector);
return;
}
// GetLocalBounds returns the mesh' bounding box in its own local space.
FVector BoundsMin, BoundsMax;
MeshComponent->GetLocalBounds(BoundsMin, BoundsMax);
OverlapBoxComponent->SetRelativeLocation((BoundsMin + BoundsMax) * 0.5f);
OverlapBoxComponent->SetBoxExtent(!BoxExtentOverride.IsNearlyZero()
? BoxExtentOverride
: (BoundsMax - BoundsMin) * 0.5f);
} }
void ARemovableStaticMeshActor::BeginPlay() void ARemovableStaticMeshActor::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
// Keep the detection box in sync with the mesh that is actually assigned at runtime.
UpdateOverlapBoxExtent();
PlayerRef = UGameplayStatics::GetActorOfClass(GetWorld(), AMyCharacter::StaticClass()); PlayerRef = UGameplayStatics::GetActorOfClass(GetWorld(), AMyCharacter::StaticClass());
if (PlayerRef) { if (PlayerRef) {
MoveableComponent = PlayerRef->GetComponentByClass<UMoveActorsComponent>(); MoveableComponent = PlayerRef->GetComponentByClass<UMoveActorsComponent>();

View File

@@ -6,6 +6,7 @@
#include "Engine/StaticMeshActor.h" #include "Engine/StaticMeshActor.h"
#include "../Public/InventoryComponent.h" #include "../Public/InventoryComponent.h"
#include "Components/InteractionWidgetComponent.h" #include "Components/InteractionWidgetComponent.h"
#include "Components/BoxComponent.h"
#include "Interface/InteractableActorInterface.h" #include "Interface/InteractableActorInterface.h"
#include "RemovableStaticMeshActor.generated.h" #include "RemovableStaticMeshActor.generated.h"
@@ -29,6 +30,8 @@ public:
virtual void BeginPlay() override; virtual void BeginPlay() override;
virtual void OnConstruction(const FTransform& Transform) override;
virtual void ToggleInteractableUI_Implementation(bool bActive) override; virtual void ToggleInteractableUI_Implementation(bool bActive) override;
virtual bool IsInteractable_Implementation() const override; virtual bool IsInteractable_Implementation() const override;
@@ -76,11 +79,24 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Removable Object") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Removable Object")
FPrimaryAssetId DataAssetId; FPrimaryAssetId DataAssetId;
/** Overlap only collision box that always fits the static mesh' bounding box.
* It never blocks anything, it only reports what the object currently overlaps. */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Removable Object")
UBoxComponent* OverlapBoxComponent;
/** Collision object type of OverlapBoxComponent. Defaults to the project's "RemovableStaticMEsh" channel. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Removable Object")
TEnumAsByte<ECollisionChannel> OverlapBoxObjectType = ECC_GameTraceChannel2;
/** Resizes OverlapBoxComponent so it fits the mesh' bounding box (or BoxExtentOverride when that one is set). */
UFUNCTION(BlueprintCallable, Category = "Removable Object")
void UpdateOverlapBoxExtent();
//Used for movable actors to override their box extent for checking collision //Used for movable actors to override their box extent for checking collision
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Removable Object") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Removable Object")
FVector BoxExtentOverride = FVector::ZeroVector; FVector BoxExtentOverride = FVector::ZeroVector;
UFUNCTION(BlueprintCallable, BLueprintPure) UFUNCTION(BlueprintCallable, BlueprintPure)
FORCEINLINE bool IsPlaceable() const { return bPlaceable; } FORCEINLINE bool IsPlaceable() const { return bPlaceable; }
UFUNCTION(BlueprintCallable, BLueprintPure) UFUNCTION(BlueprintCallable, BLueprintPure)
FORCEINLINE bool FromInventory() const { return InventoryRef != nullptr; } FORCEINLINE bool FromInventory() const { return InventoryRef != nullptr; }

View File

@@ -33,7 +33,7 @@ public class ProjectEleri : ModuleRules
"InteractionSystem" "InteractionSystem"
}); });
PrivateDependencyModuleNames.AddRange(new string[] { "OnlineSubsystem", "OnlineSubsystemUtils" }); PrivateDependencyModuleNames.AddRange(new string[] { "OnlineSubsystem", "OnlineSubsystemUtils", "Landscape" });
// Uncomment if you are using Slate UI // Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

View File

@@ -3,7 +3,6 @@
#include "ObjectPersistenceSubsystem.h" #include "ObjectPersistenceSubsystem.h"
#include "Interface/RemovableActorInterface.h" #include "Interface/RemovableActorInterface.h"
#include "../../Components/PlacableActorComponent.h"
#include "Kismet/GameplayStatics.h" #include "Kismet/GameplayStatics.h"
#include "../../Public/EleriSaveGame.h" #include "../../Public/EleriSaveGame.h"
#include "../GameObjects/RemovableStaticMeshActor.h" #include "../GameObjects/RemovableStaticMeshActor.h"
@@ -27,16 +26,10 @@ void UObjectPersistenceSubsystem::RemoveActorFromLevel(AActor* ActorToRemove)
//Check if actor existed meaning we're placing it for the first time //Check if actor existed meaning we're placing it for the first time
int32 ActorIndex = ActorExistsInList(ActorToRemove); int32 ActorIndex = ActorExistsInList(ActorToRemove);
if (ActorIndex == -1) if (ActorIndex != INDEX_NONE)
{
if (UPlacableActorComponent* Placeable = ActorToRemove->GetComponentByClass<UPlacableActorComponent>())
{
//Placeable->ReturnToInventory();
}
}
else
{ {
PlacedActors.RemoveAt(ActorIndex); PlacedActors.RemoveAt(ActorIndex);
} }
ActorToRemove->Destroy(); ActorToRemove->Destroy();

View File

@@ -6,7 +6,6 @@
#include "Subsystems/GameInstanceSubsystem.h" #include "Subsystems/GameInstanceSubsystem.h"
#include "ObjectPersistenceSubsystem.generated.h" #include "ObjectPersistenceSubsystem.generated.h"
class UPlacableActorComponent;
class UEleriSaveGame; class UEleriSaveGame;
USTRUCT(BlueprintType) USTRUCT(BlueprintType)