Skip to content
Snippets Groups Projects
Commit ecc7256e authored by Sebastian Pape's avatar Sebastian Pape
Browse files

Adding BasicVRInteractionComponent to encapsulate clicking and grabbing behavior

parent 2b67de5d
No related branches found
No related tags found
No related merge requests found
// Fill out your copyright notice in the Description page of Project Settings.
#include "BasicVRInteractionComponent.h"
#include "Clickable.h"
#include "Grabable.h"
#include "GrabbingBehaviorComponent.h"
// Sets default values for this component's properties
UBasicVRInteractionComponent::UBasicVRInteractionComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
void UBasicVRInteractionComponent::BeginInteraction()
{
if(!InteractionRayEmitter) return;
// start and end point for raytracing
const FTwoVectors StartEnd = GetHandRay(MaxClickDistance);
const FVector Start = StartEnd.v1;
const FVector End = StartEnd.v2;
// will be filled by the Line Trace Function
FHitResult Hit;
//if hit was not found return
const FCollisionObjectQueryParams Params;
if (!GetWorld()->LineTraceSingleByObjectType(Hit, Start, End, Params))
return;
AActor* HitActor = Hit.GetActor();
// try to cast HitActor int a Grabable if not succeeded will become a nullptr
IGrabable* GrabableActor = Cast<IGrabable>(HitActor);
IClickable* ClickableActor = Cast<IClickable>(HitActor);
if (GrabableActor != nullptr && Hit.Distance < MaxGrabDistance)
{
// call grabable actors function so he reacts to our grab
GrabableActor->OnGrabbed_Implementation();
UGrabbingBehaviorComponent* Behavior = HitActor->FindComponentByClass<UGrabbingBehaviorComponent>();
if ( Behavior == nullptr)
HandlePhysicsAndAttachActor(HitActor);
// we save the grabbedActor in a general form to access all of AActors functions easily later
GrabbedActor = HitActor;
}
else if (ClickableActor != nullptr && Hit.Distance < MaxClickDistance)
{
ClickableActor->OnClicked_Implementation(Hit.Location);
}
}
void UBasicVRInteractionComponent::EndInteraction()
{
if(!InteractionRayEmitter) return;
// if we didnt grab anyone there is no need to release
if (GrabbedActor == nullptr)
return;
// let the grabbed object reacot to release
Cast<IGrabable>(GrabbedActor)->OnReleased_Implementation();
// Detach the Actor
UPrimitiveComponent* PhysicsComp = GrabbedActor->FindComponentByClass<UPrimitiveComponent>();
UGrabbingBehaviorComponent* Behavior = GrabbedActor->FindComponentByClass<UGrabbingBehaviorComponent>();
if (Behavior == nullptr)
{
GrabbedActor->GetRootComponent()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
PhysicsComp->SetSimulatePhysics(bDidSimulatePhysics);
}
// forget about the actor
GrabbedActor = nullptr;
}
// Called every frame
void UBasicVRInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// if an actor is grabbed and a behavior is defined move move him accordingly
if (GrabbedActor == nullptr || InteractionRayEmitter == nullptr) return;
UGrabbingBehaviorComponent* Behavior = GrabbedActor->FindComponentByClass<UGrabbingBehaviorComponent>();
// if our Grabable Actor is not constrained
if (Behavior != nullptr)
{
// specifies the hand in space
const FVector HandPos = InteractionRayEmitter->GetComponentLocation();
const FQuat HandQuat = InteractionRayEmitter->GetComponentQuat();
Behavior->HandleNewPositionAndDirection(HandPos, HandQuat);
}
}
void UBasicVRInteractionComponent::Initialize(USceneComponent* RayEmitter, float InMaxGrabDistance, float InMaxClickDistance)
{
if(RayEmitter != nullptr) return;
InteractionRayEmitter = RayEmitter;
MaxGrabDistance = InMaxGrabDistance;
MaxClickDistance = InMaxClickDistance;
}
void UBasicVRInteractionComponent::HandlePhysicsAndAttachActor(AActor* HitActor)
{
UPrimitiveComponent* PhysicsComp = HitActor->FindComponentByClass<UPrimitiveComponent>();
bDidSimulatePhysics = PhysicsComp->IsSimulatingPhysics(); // remember if we need to tun physics back on or not
PhysicsComp->SetSimulatePhysics(false);
FAttachmentTransformRules Rules = FAttachmentTransformRules(EAttachmentRule::KeepWorld, true);
HitActor->AttachToComponent(InteractionRayEmitter, Rules);
}
FTwoVectors UBasicVRInteractionComponent::GetHandRay(const float Length) const
{
const FVector Start = InteractionRayEmitter->GetComponentLocation();
const FVector Direction = InteractionRayEmitter->GetForwardVector();
const FVector End = Start + Length * Direction;
return FTwoVectors(Start, End);
}
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BasicVRInteractionComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DISPLAYCLUSTEREXTENSIONS_API UBasicVRInteractionComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UBasicVRInteractionComponent();
UFUNCTION(BlueprintCallable) void BeginInteraction();
UFUNCTION(BlueprintCallable) void EndInteraction();
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY(BlueprintReadWrite) float MaxGrabDistance = 50;
UPROPERTY(BlueprintReadWrite) float MaxClickDistance = 500;
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; }
private:
/* indicates if the grabbed actor was simulating physics before we grabbed it */
UPROPERTY() bool bDidSimulatePhysics;
/* Holding a reference to the actor that is currently being grabbed */
UPROPERTY() AActor* GrabbedActor;
UPROPERTY() USceneComponent* InteractionRayEmitter = nullptr;
void HandlePhysicsAndAttachActor(AActor* HitActor);
FTwoVectors GetHandRay(float Length) const;
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment