237 lines
6.9 KiB
C++
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();
|
|
}
|