I am working on an inventory system and want to drop the item where the cursor is in an isometric view.
So I have an InventoryComponent.h that I have mounted on the player pawn with a drop item method:
bool UInventory::DropItem(const int32 Index, UInventory* Inventory)
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: Removed item from inventory"));
const AFarmingCharacter* Owner = Cast<AFarmingCharacter>(GetOwner());
if (!Owner)
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: Owner is null"));
return false;
}
AFarmingPlayerController* FarmingPlayerController = Cast<AFarmingPlayerController>(Owner->GetController());
if (!FarmingPlayerController)
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: Owner is not a FarmingPlayerController"));
return false;
}
const FHitResult HitResult = FarmingPlayerController->GetCursorWorldLocation();
// Debug trace from the owner camera to the player pawn
DrawDebugLine(GetWorld(), Owner->GetCameraBoom()->GetComponentLocation(), Owner->GetActorLocation() , FColor::Red, false, 60.0f, 0, 1.0f);
const FVector Start = HitResult.TraceStart;
const FVector End = HitResult.TraceEnd;
if (HitResult.bBlockingHit)
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: did not hit anything"));
return false;
}
if (GetOwnerRole() == ROLE_Authority)
{
DropItem(Index, Inventory, Start, End);
return true;
} else
{
Server_DropItem(Index, Inventory, Start, End);
return true;
}
}
bool UInventory::DropItem(const int32 Index, UInventory* Inventory, FVector Start, FVector End)
{
if (GetOwnerRole() == ROLE_Authority)
{
UE_LOG(LogTemp, Warning, TEXT("DropItem as server"));
if (Inventory)
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: Inventory found"));
FItemData ItemData = Inventory->GetItem(Index);
if (Inventory->RemoveItem(Index, 1))
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: HitResult.TraceStart: %s, HitResult.ImpactPoint: %s"), *Start.ToString(), *End.ToString());
FHitResult HitResult;
FCollisionQueryParams Params;
GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECollisionChannel::ECC_Visibility, Params);
// Debug trace
DrawDebugLine(GetWorld(), HitResult.TraceStart, HitResult.ImpactPoint, FColor::Blue, false, 2.0f, 0, 1.0f);
UE_LOG(LogTemp, Warning, TEXT("DropItem: HitResult.TraceStart: %s, HitResult.ImpactPoint: %s"), *HitResult.TraceStart.ToString(), *HitResult.ImpactPoint.ToString());
// If the hit from the client and server are the same within a certain tolerance, we assume that the client and server are dropping on the same place
if (HitResult.bBlockingHit && Start.Distance(Start, HitResult.TraceStart) < 10.0f && End.Distance(End, HitResult.TraceEnd) < 10.0f)
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: HitResult.bBlockingHit is true"));
FVector HitLocation = HitResult.ImpactPoint;
HitLocation.Z = HitResult.ImpactPoint.Z + 10.0f;
if (UWorld* World = GetWorld())
{
World->SpawnActor<AItem>(ItemData.ItemClass, HitLocation, FRotator::ZeroRotator);
return true;
}
else
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: World is null"));
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: HitResult.bBlockingHit is false"));
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: Failed to remove item from inventory"));
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("DropItem: Inventory not found"));
}
}
else
{
Server_DropItem(Index, Inventory, Start, End);
return true;
}
UE_LOG(LogTemp, Warning, TEXT("DropItem: Failed to drop item"));
return false;
}
bool UInventory::Server_DropItem_Validate(const int32 Index, UInventory* Inventory, FVector Start, FVector End)
{
return true;
}
void UInventory::Server_DropItem_Implementation(const int32 Index, UInventory* Inventory, FVector Start, FVector End)
{
DropItem(Index, Inventory, Start, End);
}
In InventoryComponent.h
protected:
UFUNCTION(BlueprintCallable, Category = "Inventory")
bool DropItem(const int32 Index, UInventory* Inventory);
bool DropItem(const int32 Index, UInventory* Inventory, FVector Start, FVector End);
UFUNCTION(Server, Reliable, WithValidation)
void Server_DropItem(const int32 Index, UInventory* Inventory, FVector Start, FVector End);
AFarmingPlayerController.h
FHitResult AFarmingPlayerController::GetCursorWorldLocation()
{
FVector2D ScreenLocation;
FHitResult Hit;
// Get the mouse position in screen space
if (GetMousePosition(ScreenLocation.X, ScreenLocation.Y))
{
FVector WorldLocation, WorldDirection;
DeprojectScreenPositionToWorld(ScreenLocation.X, ScreenLocation.Y, WorldLocation, WorldDirection);
const FVector TraceStart = WorldLocation;
const FVector TraceEnd = TraceStart + (WorldDirection * 10000.0f);
// Perform a line trace to get the hit result
GetWorld()->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Visibility);
// Debug trace
DrawDebugLine(GetWorld(), TraceStart, TraceEnd, FColor::Blue, false, 2.0f, 0, 1.0f);
UE_LOG(LogTemp, Warning, TEXT("GetCursorWorldLocation: Hit.TraceStart: %s, Hit.ImpactPoint: %s"), *Hit.TraceStart.ToString(), *Hit.ImpactPoint.ToString());
}
else
{
UE_LOG(LogTemp, Warning, TEXT("GetCursorWorldLocation: Unable to get mouse position"));
}
return Hit;
}
While if we use a Key input like “N” to test if the player controller method works, we can get to be called correctly. With a test method like this:
void AFarmingCharacter::Test() const
{
// Additional logging to debug the issue
AFarmingPlayerController* FarmingPlayerController = Cast<AFarmingPlayerController>(GetController());
const FHitResult HitResult = FarmingPlayerController->GetCursorWorldLocation();
UE_LOG(LogTemp, Warning, TEXT("Test: HitResult.TraceStart: %s, HitResult.ImpactPoint: %s"), *HitResult.TraceStart.ToString(), *HitResult.ImpactPoint.ToString());
}
Where I am struggeling now is to actually do the same on a drop event from UMG:
How I set the player pawn on the dropped UMG (the inventory main screen)
On the UMG widget that created the drag operation (the inventory item)
Each inventory item widget is created from a method on the inventory grid
Which is called from the Player pawn and passed the replicated Inventory component
Where would I start to look for what might be the issue here?