Skip to content
Snippets Groups Projects
Commit aeb67856 authored by Timon Römer's avatar Timon Römer
Browse files

Comments and explains IntenSelect Component source code

parent 3ea5666d
Branches
No related tags found
1 merge request!87Merge IntenSelect into dev5.3
...@@ -77,47 +77,57 @@ void UIntenSelectComponent::BeginPlay() ...@@ -77,47 +77,57 @@ void UIntenSelectComponent::BeginPlay()
InitInputBindings(); InitInputBindings();
InitMaterialParamCollection(); InitMaterialParamCollection();
// Calculate sphere cast radius
SphereCastRadius = CalculateSphereCastRadius(); SphereCastRadius = CalculateSphereCastRadius();
// Set interaction distance to maximum selection distance
InteractionDistance = MaxSelectionDistance; InteractionDistance = MaxSelectionDistance;
// Set the component active based on the SetActiveOnStart flag
SetActive(SetActiveOnStart, false); SetActive(SetActiveOnStart, false);
} }
void UIntenSelectComponent::InitInputBindings() void UIntenSelectComponent::InitInputBindings()
{ {
// Get the player controller
const APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0); const APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
// Get the local player subsystem for enhanced input
UEnhancedInputLocalPlayerSubsystem* Subsystem = UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()); ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer());
// Get the player input component
UInputComponent* PlayerInputComponent = PC->InputComponent; UInputComponent* PlayerInputComponent = PC->InputComponent;
UEnhancedInputComponent* PEI = Cast<UEnhancedInputComponent>(PlayerInputComponent); UEnhancedInputComponent* PEI = Cast<UEnhancedInputComponent>(PlayerInputComponent);
// Check if the enhanced input component is valid
if (!PEI) if (!PEI)
{ {
// Display an error message and quit the game if the enhanced input component is not found
const FString Message = "Could not get PlayerInputComponent for IntenSelect Input Assignment!"; const FString Message = "Could not get PlayerInputComponent for IntenSelect Input Assignment!";
#if WITH_EDITOR #if WITH_EDITOR
const FText Title = FText::FromString(FString("ERROR")); const FText Title = FText::FromString(FString("ERROR"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Message), Title); FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Message), Title);
#endif #endif
UE_LOG(LogTemp, Error, TEXT("%s"), *Message) UE_LOG(LogTemp, Error, TEXT("%s"), *Message)
UKismetSystemLibrary::QuitGame(this, nullptr, EQuitPreference::Quit, false); UKismetSystemLibrary::QuitGame(this, nullptr, EQuitPreference::Quit, false);
return; return;
} }
// Bind the actions // Bind the actions for input events
PEI->BindAction(InputClick, ETriggerEvent::Started, this, &UIntenSelectComponent::OnFireDown); PEI->BindAction(InputClick, ETriggerEvent::Started, this, &UIntenSelectComponent::OnFireDown);
PEI->BindAction(InputClick, ETriggerEvent::Completed, this, &UIntenSelectComponent::OnFireUp); PEI->BindAction(InputClick, ETriggerEvent::Completed, this, &UIntenSelectComponent::OnFireUp);
} }
void UIntenSelectComponent::InitSplineComponent() void UIntenSelectComponent::InitSplineComponent()
{ {
// Create a new spline component
SplineComponent = NewObject<USplineComponent>(this, TEXT("SplineComponent")); SplineComponent = NewObject<USplineComponent>(this, TEXT("SplineComponent"));
// Check if the spline component was successfully created
if (SplineComponent) if (SplineComponent)
{ {
// Setup attachment and mobility of the spline component
SplineComponent->SetupAttachment(this); SplineComponent->SetupAttachment(this);
SplineComponent->SetMobility(EComponentMobility::Movable); SplineComponent->SetMobility(EComponentMobility::Movable);
SplineComponent->RegisterComponent(); SplineComponent->RegisterComponent();
...@@ -125,27 +135,32 @@ void UIntenSelectComponent::InitSplineComponent() ...@@ -125,27 +135,32 @@ void UIntenSelectComponent::InitSplineComponent()
} }
else else
{ {
// Display an error message if the spline component creation fails
const FString Message = "Error while spawning SplineComponent!"; const FString Message = "Error while spawning SplineComponent!";
#if WITH_EDITOR #if WITH_EDITOR
const FText Title = FText::FromString(FString("ERROR")); const FText Title = FText::FromString(FString("ERROR"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Message), Title); FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Message), Title);
#endif #endif
UE_LOG(LogTemp, Error, TEXT("%s"), *Message) UE_LOG(LogTemp, Error, TEXT("%s"), *Message)
} }
} }
void UIntenSelectComponent::InitSplineMeshComponent() void UIntenSelectComponent::InitSplineMeshComponent()
{ {
// Create a new spline mesh component
SplineMeshComponent = SplineMeshComponent =
NewObject<USplineMeshComponent>(this, USplineMeshComponent::StaticClass(), TEXT("SplineMeshComponent")); NewObject<USplineMeshComponent>(this, USplineMeshComponent::StaticClass(), TEXT("SplineMeshComponent"));
// Check if the spline mesh component was successfully created
if (SplineMeshComponent) if (SplineMeshComponent)
{ {
// Setup attachment and mobility of the spline mesh component
SplineMeshComponent->SetupAttachment(this); SplineMeshComponent->SetupAttachment(this);
SplineMeshComponent->SetMobility(EComponentMobility::Movable); SplineMeshComponent->SetMobility(EComponentMobility::Movable);
SplineMeshComponent->RegisterComponent(); SplineMeshComponent->RegisterComponent();
SplineMeshComponent->CreationMethod = EComponentCreationMethod::Instance; SplineMeshComponent->CreationMethod = EComponentCreationMethod::Instance;
// Set the static mesh if available
if (SplineMesh) if (SplineMesh)
{ {
SplineMeshComponent->SetStaticMesh(SplineMesh); SplineMeshComponent->SetStaticMesh(SplineMesh);
...@@ -155,6 +170,7 @@ void UIntenSelectComponent::InitSplineMeshComponent() ...@@ -155,6 +170,7 @@ void UIntenSelectComponent::InitSplineMeshComponent()
UE_LOG(LogTemp, Warning, TEXT("SplineMesh not set!")); UE_LOG(LogTemp, Warning, TEXT("SplineMesh not set!"));
} }
// Set the material if available
if (SplineMaterial) if (SplineMaterial)
{ {
SplineMeshComponent->SetMaterial(0, SplineMaterial); SplineMeshComponent->SetMaterial(0, SplineMaterial);
...@@ -164,35 +180,43 @@ void UIntenSelectComponent::InitSplineMeshComponent() ...@@ -164,35 +180,43 @@ void UIntenSelectComponent::InitSplineMeshComponent()
UE_LOG(LogTemp, Warning, TEXT("SplineMesh material not set! Using default material instead.")); UE_LOG(LogTemp, Warning, TEXT("SplineMesh material not set! Using default material instead."));
} }
// Set the forward axis and shadow casting properties
SplineMeshComponent->SetForwardAxis(ESplineMeshAxis::Z); SplineMeshComponent->SetForwardAxis(ESplineMeshAxis::Z);
SplineMeshComponent->CastShadow = false; SplineMeshComponent->CastShadow = false;
} }
else else
{ {
// Display an error message if the spline mesh component creation fails
UE_LOG(LogTemp, Error, TEXT("Error while spawning SplineMeshComponent!")) UE_LOG(LogTemp, Error, TEXT("Error while spawning SplineMeshComponent!"))
} }
} }
void UIntenSelectComponent::InitForwardRayMeshComponent() void UIntenSelectComponent::InitForwardRayMeshComponent()
{ {
// Create a new static mesh component for the forward ray
ForwardRayMeshComponent = ForwardRayMeshComponent =
NewObject<UStaticMeshComponent>(this, UStaticMeshComponent::StaticClass(), TEXT("ForwardRay")); NewObject<UStaticMeshComponent>(this, UStaticMeshComponent::StaticClass(), TEXT("ForwardRay"));
// Check if the forward ray mesh component was successfully created
if (ForwardRayMeshComponent) if (ForwardRayMeshComponent)
{ {
// Setup attachment and mobility of the forward ray mesh component
ForwardRayMeshComponent->SetupAttachment(this); ForwardRayMeshComponent->SetupAttachment(this);
ForwardRayMeshComponent->SetMobility((EComponentMobility::Movable)); ForwardRayMeshComponent->SetMobility(EComponentMobility::Movable);
ForwardRayMeshComponent->RegisterComponent(); ForwardRayMeshComponent->RegisterComponent();
ForwardRayMeshComponent->CreationMethod = EComponentCreationMethod::Instance; ForwardRayMeshComponent->CreationMethod = EComponentCreationMethod::Instance;
// Configure shadow casting and collision properties
ForwardRayMeshComponent->SetCastShadow(false); ForwardRayMeshComponent->SetCastShadow(false);
ForwardRayMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision); ForwardRayMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
// Set relative scale and location based on max selection distance
const float MeshLength = MaxSelectionDistance > 1000 ? 1000 : MaxSelectionDistance; const float MeshLength = MaxSelectionDistance > 1000 ? 1000 : MaxSelectionDistance;
ForwardRayMeshComponent->SetRelativeScale3D(FVector(MeshLength, ForwardRayWidth, ForwardRayWidth)); ForwardRayMeshComponent->SetRelativeScale3D(FVector(MeshLength, ForwardRayWidth, ForwardRayWidth));
ForwardRayMeshComponent->SetRelativeLocation(FVector(MeshLength * 50, 0, 0)); ForwardRayMeshComponent->SetRelativeLocation(FVector(MeshLength * 50, 0, 0));
// const ConstructorHelpers::FObjectFinder<UStaticMesh> CubeMesh(TEXT("/Engine/BasicShapes/Cube.Cube")); // Set the static mesh for the forward ray component if available
if (ForwardRayMesh) if (ForwardRayMesh)
{ {
ForwardRayMeshComponent->SetStaticMesh(ForwardRayMesh); ForwardRayMeshComponent->SetStaticMesh(ForwardRayMesh);
...@@ -202,62 +226,76 @@ void UIntenSelectComponent::InitForwardRayMeshComponent() ...@@ -202,62 +226,76 @@ void UIntenSelectComponent::InitForwardRayMeshComponent()
UE_LOG(LogTemp, Warning, TEXT("Mesh for RayComponent not set!")); UE_LOG(LogTemp, Warning, TEXT("Mesh for RayComponent not set!"));
} }
// Create dynamic material instance for the forward ray mesh component
UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic* DynamicMaterial =
UMaterialInstanceDynamic::Create(ForwardRayMaterial, ForwardRayMeshComponent); UMaterialInstanceDynamic::Create(ForwardRayMaterial, ForwardRayMeshComponent);
this->ForwardRayMeshComponent->SetMaterial(0, DynamicMaterial); this->ForwardRayMeshComponent->SetMaterial(0, DynamicMaterial);
// Set visibility based on draw forward ray flag
ForwardRayMeshComponent->SetHiddenInGame(!bDrawForwardRay); ForwardRayMeshComponent->SetHiddenInGame(!bDrawForwardRay);
} }
else else
{ {
// Display an error message if the forward ray mesh component creation fails
UE_LOG(LogTemp, Error, TEXT("Error while spawning ForwardRayMesh component!")); UE_LOG(LogTemp, Error, TEXT("Error while spawning ForwardRayMesh component!"));
} }
} }
void UIntenSelectComponent::InitMaterialParamCollection() void UIntenSelectComponent::InitMaterialParamCollection()
{ {
// Check if the material parameter collection is set
if (MaterialParamCollection) if (MaterialParamCollection)
{ {
// Get the parameter collection instance from the world
this->ParameterCollectionInstance = GetWorld()->GetParameterCollectionInstance(MaterialParamCollection); this->ParameterCollectionInstance = GetWorld()->GetParameterCollectionInstance(MaterialParamCollection);
if (this->ParameterCollectionInstance) if (this->ParameterCollectionInstance)
{ {
// Set the scalar parameter value for transparency
this->ParameterCollectionInstance->SetScalarParameterValue("Transparency", DebugRayTransparency); this->ParameterCollectionInstance->SetScalarParameterValue("Transparency", DebugRayTransparency);
} }
else else
{ {
// Display a warning if the parameter collection instance is not found
UE_LOG(LogTemp, Warning, UE_LOG(LogTemp, Warning,
TEXT("MaterialParameterCollection required for rendering of IntenSelect could not be found!")) TEXT("MaterialParameterCollection required for rendering of IntenSelect could not be found!"))
} }
} }
else else
{ {
// Display a warning if the material parameter collection is not set
UE_LOG(LogTemp, Warning, TEXT("MaterialParameterCollection required for InteSelect visualization is not set!")); UE_LOG(LogTemp, Warning, TEXT("MaterialParameterCollection required for InteSelect visualization is not set!"));
} }
} }
void UIntenSelectComponent::InitDebugConeMeshComponent() void UIntenSelectComponent::InitDebugConeMeshComponent()
{ {
// Create a new static mesh component for the debug cone
DebugConeMeshComponent = DebugConeMeshComponent =
NewObject<UStaticMeshComponent>(this, UStaticMeshComponent::StaticClass(), TEXT("DebugCone")); NewObject<UStaticMeshComponent>(this, UStaticMeshComponent::StaticClass(), TEXT("DebugCone"));
// Check if the debug cone mesh component was successfully created
if (DebugConeMeshComponent) if (DebugConeMeshComponent)
{ {
// Setup attachment and mobility of the debug cone mesh component
DebugConeMeshComponent->SetupAttachment(this); DebugConeMeshComponent->SetupAttachment(this);
DebugConeMeshComponent->SetMobility(EComponentMobility::Movable); DebugConeMeshComponent->SetMobility(EComponentMobility::Movable);
DebugConeMeshComponent->RegisterComponent(); DebugConeMeshComponent->RegisterComponent();
DebugConeMeshComponent->CreationMethod = EComponentCreationMethod::Instance; DebugConeMeshComponent->CreationMethod = EComponentCreationMethod::Instance;
// Calculate transform for the cone based on selection cone angle and distance
FTransform ConeTransform = DebugConeMeshComponent->GetRelativeTransform(); FTransform ConeTransform = DebugConeMeshComponent->GetRelativeTransform();
const float ConeScale = MaxSelectionDistance / 50 * FMath::Tan(FMath::DegreesToRadians(SelectionConeAngle)); const float ConeScale = MaxSelectionDistance / 50 * FMath::Tan(FMath::DegreesToRadians(SelectionConeAngle));
ConeTransform.SetScale3D(FVector(ConeScale, ConeScale, MaxSelectionDistance / 100)); ConeTransform.SetScale3D(FVector(ConeScale, ConeScale, MaxSelectionDistance / 100));
// Set relative transform and location for the debug cone
DebugConeMeshComponent->SetRelativeTransform(ConeTransform); DebugConeMeshComponent->SetRelativeTransform(ConeTransform);
DebugConeMeshComponent->SetRelativeLocation(FVector(MaxSelectionDistance - ConeBackwardShiftDistance, 0, 0), DebugConeMeshComponent->SetRelativeLocation(FVector(MaxSelectionDistance - ConeBackwardShiftDistance, 0, 0),
false); false);
DebugConeMeshComponent->SetRelativeRotation(DebugConeRotation, false); DebugConeMeshComponent->SetRelativeRotation(DebugConeRotation, false);
DebugConeMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision); DebugConeMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
// Set the static mesh for the debug cone component if available
if (DebugConeMesh) if (DebugConeMesh)
{ {
DebugConeMeshComponent->SetStaticMesh(DebugConeMesh); DebugConeMeshComponent->SetStaticMesh(DebugConeMesh);
...@@ -266,6 +304,8 @@ void UIntenSelectComponent::InitDebugConeMeshComponent() ...@@ -266,6 +304,8 @@ void UIntenSelectComponent::InitDebugConeMeshComponent()
{ {
UE_LOG(LogTemp, Warning, TEXT("DebugCone mesh not set!")) UE_LOG(LogTemp, Warning, TEXT("DebugCone mesh not set!"))
} }
// Set the material for the debug cone component if available
if (DebugConeMaterial) if (DebugConeMaterial)
{ {
DebugConeMeshComponent->SetMaterial(0, DebugConeMaterial); DebugConeMeshComponent->SetMaterial(0, DebugConeMaterial);
...@@ -275,10 +315,12 @@ void UIntenSelectComponent::InitDebugConeMeshComponent() ...@@ -275,10 +315,12 @@ void UIntenSelectComponent::InitDebugConeMeshComponent()
UE_LOG(LogTemp, Warning, TEXT("DebugCone material not set! Using default material instead.")) UE_LOG(LogTemp, Warning, TEXT("DebugCone material not set! Using default material instead."))
} }
// Set visibility based on draw debug cone flag
DebugConeMeshComponent->SetVisibility(bDrawDebugCone); DebugConeMeshComponent->SetVisibility(bDrawDebugCone);
} }
else else
{ {
// Display an error message if the debug cone mesh component creation fails
UE_LOG(LogTemp, Error, TEXT("Error while spawning DebugCone component!")) UE_LOG(LogTemp, Error, TEXT("Error while spawning DebugCone component!"))
} }
} }
...@@ -288,59 +330,72 @@ void UIntenSelectComponent::InitDebugConeMeshComponent() ...@@ -288,59 +330,72 @@ void UIntenSelectComponent::InitDebugConeMeshComponent()
float UIntenSelectComponent::CalculateSphereCastRadius() const float UIntenSelectComponent::CalculateSphereCastRadius() const
{ {
// Calculate sphere cast radius based on selection cone angle and max selection distance
return FMath::Tan(FMath::DegreesToRadians(SelectionConeAngle)) * MaxSelectionDistance; return FMath::Tan(FMath::DegreesToRadians(SelectionConeAngle)) * MaxSelectionDistance;
} }
bool UIntenSelectComponent::CheckPointInCone(const FVector ConeStartPoint, const FVector ConeForward, bool UIntenSelectComponent::CheckPointInCone(const FVector ConeStartPoint, const FVector ConeForward,
const FVector PointToTest, const float Angle) const const FVector PointToTest, const float Angle) const
{ {
// Shift the start origin point of the cone backward
const FVector ShiftedStartOriginPoint = ConeStartPoint - (ConeForward * ConeBackwardShiftDistance); const FVector ShiftedStartOriginPoint = ConeStartPoint - (ConeForward * ConeBackwardShiftDistance);
// Calculate the direction to the test point
const FVector DirectionToTestPoint = (PointToTest - ShiftedStartOriginPoint).GetSafeNormal(); const FVector DirectionToTestPoint = (PointToTest - ShiftedStartOriginPoint).GetSafeNormal();
// Calculate the angle to the test point
const float AngleToTestPoint = const float AngleToTestPoint =
FMath::RadiansToDegrees(FMath::Acos((FVector::DotProduct(ConeForward, DirectionToTestPoint)))); FMath::RadiansToDegrees(FMath::Acos((FVector::DotProduct(ConeForward, DirectionToTestPoint))));
// Check if the angle to the test point is within the specified cone angle
return AngleToTestPoint <= Angle; return AngleToTestPoint <= Angle;
} }
void UIntenSelectComponent::OnNewSelected_Implementation(UIntenSelectable* Selection) void UIntenSelectComponent::OnNewSelected_Implementation(UIntenSelectable* Selection)
{ {
// Set the current selection
CurrentSelection = Selection; CurrentSelection = Selection;
// Play sound feedback if cooldown allows
if (FeedbackCooldown == 0) if (FeedbackCooldown == 0)
{ {
// UGameplayStatics::GetPlayerController(GetWorld(), 0)->PlayHapticEffect(SelectionFeedbackHaptic,
// EControllerHand::Right, 0.1, false);
UGameplayStatics::PlaySound2D(GetWorld(), OnSelectSound); UGameplayStatics::PlaySound2D(GetWorld(), OnSelectSound);
FeedbackCooldown = 0.1; FeedbackCooldown = 0.1;
} }
} }
bool UIntenSelectComponent::GetActorsFromSphereCast(const FVector& SphereCastStart, TArray<FHitResult>& OutHits) const bool UIntenSelectComponent::GetActorsFromSphereCast(const FVector& SphereCastStart, TArray<FHitResult>& OutHits) const
{ {
// Calculate start and end positions for the sphere cast
const FVector StartPos = const FVector StartPos =
SphereCastStart + (GetComponentTransform().GetRotation().GetForwardVector() * SphereCastRadius); SphereCastStart + (GetComponentTransform().GetRotation().GetForwardVector() * SphereCastRadius);
const FVector EndPos = const FVector EndPos = StartPos + (GetComponentTransform().GetRotation().GetForwardVector() * MaxSelectionDistance);
StartPos + (this->GetComponentTransform().GetRotation().GetForwardVector() * (MaxSelectionDistance));
// Set up collision query parameters
const FCollisionQueryParams Params = FCollisionQueryParams(FName(TEXT("SphereTraceMultiForObjects")), false); const FCollisionQueryParams Params = FCollisionQueryParams(FName(TEXT("SphereTraceMultiForObjects")), false);
// GetWorld()->SweepMultiByChannel(OutHits, StartPos, EndPos, FQuat::Identity, ECC_Visibility,
// FCollisionShape::MakeSphere(SphereCastRadius), Params);
// Perform sphere cast
GetWorld()->SweepMultiByChannel(OutHits, StartPos, EndPos, FQuat::Identity, ECC_Visibility, GetWorld()->SweepMultiByChannel(OutHits, StartPos, EndPos, FQuat::Identity, ECC_Visibility,
FCollisionShape::MakeSphere(SphereCastRadius), Params); FCollisionShape::MakeSphere(SphereCastRadius), Params);
// UKismetSystemLibrary::SphereTraceMulti(GetWorld(),StartPos,EndPos,SphereCastRadius,ETraceTypeQuery::TraceTypeQuery1,false,{},EDrawDebugTrace::ForOneFrame,OutHits,true);
return true; return true;
} }
UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime) UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime)
{ {
const FVector ConeOrigin = this->GetComponentTransform().GetLocation(); // Get cone origin and forward direction
const FVector ConeForward = this->GetComponentTransform().GetRotation().GetForwardVector(); const FVector ConeOrigin = GetComponentTransform().GetLocation();
const FVector ConeForward = GetComponentTransform().GetRotation().GetForwardVector();
// Perform sphere cast to detect selectable actors
TArray<FHitResult> OutHits; TArray<FHitResult> OutHits;
if (GetActorsFromSphereCast(ConeOrigin, OutHits)) if (GetActorsFromSphereCast(ConeOrigin, OutHits))
{ {
// Iterate through hit results
for (const FHitResult& Hit : OutHits) for (const FHitResult& Hit : OutHits)
{ {
const FVector PointToCheck = Hit.ImpactPoint; const FVector PointToCheck = Hit.ImpactPoint;
...@@ -349,36 +404,39 @@ UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime) ...@@ -349,36 +404,39 @@ UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime)
const AActor* HitActor = Hit.GetActor(); const AActor* HitActor = Hit.GetActor();
if (HitActor) if (HitActor)
{ {
const auto Selectable = HitActor->FindComponentByClass<UIntenSelectable>(); // Check if the hit actor is selectable and within selection distance
UIntenSelectable* Selectable = HitActor->FindComponentByClass<UIntenSelectable>();
if (Selectable && Selectable->IsSelectable && DistanceToActor <= MaxSelectionDistance) if (Selectable && Selectable->IsSelectable && DistanceToActor <= MaxSelectionDistance)
{ {
// Add to score map
ScoreMap.FindOrAdd(Selectable, 0); ScoreMap.FindOrAdd(Selectable, 0);
} }
} }
} }
} }
// Variables for tracking the maximum score selectable
UIntenSelectable* MaxScoreSelectable = nullptr; UIntenSelectable* MaxScoreSelectable = nullptr;
float MaxScore = TNumericLimits<float>::Min(); float MaxScore = TNumericLimits<float>::Min();
TArray<UIntenSelectable*> RemoveList; TArray<UIntenSelectable*> RemoveList;
TArray<TPair<UIntenSelectable*, FHitResult>> CandidateList; TArray<TPair<UIntenSelectable*, FHitResult>> CandidateList;
for (TTuple<UIntenSelectable*, float>& OldScoreEntry : ScoreMap) // Iterate through the score map
for (TPair<UIntenSelectable*, float>& OldScoreEntry : ScoreMap)
{ {
// GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0, FColor::Black, OldScoreEntry.Key->GetOwner()->GetName() + "
// - Score: " + FString::SanitizeFloat(OldScoreEntry.Value));
if (!OldScoreEntry.Key) if (!OldScoreEntry.Key)
{ {
continue; continue;
} }
// Calculate the new score and contact point
TPair<FHitResult, float> NewScorePair = OldScoreEntry.Key->GetBestPointScorePair( TPair<FHitResult, float> NewScorePair = OldScoreEntry.Key->GetBestPointScorePair(
ConeOrigin, ConeForward, ConeBackwardShiftDistance, SelectionConeAngle, OldScoreEntry.Value, DeltaTime); ConeOrigin, ConeForward, ConeBackwardShiftDistance, SelectionConeAngle, OldScoreEntry.Value, DeltaTime);
ContactPointMap.Add(OldScoreEntry.Key, NewScorePair.Key); ContactPointMap.Add(OldScoreEntry.Key, NewScorePair.Key);
const float DistanceToActor = FVector::Dist(ConeOrigin, NewScorePair.Key.ImpactPoint); const float DistanceToActor = FVector::Dist(ConeOrigin, NewScorePair.Key.ImpactPoint);
// Check if the new score is valid and if the actor is still selectable
const float Eps = 0.01; const float Eps = 0.01;
if (NewScorePair.Value <= 0.01 || DistanceToActor >= MaxSelectionDistance || !OldScoreEntry.Key->IsSelectable) if (NewScorePair.Value <= 0.01 || DistanceToActor >= MaxSelectionDistance || !OldScoreEntry.Key->IsSelectable)
{ {
...@@ -388,15 +446,16 @@ UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime) ...@@ -388,15 +446,16 @@ UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime)
{ {
OldScoreEntry.Value = NewScorePair.Value; OldScoreEntry.Value = NewScorePair.Value;
// Check if the new score exceeds the maximum score
if (NewScorePair.Value > (1.0 - Eps) && if (NewScorePair.Value > (1.0 - Eps) &&
this->CheckPointInCone(ConeOrigin, ConeForward, NewScorePair.Key.ImpactPoint, SelectionConeAngle)) CheckPointInCone(ConeOrigin, ConeForward, NewScorePair.Key.ImpactPoint, SelectionConeAngle))
{ {
CandidateList.Emplace(OldScoreEntry.Key, NewScorePair.Key); CandidateList.Emplace(OldScoreEntry.Key, NewScorePair.Key);
MaxScore = NewScorePair.Value; MaxScore = NewScorePair.Value;
MaxScoreSelectable = OldScoreEntry.Key; MaxScoreSelectable = OldScoreEntry.Key;
} }
else if (NewScorePair.Value > MaxScore && else if (NewScorePair.Value > MaxScore &&
this->CheckPointInCone(ConeOrigin, ConeForward, NewScorePair.Key.ImpactPoint, SelectionConeAngle)) CheckPointInCone(ConeOrigin, ConeForward, NewScorePair.Key.ImpactPoint, SelectionConeAngle))
{ {
MaxScore = NewScorePair.Value; MaxScore = NewScorePair.Value;
MaxScoreSelectable = OldScoreEntry.Key; MaxScoreSelectable = OldScoreEntry.Key;
...@@ -404,19 +463,22 @@ UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime) ...@@ -404,19 +463,22 @@ UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime)
} }
} }
for (const UIntenSelectable* i : RemoveList) // Remove non-selectable actors from the maps
for (UIntenSelectable* i : RemoveList)
{ {
ContactPointMap.Remove(i); ContactPointMap.Remove(i);
ScoreMap.Remove(i); ScoreMap.Remove(i);
} }
// Select the nearest actor from the candidate list if available
if (CandidateList.Num() > 0) if (CandidateList.Num() > 0)
{ {
auto DistanceToMaxScore = float DistanceToMaxScore =
FVector::Distance(MaxScoreSelectable->GetOwner()->GetActorLocation(), GetComponentLocation()); FVector::Distance(MaxScoreSelectable->GetOwner()->GetActorLocation(), GetComponentLocation());
auto Dist = TNumericLimits<float>::Max(); float Dist = TNumericLimits<float>::Max();
for (const TPair<UIntenSelectable*, FHitResult>& Actor : CandidateList) for (const TPair<UIntenSelectable*, FHitResult>& Actor : CandidateList)
{ {
const auto DistanceToCandidate = FVector::Distance(Actor.Value.ImpactPoint, GetComponentLocation()); const float DistanceToCandidate = FVector::Distance(Actor.Value.ImpactPoint, GetComponentLocation());
if (DistanceToCandidate < Dist) if (DistanceToCandidate < Dist)
{ {
MaxScoreSelectable = Actor.Key; MaxScoreSelectable = Actor.Key;
...@@ -427,46 +489,48 @@ UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime) ...@@ -427,46 +489,48 @@ UIntenSelectable* UIntenSelectComponent::GetMaxScoreActor(const float DeltaTime)
return MaxScoreSelectable; return MaxScoreSelectable;
} }
// RAYCASTING // RAYCASTING
void UIntenSelectComponent::HandleWidgetInteraction() void UIntenSelectComponent::HandleWidgetInteraction()
{ {
const FVector Forward = this->GetComponentTransform().GetRotation().GetForwardVector(); // Get forward vector and origin of the component
const FVector Origin = this->GetComponentTransform().GetLocation(); const FVector Forward = GetComponentTransform().GetRotation().GetForwardVector();
const FVector Origin = GetComponentTransform().GetLocation();
// Raytrace to find the first hit
TOptional<FHitResult> Hit = RaytraceForFirstHit(Origin, Origin + Forward * MaxSelectionDistance); TOptional<FHitResult> Hit = RaytraceForFirstHit(Origin, Origin + Forward * MaxSelectionDistance);
// If no hit, clear focus and return
if (!Hit.IsSet()) if (!Hit.IsSet())
{ {
IsWidgetInFocus = false; IsWidgetInFocus = false;
return; return;
} }
// Set hit result and check if a widget is in focus
SetCustomHitResult(Hit.GetValue()); SetCustomHitResult(Hit.GetValue());
UWidgetComponent* FocusedWidget = Cast<UWidgetComponent>(Hit.GetValue().GetComponent()); UWidgetComponent* FocusedWidget = Cast<UWidgetComponent>(Hit.GetValue().GetComponent());
IsWidgetInFocus = (FocusedWidget != nullptr); IsWidgetInFocus = (FocusedWidget != nullptr);
// Handle widget events (commented out for now)
/* /*
if (IsWidgetInFocus) if (IsWidgetInFocus)
{ {
if (FocusedWidget != LastFocusedWidget) if (FocusedWidget != LastFocusedWidget)
{ {
//We can always execute the enter event as we are sure that a hit occured
if (FocusedWidget->GetOwner()->Implements<UTargetable>()) if (FocusedWidget->GetOwner()->Implements<UTargetable>())
{ {
ITargetable::Execute_OnTargetedEnter(FocusedWidget->GetOwner()); ITargetable::Execute_OnTargetedEnter(FocusedWidget->GetOwner());
} }
//Only execute the Leave Event if there was an actor that was focused previously
if (LastFocusedWidget != nullptr && LastFocusedWidget->GetOwner()->Implements<UTargetable>()) if (LastFocusedWidget != nullptr && LastFocusedWidget->GetOwner()->Implements<UTargetable>())
{ {
ITargetable::Execute_OnTargetedLeave(LastFocusedWidget->GetOwner()); ITargetable::Execute_OnTargetedLeave(LastFocusedWidget->GetOwner());
} }
} }
// for now uses the same distance as clicking
if (FocusedWidget->GetOwner()->Implements<UTargetable>()) if (FocusedWidget->GetOwner()->Implements<UTargetable>())
{ {
ITargetable::Execute_OnTargeted(FocusedWidget->GetOwner(), Hit->Location); ITargetable::Execute_OnTargeted(FocusedWidget->GetOwner(), Hit->Location);
...@@ -478,27 +542,33 @@ void UIntenSelectComponent::HandleWidgetInteraction() ...@@ -478,27 +542,33 @@ void UIntenSelectComponent::HandleWidgetInteraction()
FVector pos = IIntenSelectableWidget::Execute_GetCoordinates(FocusedWidget->GetOwner()); FVector pos = IIntenSelectableWidget::Execute_GetCoordinates(FocusedWidget->GetOwner());
GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0, FColor::Black, "C++ Pos: " + pos.ToString()); GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0, FColor::Black, "C++ Pos: " + pos.ToString());
WidgetFocusPoint = pos; WidgetFocusPoint = pos;
}else }
else
{ {
GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0, FColor::Black, "C++ Pos not available"); GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0, FColor::Black, "C++ Pos not available");
} }
}*/
} }
*/
}
TOptional<FHitResult> UIntenSelectComponent::RaytraceForFirstHit(const FVector& Start, const FVector& End) const TOptional<FHitResult> UIntenSelectComponent::RaytraceForFirstHit(const FVector& Start, const FVector& End) const
{ {
// will be filled by the Line Trace Function // Hit result to be filled by Line Trace function
FHitResult Hit; FHitResult Hit;
// Set up collision query parameters
FCollisionQueryParams Params; FCollisionQueryParams Params;
Params.AddIgnoredActor(GetOwner()->GetUniqueID()); // prevents actor hitting itself Params.AddIgnoredActor(GetOwner()->GetUniqueID()); // Ignore the owner actor to prevent hitting itself
// Perform line trace
if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECollisionChannel::ECC_Visibility, Params)) if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECollisionChannel::ECC_Visibility, Params))
{ {
return {Hit}; return {Hit};
} }
else else
{ {
return {}; return {}; // No hit
} }
} }
...@@ -507,78 +577,106 @@ TOptional<FHitResult> UIntenSelectComponent::RaytraceForFirstHit(const FVector& ...@@ -507,78 +577,106 @@ TOptional<FHitResult> UIntenSelectComponent::RaytraceForFirstHit(const FVector&
void UIntenSelectComponent::DrawSelectionCurve(const FVector& EndPoint) const void UIntenSelectComponent::DrawSelectionCurve(const FVector& EndPoint) const
{ {
const FVector StartPoint = this->GetComponentTransform().GetLocation(); // Get start point, forward vector, and set spline points
const FVector Forward = this->GetComponentTransform().GetRotation().GetForwardVector(); const FVector StartPoint = GetComponentTransform().GetLocation();
const FVector Forward = GetComponentTransform().GetRotation().GetForwardVector();
SplineComponent->ClearSplinePoints(true); SplineComponent->ClearSplinePoints(true);
SplineMeshComponent->SetHiddenInGame(false); SplineMeshComponent->SetHiddenInGame(false);
AddSplinePointsDefault(StartPoint, Forward, EndPoint); AddSplinePointsDefault(StartPoint, Forward, EndPoint);
// Get spline start and end positions and tangents
const FVector StartPosition = SplineComponent->GetLocationAtSplinePoint(0, ESplineCoordinateSpace::Local); const FVector StartPosition = SplineComponent->GetLocationAtSplinePoint(0, ESplineCoordinateSpace::Local);
const FVector StartTangent = SplineComponent->GetTangentAtSplinePoint(0, ESplineCoordinateSpace::Local); const FVector StartTangent = SplineComponent->GetTangentAtSplinePoint(0, ESplineCoordinateSpace::Local);
const FVector EndPosition = SplineComponent->GetLocationAtSplinePoint(1, ESplineCoordinateSpace::Local); const FVector EndPosition = SplineComponent->GetLocationAtSplinePoint(1, ESplineCoordinateSpace::Local);
const FVector EndTangent = SplineComponent->GetTangentAtSplinePoint(1, ESplineCoordinateSpace::Local); const FVector EndTangent = SplineComponent->GetTangentAtSplinePoint(1, ESplineCoordinateSpace::Local);
// Set start and end for spline mesh component
SplineMeshComponent->SetStartAndEnd(StartPosition, StartTangent, EndPosition, EndTangent, true); SplineMeshComponent->SetStartAndEnd(StartPosition, StartTangent, EndPosition, EndTangent, true);
} }
void UIntenSelectComponent::AddSplinePointsDefault(const FVector& StartPoint, const FVector& Forward, void UIntenSelectComponent::AddSplinePointsDefault(const FVector& StartPoint, const FVector& Forward,
const FVector& EndPoint) const const FVector& EndPoint) const
{ {
// Add start and end points to the spline
SplineComponent->AddSplineWorldPoint(StartPoint); SplineComponent->AddSplineWorldPoint(StartPoint);
const FVector StartToEnd = EndPoint - StartPoint;
const FVector ForwardProjection = StartToEnd.ProjectOnTo(Forward);
SplineComponent->AddSplineWorldPoint(EndPoint); SplineComponent->AddSplineWorldPoint(EndPoint);
// Set spline point types
SplineComponent->SetSplinePointType(0, ESplinePointType::Curve, true); SplineComponent->SetSplinePointType(0, ESplinePointType::Curve, true);
SplineComponent->SetSplinePointType(1, ESplinePointType::Curve, true); SplineComponent->SetSplinePointType(1, ESplinePointType::Curve, true);
SplineComponent->SetTangentAtSplinePoint(0, Forward * ForwardProjection.Size() * SplineCurvatureStrength, // Calculate tangents for smooth curve
ESplineCoordinateSpace::World, true); const FVector StartToEnd = EndPoint - StartPoint;
SplineComponent->SetTangentAtSplinePoint(1, StartToEnd.GetSafeNormal(), ESplineCoordinateSpace::World, true); const FVector ForwardProjection = StartToEnd.ProjectOnTo(Forward);
const FVector StartTangent = Forward * ForwardProjection.Size() * SplineCurvatureStrength;
const FVector EndTangent = StartToEnd.GetSafeNormal();
// Set tangents at spline points
SplineComponent->SetTangentAtSplinePoint(0, StartTangent, ESplineCoordinateSpace::World, true);
SplineComponent->SetTangentAtSplinePoint(1, EndTangent, ESplineCoordinateSpace::World, true);
} }
void UIntenSelectComponent::UpdateForwardRay(const FVector& ReferencePoint) const void UIntenSelectComponent::UpdateForwardRay(const FVector& ReferencePoint) const
{ {
// Check if transparency curve is available
if (ForwardRayTransparencyCurve) if (ForwardRayTransparencyCurve)
{ {
const FVector ConeForward = this->GetComponentTransform().GetRotation().GetForwardVector(); // Calculate cone forward vector and origin
const FVector ConeOrigin = const FVector ConeForward = GetComponentTransform().GetRotation().GetForwardVector();
this->GetComponentTransform().GetLocation() - (ConeForward * ConeBackwardShiftDistance); const FVector ConeOrigin = GetComponentTransform().GetLocation() - (ConeForward * ConeBackwardShiftDistance);
// Calculate angle to test point
const FVector TestPointVector = (ReferencePoint - ConeOrigin).GetSafeNormal(); const FVector TestPointVector = (ReferencePoint - ConeOrigin).GetSafeNormal();
const float AngleToTestPoint = const float AngleToTestPoint =
FMath::RadiansToDegrees(FMath::Acos((FVector::DotProduct(ConeForward, TestPointVector)))); FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(ConeForward, TestPointVector)));
// Calculate new transparency based on curve
const float NewTransparency = const float NewTransparency =
ForwardRayTransparencyCurve->GetFloatValue(AngleToTestPoint / SelectionConeAngle) * DebugRayTransparency; ForwardRayTransparencyCurve->GetFloatValue(AngleToTestPoint / SelectionConeAngle) * DebugRayTransparency;
// Set transparency parameter value in parameter collection
if (ParameterCollectionInstance)
{
ParameterCollectionInstance->SetScalarParameterValue("Transparency", NewTransparency); ParameterCollectionInstance->SetScalarParameterValue("Transparency", NewTransparency);
} }
else
{
UE_LOG(LogTemp, Warning, TEXT("ParameterCollectionInstance is null!"));
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("ForwardRayTransparencyCurve is null!"));
}
} }
// INPUT-HANDLING // INPUT-HANDLING
void UIntenSelectComponent::OnFireDown() void UIntenSelectComponent::OnFireDown()
{ {
// start interaction of WidgetInteractionComponent // Start interaction of WidgetInteractionComponent
PressPointerKey(EKeys::LeftMouseButton); PressPointerKey(EKeys::LeftMouseButton);
// Check if there is a current selection
if (!CurrentSelection) if (!CurrentSelection)
{ {
return; return;
} }
if (CurrentSelection) // Handle action start events for current selection
const FHitResult* GrabbedPoint = ContactPointMap.Find(CurrentSelection);
if (GrabbedPoint)
{ {
const FHitResult GrabbedPoint = *ContactPointMap.Find(CurrentSelection);
CurrentSelection->HandleOnActionStartEvents(this); CurrentSelection->HandleOnActionStartEvents(this);
LastKnownSelection = CurrentSelection; LastKnownSelection = CurrentSelection;
LastKnownGrabPoint = LastKnownGrabPoint =
LastKnownSelection->GetOwner()->GetRootComponent()->GetComponentTransform().InverseTransformPosition( LastKnownSelection->GetOwner()->GetRootComponent()->GetComponentTransform().InverseTransformPosition(
GrabbedPoint.ImpactPoint); GrabbedPoint->ImpactPoint);
} }
else else
{ {
...@@ -587,19 +685,22 @@ void UIntenSelectComponent::OnFireDown() ...@@ -587,19 +685,22 @@ void UIntenSelectComponent::OnFireDown()
IsGrabbing = true; IsGrabbing = true;
// Update transparency if required
if (bDrawForwardRay && ParameterCollectionInstance) if (bDrawForwardRay && ParameterCollectionInstance)
{ {
ParameterCollectionInstance->SetScalarParameterValue("Transparency", 0); ParameterCollectionInstance->SetScalarParameterValue("Transparency", 0);
} }
} }
void UIntenSelectComponent::OnFireUp() void UIntenSelectComponent::OnFireUp()
{ {
// end interaction of WidgetInteractionComponent // End interaction of WidgetInteractionComponent
ReleasePointerKey(EKeys::LeftMouseButton); ReleasePointerKey(EKeys::LeftMouseButton);
IsGrabbing = false; IsGrabbing = false;
// Handle action end events for last known selection
if (LastKnownSelection) if (LastKnownSelection)
{ {
FInputActionValue v; FInputActionValue v;
...@@ -607,92 +708,113 @@ void UIntenSelectComponent::OnFireUp() ...@@ -607,92 +708,113 @@ void UIntenSelectComponent::OnFireUp()
} }
} }
// SELECTION-HANDLING // SELECTION-HANDLING
void UIntenSelectComponent::SelectObject(UIntenSelectable* SelectableComponent, AActor* SelectedBy) void UIntenSelectComponent::SelectObject(UIntenSelectable* SelectableComponent, AActor* SelectedBy)
{ {
// Set the current selection to the specified selectable component
CurrentSelection = SelectableComponent; CurrentSelection = SelectableComponent;
} }
void UIntenSelectComponent::Unselect() void UIntenSelectComponent::Unselect()
{ {
// Stop grabbing
IsGrabbing = false; IsGrabbing = false;
// Hide spline mesh component
SplineMeshComponent->SetHiddenInGame(true); SplineMeshComponent->SetHiddenInGame(true);
// Reset current selection
CurrentSelection = nullptr; CurrentSelection = nullptr;
this->CurrentSelection = nullptr;
} }
void UIntenSelectComponent::SetActive(bool bNewActive, bool bReset) void UIntenSelectComponent::SetActive(bool bNewActive, bool bReset)
{ {
if (bNewActive) if (bNewActive)
{ {
// Show forward ray and spline mesh components
ForwardRayMeshComponent->SetVisibility(true); ForwardRayMeshComponent->SetVisibility(true);
SplineMeshComponent->SetVisibility(true); SplineMeshComponent->SetVisibility(true);
// Call superclass setActive function
Super::SetActive(true, bReset); Super::SetActive(true, bReset);
} }
else else
{ {
// If there is a current selection, handle no actor selected
if (CurrentSelection) if (CurrentSelection)
{ {
HandleNoActorSelected(); HandleNoActorSelected();
} }
// If there is a last known selection, end the fire action
if (LastKnownSelection) if (LastKnownSelection)
{ {
OnFireUp(); OnFireUp();
} }
// Hide forward ray and spline mesh components
ForwardRayMeshComponent->SetVisibility(false); ForwardRayMeshComponent->SetVisibility(false);
SplineMeshComponent->SetVisibility(false); SplineMeshComponent->SetVisibility(false);
// Call superclass setActive function
Super::SetActive(false, bReset); Super::SetActive(false, bReset);
} }
} }
// TICK // TICK
void UIntenSelectComponent::HandleCooldown(const float DeltaTime) void UIntenSelectComponent::HandleCooldown(const float DeltaTime)
{ {
// Reduce feedback cooldown by delta time
if (FeedbackCooldown > 0) if (FeedbackCooldown > 0)
{ {
FeedbackCooldown -= DeltaTime; FeedbackCooldown -= DeltaTime;
} }
// Ensure feedback cooldown does not go below 0
else else
{ {
FeedbackCooldown = 0; FeedbackCooldown = 0;
} }
} }
void UIntenSelectComponent::HandleGrabbing(const float DeltaTime) const {}
void UIntenSelectComponent::HandleActorSelected(UIntenSelectable* NewSelection) void UIntenSelectComponent::HandleActorSelected(UIntenSelectable* NewSelection)
{ {
// Check if the new selection is different from the current selection
if (NewSelection != CurrentSelection) if (NewSelection != CurrentSelection)
{ {
// If there is a current selection, handle hover end events
if (CurrentSelection) if (CurrentSelection)
{ {
CurrentSelection->HandleOnHoverEndEvents(this); CurrentSelection->HandleOnHoverEndEvents(this);
} }
// If there is a new selection, handle hover start events
if (NewSelection) if (NewSelection)
{ {
UIntenSelectable* NewIntenSelectable = NewSelection; const FHitResult* GrabbedPoint = ContactPointMap.Find(NewSelection);
const FHitResult GrabbedPoint = *ContactPointMap.Find(NewIntenSelectable); if (GrabbedPoint)
NewIntenSelectable->HandleOnHoverStartEvents(this, GrabbedPoint); {
NewSelection->HandleOnHoverStartEvents(this, *GrabbedPoint);
}
} }
// Set the new selection as the current selection and trigger new selected event
CurrentSelection = NewSelection; CurrentSelection = NewSelection;
OnNewSelected(NewSelection); OnNewSelected(NewSelection);
} }
// If there is a current selection, update forward ray and draw selection curve
if (CurrentSelection) if (CurrentSelection)
{ {
const UIntenSelectable* NewIntenSelectable = NewSelection; const FHitResult* GrabbedPoint = ContactPointMap.Find(NewSelection);
const auto V_Net = ContactPointMap.Find(NewIntenSelectable)->ImpactPoint; if (GrabbedPoint)
const FVector PointToDrawTo = ConvertNetVector(V_Net); {
const FVector PointToDrawTo = ConvertNetVector(GrabbedPoint->ImpactPoint);
if (bDrawForwardRay) if (bDrawForwardRay)
{ {
...@@ -702,41 +824,49 @@ void UIntenSelectComponent::HandleActorSelected(UIntenSelectable* NewSelection) ...@@ -702,41 +824,49 @@ void UIntenSelectComponent::HandleActorSelected(UIntenSelectable* NewSelection)
DrawSelectionCurve(PointToDrawTo); DrawSelectionCurve(PointToDrawTo);
} }
} }
}
FVector UIntenSelectComponent::ConvertNetVector(FVector_NetQuantize v) FVector UIntenSelectComponent::ConvertNetVector(FVector_NetQuantize v)
{ {
FVector Result; // Convert NetQuantize vector to FVector
Result.X = v.X; return FVector(v.X, v.Y, v.Z);
Result.Y = v.Y;
Result.Z = v.Z;
return Result;
} }
void UIntenSelectComponent::HandleNoActorSelected() void UIntenSelectComponent::HandleNoActorSelected()
{ {
// Hide spline mesh component
SplineMeshComponent->SetHiddenInGame(true); SplineMeshComponent->SetHiddenInGame(true);
// If there is a current selection, handle hover end events and reset current selection
if (CurrentSelection) if (CurrentSelection)
{ {
CurrentSelection->HandleOnHoverEndEvents(this); CurrentSelection->HandleOnHoverEndEvents(this);
CurrentSelection = nullptr;
} }
// Reset transparency parameter if drawing forward ray
if (bDrawForwardRay && ParameterCollectionInstance) if (bDrawForwardRay && ParameterCollectionInstance)
{ {
ParameterCollectionInstance->SetScalarParameterValue("Transparency", DebugRayTransparency); ParameterCollectionInstance->SetScalarParameterValue("Transparency", DebugRayTransparency);
} }
CurrentSelection = nullptr;
} }
void UIntenSelectComponent::TickComponent(const float DeltaTime, const ELevelTick TickType, void UIntenSelectComponent::TickComponent(const float DeltaTime, const ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) FActorComponentTickFunction* ThisTickFunction)
{ {
// Call parent tick function
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
this->HandleCooldown(DeltaTime); // Handle cooldown for feedback
HandleCooldown(DeltaTime);
// Get the new selection based on current parameters
UIntenSelectable* const NewSelection = GetMaxScoreActor(DeltaTime); UIntenSelectable* const NewSelection = GetMaxScoreActor(DeltaTime);
// If currently grabbing an object, update selection curve and check for angle constraints
if (IsGrabbing && LastKnownSelection) if (IsGrabbing && LastKnownSelection)
{ {
const FVector GrabPointWorld = const FVector GrabPointWorld =
...@@ -744,10 +874,10 @@ void UIntenSelectComponent::TickComponent(const float DeltaTime, const ELevelTic ...@@ -744,10 +874,10 @@ void UIntenSelectComponent::TickComponent(const float DeltaTime, const ELevelTic
LastKnownGrabPoint); LastKnownGrabPoint);
DrawSelectionCurve(GrabPointWorld); DrawSelectionCurve(GrabPointWorld);
const FVector ConeOrigin = this->GetComponentLocation(); const FVector ConeOrigin = GetComponentLocation();
const FVector ConeForward = this->GetForwardVector().GetSafeNormal(); const FVector ConeForward = GetForwardVector().GetSafeNormal();
if (!this->CheckPointInCone(ConeOrigin, ConeForward, GrabPointWorld, MaxClickStickAngle)) if (!CheckPointInCone(ConeOrigin, ConeForward, GrabPointWorld, MaxClickStickAngle))
{ {
OnFireUp(); OnFireUp();
} }
...@@ -760,11 +890,10 @@ void UIntenSelectComponent::TickComponent(const float DeltaTime, const ELevelTic ...@@ -760,11 +890,10 @@ void UIntenSelectComponent::TickComponent(const float DeltaTime, const ELevelTic
DrawSelectionCurve(GrabbedPoint); DrawSelectionCurve(GrabbedPoint);
} }
// this->HandleWidgetInteraction(); // Handle widget interaction
IsWidgetInFocus = false; IsWidgetInFocus = false;
if (IsWidgetInFocus) if (IsWidgetInFocus)
{ {
// GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0, FColor::Red, "Widget focused");
HandleNoActorSelected(); HandleNoActorSelected();
const FVector PointToDrawTo = WidgetFocusPoint; const FVector PointToDrawTo = WidgetFocusPoint;
...@@ -778,14 +907,13 @@ void UIntenSelectComponent::TickComponent(const float DeltaTime, const ELevelTic ...@@ -778,14 +907,13 @@ void UIntenSelectComponent::TickComponent(const float DeltaTime, const ELevelTic
} }
else else
{ {
// If there is a new selection, handle it; otherwise, handle no actor selected
if (NewSelection) if (NewSelection)
{ {
// GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0, FColor::Red, "Focused Actor:" + NewSelection->GetName());
HandleActorSelected(NewSelection); HandleActorSelected(NewSelection);
} }
else else
{ {
// GEngine->AddOnScreenDebugMessage(INDEX_NONE, 0, FColor::Red, "No Actor in Focus");
HandleNoActorSelected(); HandleNoActorSelected();
} }
} }
......
...@@ -143,71 +143,217 @@ public: ...@@ -143,71 +143,217 @@ public:
#pragma region /** INITIALIZATION */ #pragma region /** INITIALIZATION */
private: private:
/**
* \brief Initializes the input bindings for interacting with the component.
*/
void InitInputBindings(); void InitInputBindings();
/**
* \brief Initializes the debug cone mesh component used for visualization.
*/
void InitDebugConeMeshComponent(); void InitDebugConeMeshComponent();
/**
* \brief Initializes the spline mesh component used for visualization.
*/
void InitSplineMeshComponent(); void InitSplineMeshComponent();
/**
* \brief Initializes the spline component used for visualization.
*/
void InitSplineComponent(); void InitSplineComponent();
/**
* \brief Initializes the forward ray mesh component used for visualization.
*/
void InitForwardRayMeshComponent(); void InitForwardRayMeshComponent();
/**
* \brief Initializes the material parameter collection used for visualization.
*/
void InitMaterialParamCollection(); void InitMaterialParamCollection();
#pragma endregion #pragma endregion
#pragma region /** SCORING */ #pragma region /** SCORING */
private: private:
/**
* \brief Calculates the radius for sphere casting based on the selection cone angle and maximum selection distance.
* \return The calculated sphere cast radius.
*/
float CalculateSphereCastRadius() const; float CalculateSphereCastRadius() const;
/**
* \brief Retrieves actors from sphere casting to determine potential selections.
* \param SphereCastStart The starting point of the sphere cast.
* \param OutHits The array to store hit results.
* \return True if successful, false otherwise.
*/
bool GetActorsFromSphereCast(const FVector& SphereCastStart, TArray<FHitResult>& OutHits) const; bool GetActorsFromSphereCast(const FVector& SphereCastStart, TArray<FHitResult>& OutHits) const;
/**
* \brief Checks if a point is within the selection cone.
* \param ConeStartPoint The starting point of the selection cone.
* \param ConeForward The forward direction of the selection cone.
* \param PointToTest The point to test for inclusion in the cone.
* \param Angle The angle of the selection cone.
* \return True if the point is within the cone, false otherwise.
*/
bool CheckPointInCone(const FVector ConeStartPoint, const FVector ConeForward, const FVector PointToTest, bool CheckPointInCone(const FVector ConeStartPoint, const FVector ConeForward, const FVector PointToTest,
const float Angle) const; const float Angle) const;
/**
* \brief Determines the actor with the maximum score for selection.
* \param DeltaTime The time elapsed since the last frame.
* \return The actor with the maximum score for selection.
*/
UIntenSelectable* GetMaxScoreActor(const float DeltaTime); UIntenSelectable* GetMaxScoreActor(const float DeltaTime);
#pragma endregion #pragma endregion
#pragma region /** VISUALS */ #pragma region /** VISUALS */
private: private:
/**
* \brief Draws a selection curve from the component to the specified end point.
* \param EndPoint The end point of the selection curve.
*/
void DrawSelectionCurve(const FVector& EndPoint) const; void DrawSelectionCurve(const FVector& EndPoint) const;
/**
* \brief Adds default spline points for creating a spline between start and end points.
* \param StartPoint The starting point of the spline.
* \param Forward The forward direction of the spline.
* \param EndPoint The end point of the spline.
*/
void AddSplinePointsDefault(const FVector& StartPoint, const FVector& Forward, const FVector& EndPoint) const; void AddSplinePointsDefault(const FVector& StartPoint, const FVector& Forward, const FVector& EndPoint) const;
/**
* \brief Updates the forward ray visualization based on the reference point.
* \param ReferencePoint The reference point to update the forward ray to.
*/
void UpdateForwardRay(const FVector& ReferencePoint) const; void UpdateForwardRay(const FVector& ReferencePoint) const;
#pragma endregion #pragma endregion
#pragma region /** RAYCASTING */ #pragma region /** RAYCASTING */
private: private:
/**
* \brief Handles the interaction with widgets.
*/
void HandleWidgetInteraction(); void HandleWidgetInteraction();
/**
* \brief Performs a raycast from the start to the end point and returns the first hit result, if any.
* \param Start The starting point of the raycast.
* \param End The ending point of the raycast.
* \return The optional hit result of the raycast.
*/
TOptional<FHitResult> RaytraceForFirstHit(const FVector& Start, const FVector& End) const; TOptional<FHitResult> RaytraceForFirstHit(const FVector& Start, const FVector& End) const;
#pragma endregion #pragma endregion
#pragma region /** INPUT-HANDLING */ #pragma region /** INPUT-HANDLING */
private: private:
/**
* \brief Handles the input event when the fire button is pressed.
*/
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void OnFireDown(); void OnFireDown();
/**
* \brief Handles the input event when the fire button is released.
*/
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void OnFireUp(); void OnFireUp();
#pragma endregion #pragma endregion
#pragma region /** OTHER */ #pragma region /** OTHER */
private: private:
/**
* \brief Handles the cooldown mechanism based on the passed time interval.
* \param DeltaTime The time elapsed since the last frame.
*/
void HandleCooldown(const float DeltaTime); void HandleCooldown(const float DeltaTime);
void HandleGrabbing(const float DeltaTime) const;
/**
* \brief Handles the scenario when no actor is selected.
*/
void HandleNoActorSelected(); void HandleNoActorSelected();
/**
* \brief Handles the selection of a new actor.
* \param NewSelection The new selectable component to be selected.
*/
void HandleActorSelected(UIntenSelectable* NewSelection); void HandleActorSelected(UIntenSelectable* NewSelection);
/**
* \brief Converts a network quantized vector to a regular FVector.
* \param v The network quantized vector to convert.
* \return The converted FVector.
*/
FVector ConvertNetVector(FVector_NetQuantize v); FVector ConvertNetVector(FVector_NetQuantize v);
#pragma endregion #pragma endregion
public: public:
/**
* \brief Constructor for the UIntenSelectComponent class.
*/
UIntenSelectComponent(const FObjectInitializer& ObjectInitializer); UIntenSelectComponent(const FObjectInitializer& ObjectInitializer);
#pragma region /** SELECTION */ #pragma region /** SELECTION */
/**
* \brief Selects a given selectable component.
* \param SelectableComponent The selectable component to be selected.
* \param SelectedBy The actor responsible for the selection.
*/
void SelectObject(UIntenSelectable* SelectableComponent, AActor* SelectedBy); void SelectObject(UIntenSelectable* SelectableComponent, AActor* SelectedBy);
/**
* \brief Unselects the currently selected object.
*/
void Unselect(); void Unselect();
#pragma endregion #pragma endregion
/**
* \brief Sets whether the component is active or not.
* \param bNewActive The new active state of the component.
* \param bReset Whether the activation should happen even if ShouldActivate returns false.
*/
virtual void SetActive(bool bNewActive, bool bReset) override; virtual void SetActive(bool bNewActive, bool bReset) override;
/**
* \brief Blueprint-native event called when a new object is selected.
* \param Selection The newly selected object.
*/
UFUNCTION(BlueprintNativeEvent) UFUNCTION(BlueprintNativeEvent)
void OnNewSelected(UIntenSelectable* Selection); void OnNewSelected(UIntenSelectable* Selection);
protected: protected:
// Called when the game starts /**
* \brief Called when the game starts.
*/
virtual void BeginPlay() override; virtual void BeginPlay() override;
// Called every frame /**
* \brief Called every frame.
* \param DeltaTime The time since the last frame.
* \param TickType The type of tick.
* \param ThisTickFunction The tick function for this component.
*/
virtual void TickComponent(float DeltaTime, ELevelTick TickType, virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override; FActorComponentTickFunction* ThisTickFunction) override;
}; };
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment