Skip to content
Snippets Groups Projects
Commit c6e5f561 authored by Simon Oehrl's avatar Simon Oehrl
Browse files

Merge branch 'feature/pointing-ray' into 'develop'

add an interaction ray and make VRInteractionComponent derive from WidgetInteractionComponent so it can also handle UMG widgets

See merge request VR-Group/unreal-development/plugins/ndisplayextensions!41
parents ce42e1f0 139c5fc6
No related branches found
No related tags found
1 merge request!41add an interaction ray and make VRInteractionComponent derive from WidgetInteractionComponent so it can also handle UMG widgets
File added
File added
......@@ -10,6 +10,9 @@
#include "Interaction/GrabbingBehaviorComponent.h"
#include "Misc/Optional.h"
#include "DrawDebugHelpers.h"
#include "Components/WidgetComponent.h"
DEFINE_LOG_CATEGORY(LogVRInteractionComponent);
// Sets default values for this component's properties
UBasicVRInteractionComponent::UBasicVRInteractionComponent()
......@@ -18,7 +21,28 @@ UBasicVRInteractionComponent::UBasicVRInteractionComponent()
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
// Setup the interaction ray.
InteractionRay = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Interaction Ray"));
//this ray model has an inlayed cross with flipped normals so it can be seen as a cross in desktop mode where the right hand is attached to the head
ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/nDisplayExtensions/PointingRay/Ray_Mesh"));
if (MeshAsset.Object != nullptr)
{
InteractionRay->SetStaticMesh(MeshAsset.Object);
}
// turns off collisions as the InteractionRay is only meant to visualize the ray
InteractionRay->SetCollisionProfileName(TEXT("NoCollision"));
bShowDebug = false; //otherwise the WidgetInteractionComponent debug vis is shown
InteractionSource = EWidgetInteractionSource::Custom; //can also be kept at default (World), this way, however, we efficiently reuse the line traces
}
void UBasicVRInteractionComponent::BeginPlay()
{
Super::BeginPlay();
//WidgetInteractionComponent
InteractionDistance = MaxClickDistance;
SetInteractionRayVisibility(InteractionRayVisibility);
}
void UBasicVRInteractionComponent::BeginInteraction()
......@@ -32,6 +56,12 @@ void UBasicVRInteractionComponent::BeginInteraction()
return;
AActor* HitActor = Hit->GetActor();
//trigger interaction of WidgetInteractionComponent
SetCustomHitResult(Hit.GetValue());
//if !bCanRaytraceEveryTick, you have to click twice, since the first tick it only highlights and can't directly click
PressPointerKey(EKeys::LeftMouseButton);
if (HitActor->Implements<UGrabable>() && Hit->Distance < MaxGrabDistance)
{
......@@ -55,6 +85,9 @@ void UBasicVRInteractionComponent::BeginInteraction()
void UBasicVRInteractionComponent::EndInteraction()
{
if(!InteractionRayEmitter) return;
//end interaction of WidgetInteractionComponent
ReleasePointerKey(EKeys::LeftMouseButton);
// if we didnt grab anyone there is no need to release
if (GrabbedActor == nullptr)
......@@ -107,6 +140,10 @@ void UBasicVRInteractionComponent::TickComponent(float DeltaTime, ELevelTick Tic
if (!Hit.IsSet())
{
if(InteractionRayVisibility==EInteractionRayVisibility::VisibleOnHoverOnly)
{
InteractionRay->SetVisibility(false);
}
// Execute leave event on the actor that lost the focus if there was one
if (LastActorHit && LastActorHit->Implements<UTargetable>())
......@@ -141,6 +178,20 @@ void UBasicVRInteractionComponent::TickComponent(float DeltaTime, ELevelTick Tic
{
ITargetable::Execute_OnTargeted(HitActor, Hit->Location);
}
// widget interaction
SetCustomHitResult(Hit.GetValue());
if(InteractionRayVisibility==EInteractionRayVisibility::VisibleOnHoverOnly)
{
if(HitActor->Implements<UTargetable>() || HitActor->Implements<UClickable>() || IsOverInteractableWidget())
{
InteractionRay->SetVisibility(true);
}
else
{
InteractionRay->SetVisibility(false);
}
}
LastActorHit = HitActor; // Store the actor that was hit to have access to it in the next frame as well
}
......@@ -151,6 +202,38 @@ void UBasicVRInteractionComponent::Initialize(USceneComponent* RayEmitter, float
InteractionRayEmitter = RayEmitter;
MaxGrabDistance = InMaxGrabDistance;
MaxClickDistance = InMaxClickDistance;
InteractionRay->AttachToComponent(RayEmitter, FAttachmentTransformRules::KeepRelativeTransform);
InteractionRay->SetRelativeScale3D(FVector(MaxClickDistance/100.0f, 1.0f, 1.0f)); //the ray model has a length of 100cm
this->AttachToComponent(RayEmitter, FAttachmentTransformRules::KeepRelativeTransform);
}
void UBasicVRInteractionComponent::SetInteractionRayVisibility(EInteractionRayVisibility NewVisibility)
{
InteractionRayVisibility = NewVisibility;
if(InteractionRay)
{
switch (InteractionRayVisibility)
{
case Visible:
InteractionRay->SetVisibility(true);
break;
case VisibleOnHoverOnly:
case Invisible:
InteractionRay->SetVisibility(false);
break;
}
}
if(InteractionRayVisibility==EInteractionRayVisibility::VisibleOnHoverOnly && !bCanRaytraceEveryTick)
{
UE_LOG(LogVRInteractionComponent, Warning, TEXT("VisibleOnHoverOnly needs bCanRaytraceEveryTick=true, so this is set!"));
bCanRaytraceEveryTick=true;
}
if(InteractionRayVisibility==EInteractionRayVisibility::Visible && !bCanRaytraceEveryTick)
{
UE_LOG(LogVRInteractionComponent, Warning, TEXT("VisibleOnHoverOnly will need two clicks to interact with widgets if bCanRaytraceEveryTick is not set!"));
}
}
void UBasicVRInteractionComponent::HandlePhysicsAndAttachActor(AActor* HitActor)
......@@ -185,10 +268,9 @@ TOptional<FHitResult> UBasicVRInteractionComponent::RaytraceForFirstHit(const FT
// will be filled by the Line Trace Function
FHitResult Hit;
const FCollisionObjectQueryParams Params;
FCollisionQueryParams Params2;
Params2.AddIgnoredActor(GetOwner()->GetUniqueID()); // prevents actor hitting itself
if (GetWorld()->LineTraceSingleByObjectType(Hit, Start, End, Params, Params2))
FCollisionQueryParams Params;
Params.AddIgnoredActor(GetOwner()->GetUniqueID()); // prevents actor hitting itself
if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECollisionChannel::ECC_Visibility,Params))
return {Hit};
else
return {};
......
......@@ -3,14 +3,24 @@
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Components/WidgetInteractionComponent.h"
#include "BasicVRInteractionComponent.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogVRInteractionComponent, Log, All);
class UGrabbingBehaviorComponent;
UENUM()
enum EInteractionRayVisibility
{
Visible UMETA(DisplayName = "Interaction ray visible"),
VisibleOnHoverOnly UMETA(DisplayName = "Interaction ray only visible when hovering over Clickable or Targetable objects, or interactable widgets"),
Invisible UMETA(DisplayName = "Interaction ray invisible")
};
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DISPLAYCLUSTEREXTENSIONS_API UBasicVRInteractionComponent : public UActorComponent
class DISPLAYCLUSTEREXTENSIONS_API UBasicVRInteractionComponent : public UWidgetInteractionComponent
{
GENERATED_BODY()
......@@ -18,6 +28,8 @@ public:
// Sets default values for this component's properties
UBasicVRInteractionComponent();
void BeginPlay() override;
UFUNCTION(BlueprintCallable) void BeginInteraction();
UFUNCTION(BlueprintCallable) void EndInteraction();
......@@ -26,13 +38,16 @@ public:
UPROPERTY(BlueprintReadWrite) float MaxGrabDistance = 50;
UPROPERTY(BlueprintReadWrite) float MaxClickDistance = 500;
// Enable this if you want to interact with Targetable classes
// Enable this if you want to interact with Targetable classes or use EInteractionRayVisibility::VisibleOnHoverOnly
UPROPERTY(EditAnywhere) bool bCanRaytraceEveryTick = false;
UPROPERTY(EditAnywhere) TEnumAsByte<EInteractionRayVisibility> InteractionRayVisibility = EInteractionRayVisibility::Invisible;
UFUNCTION(BlueprintCallable) void Initialize(USceneComponent* RayEmitter, float InMaxGrabDistance = 50, float InMaxClickDistance = 500);
UFUNCTION(BlueprintCallable, BlueprintPure) AActor* GetGrabbedActor() const { return GrabbedActor;}
UFUNCTION(BlueprintCallable, BlueprintPure) USceneComponent* GetInteractionRayEmitter() const { return InteractionRayEmitter; }
UFUNCTION(BlueprintCallable) void SetInteractionRayVisibility(EInteractionRayVisibility NewVisibility);
private:
/* Holding a reference to the actor that is currently being grabbed */
UPROPERTY() AActor* GrabbedActor;
......@@ -40,6 +55,8 @@ private:
UPROPERTY() UPrimitiveComponent* ComponentSimulatingPhysics = nullptr;
UPROPERTY() UGrabbingBehaviorComponent* Behavior = nullptr;
UPROPERTY() USceneComponent* InteractionRayEmitter = nullptr;
UPROPERTY() UStaticMeshComponent* InteractionRay = nullptr;
/* Stores the reference of the Actor that was hit in the last frame*/
UPROPERTY() AActor* LastActorHit = nullptr;
void HandlePhysicsAndAttachActor(AActor* HitActor);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment