Files
MosisUnreal/Plugins/MosisSDK/Source/MosisSDK/Private/MosisPointerComponent.cpp

237 lines
6.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MosisPointerComponent.h"
#include "MosisPhoneActor.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputAction.h"
#include "GameFramework/PlayerController.h"
#include "DrawDebugHelpers.h"
DEFINE_LOG_CATEGORY_STATIC(LogMosisPointer, Log, All);
UMosisPointerComponent::UMosisPointerComponent()
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
}
void UMosisPointerComponent::BeginPlay()
{
Super::BeginPlay();
// Try to setup input bindings after a short delay to ensure player controller is ready
if (TriggerAction)
{
SetupInputBindings();
}
UE_LOG(LogMosisPointer, Log, TEXT("MosisPointerComponent initialized. RayLength: %.1f"), RayLength);
}
void UMosisPointerComponent::SetupInputBindings()
{
if (bInputBound)
{
return;
}
AActor* Owner = GetOwner();
if (!Owner)
{
return;
}
// Find the owning pawn and its controller
APawn* OwningPawn = Cast<APawn>(Owner);
if (!OwningPawn)
{
OwningPawn = Owner->GetInstigator<APawn>();
}
if (!OwningPawn)
{
UE_LOG(LogMosisPointer, Warning, TEXT("Could not find owning pawn for input binding"));
return;
}
APlayerController* PC = Cast<APlayerController>(OwningPawn->GetController());
if (!PC)
{
UE_LOG(LogMosisPointer, Warning, TEXT("Could not find player controller for input binding"));
return;
}
// Get the Enhanced Input component from the pawn
UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(OwningPawn->InputComponent);
if (!EnhancedInput)
{
UE_LOG(LogMosisPointer, Warning, TEXT("Could not find Enhanced Input component on pawn"));
return;
}
// Bind the trigger action
EnhancedInput->BindAction(TriggerAction, ETriggerEvent::Triggered, this, &UMosisPointerComponent::OnTriggerAction);
EnhancedInput->BindAction(TriggerAction, ETriggerEvent::Completed, this, &UMosisPointerComponent::OnTriggerAction);
bInputBound = true;
UE_LOG(LogMosisPointer, Log, TEXT("Successfully bound trigger input action"));
}
void UMosisPointerComponent::OnTriggerAction(const FInputActionInstance& Instance)
{
// Triggered = pressed, Completed = released
bIsTriggerPressed = (Instance.GetTriggerEvent() == ETriggerEvent::Triggered);
}
void UMosisPointerComponent::SetTriggerPressed(bool bPressed)
{
bIsTriggerPressed = bPressed;
}
void UMosisPointerComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// Try to bind input if not yet bound and we have an action
if (TriggerAction && !bInputBound)
{
SetupInputBindings();
}
// Perform raycast to find phone
PerformRaycast();
// Update touch state and send events
UpdateTouchState();
// Draw debug visualization
if (bShowDebugRay)
{
DrawDebugRay();
}
}
void UMosisPointerComponent::PerformRaycast()
{
FVector Start = GetComponentLocation();
FVector Direction = GetForwardVector();
FVector End = Start + Direction * RayLength;
FHitResult Hit;
FCollisionQueryParams Params;
Params.AddIgnoredActor(GetOwner());
// Also ignore any parent actors
AActor* Parent = GetOwner() ? GetOwner()->GetAttachParentActor() : nullptr;
while (Parent)
{
Params.AddIgnoredActor(Parent);
Parent = Parent->GetAttachParentActor();
}
bool bHit = GetWorld()->LineTraceSingleByChannel(
Hit, Start, End, TraceChannel, Params);
if (bHit)
{
AMosisPhoneActor* HitPhone = Cast<AMosisPhoneActor>(Hit.GetActor());
if (HitPhone)
{
CurrentPhone = HitPhone;
CurrentHitLocation = Hit.ImpactPoint;
bIsOverPhone = true;
}
else
{
CurrentPhone = nullptr;
bIsOverPhone = false;
}
}
else
{
CurrentPhone = nullptr;
bIsOverPhone = false;
}
}
void UMosisPointerComponent::UpdateTouchState()
{
// Detect state transitions
bool bJustPressed = bIsTriggerPressed && !bWasTriggerPressed;
bool bJustReleased = !bIsTriggerPressed && bWasTriggerPressed;
if (bIsOverPhone && CurrentPhone.IsValid())
{
if (bJustPressed)
{
// Touch down
CurrentPhone->SendTouchAtWorldLocation(CurrentHitLocation, EMosisTouchType::Down);
LastTouchedPhone = CurrentPhone;
LastHitLocation = CurrentHitLocation;
UE_LOG(LogMosisPointer, Verbose, TEXT("Touch Down at (%.1f, %.1f, %.1f)"),
CurrentHitLocation.X, CurrentHitLocation.Y, CurrentHitLocation.Z);
}
else if (bIsTriggerPressed)
{
// Touch move (only if position changed significantly)
if (FVector::DistSquared(CurrentHitLocation, LastHitLocation) > 0.01f)
{
CurrentPhone->SendTouchAtWorldLocation(CurrentHitLocation, EMosisTouchType::Move);
LastHitLocation = CurrentHitLocation;
}
}
else if (bJustReleased)
{
// Touch up
CurrentPhone->SendTouchAtWorldLocation(CurrentHitLocation, EMosisTouchType::Up);
LastTouchedPhone = nullptr;
UE_LOG(LogMosisPointer, Verbose, TEXT("Touch Up at (%.1f, %.1f, %.1f)"),
CurrentHitLocation.X, CurrentHitLocation.Y, CurrentHitLocation.Z);
}
}
else if (bJustReleased && LastTouchedPhone.IsValid())
{
// Ray moved off phone while pressed, but trigger was just released
// Send up event to the last touched phone
LastTouchedPhone->SendTouchAtWorldLocation(LastHitLocation, EMosisTouchType::Up);
LastTouchedPhone = nullptr;
UE_LOG(LogMosisPointer, Verbose, TEXT("Touch Up (off-phone) at (%.1f, %.1f, %.1f)"),
LastHitLocation.X, LastHitLocation.Y, LastHitLocation.Z);
}
bWasTriggerPressed = bIsTriggerPressed;
}
void UMosisPointerComponent::DrawDebugRay() const
{
FVector Start = GetComponentLocation();
FVector Direction = GetForwardVector();
FVector End = bIsOverPhone ? CurrentHitLocation : (Start + Direction * RayLength);
FColor Color = bIsOverPhone ? DebugRayHitColor.ToFColor(true) : DebugRayColor.ToFColor(true);
DrawDebugLine(GetWorld(), Start, End, Color, false, -1.0f, 0, 1.0f);
if (bIsOverPhone)
{
// Draw hit point
DrawDebugSphere(GetWorld(), CurrentHitLocation, 2.0f, 8, Color, false, -1.0f, 0, 0.5f);
}
}
AMosisPhoneActor* UMosisPointerComponent::GetTargetPhone() const
{
return CurrentPhone.Get();
}
FVector UMosisPointerComponent::GetRayOrigin() const
{
return GetComponentLocation();
}
FVector UMosisPointerComponent::GetRayDirection() const
{
return GetForwardVector();
}