Skip to content
Snippets Groups Projects
Commit 0d5f5bf7 authored by David Gilbert's avatar David Gilbert :bug:
Browse files

feature(interaction, replication): Adds first draft of a replicated GrabBehaviour.

parent f19cf591
No related branches found
No related tags found
2 merge requests!107UE5.4-2024.1-rc1,!91Interaction refactoring and replication
Pipeline #433584 failed
......@@ -5,9 +5,24 @@
#include "Interaction/Interactables/InteractableComponent.h"
#include "Interaction/Interactables/InteractionEventType.h"
#include "Logging/StructuredLog.h"
#include "Pawn/Navigation/CollisionHandlingMovement.h"
#include "Serialization/JsonTypes.h"
#include "Utility/RWTHVRUtilities.h"
UGrabBehavior::UGrabBehavior()
{
SetIsReplicatedByDefault(true);
bExecuteOnServer = true;
bExecuteOnAllClients = false;
}
void UGrabBehavior::BeginPlay()
{
Super::BeginPlay();
OnActionReplicationStartedOriginatorEvent.AddDynamic(this, &UGrabBehavior::ReplicationOriginaterClientCallback);
}
UPrimitiveComponent* UGrabBehavior::GetFirstComponentSimulatingPhysics(const AActor* TargetActor)
{
TArray<UPrimitiveComponent*> PrimitiveComponents;
......@@ -34,7 +49,38 @@ UPrimitiveComponent* UGrabBehavior::GetHighestParentSimulatingPhysics(UPrimitive
return Comp;
}
void UGrabBehavior::ReplicationOriginaterClientCallback(USceneComponent* TriggerComponent,
const EInteractionEventType EventType,
const FInputActionValue& Value)
{
const USceneComponent* CurrentAttachParent = Cast<USceneComponent>(TriggerComponent->GetAttachParent());
HandleCollisionHandlingMovement(CurrentAttachParent, EventType);
}
void UGrabBehavior::HandleCollisionHandlingMovement(const USceneComponent* CurrentAttachParent, const EInteractionEventType EventType)
{
auto CHM = CurrentAttachParent->GetOwner()->GetComponentByClass<UCollisionHandlingMovement>();
if(!CHM)
return;
if (EventType == EInteractionEventType::InteractionStart)
{
// Add to ignore list for collision handling movement, if it exists
if (bIgnoreGrabbedActorInCollisionMovement)
{
bWasAddedToIgnore = CHM->AddActorToIgnore(GetOwner());
}
}
else
{
// If our attach parent has a collision handling component, remove
if (bWasAddedToIgnore)
{
CHM->RemoveActorFromIgnore(GetOwner());
}
}
}
void UGrabBehavior::OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
const FInputActionValue& Value)
{
......@@ -52,6 +98,7 @@ bool UGrabBehavior::TryRelease()
{
if (!bObjectGrabbed)
{
UE_LOGFMT(Toolkit, Display, "UGrabBehavior::TryRelease: bObjectGrabbed was false!");
return false;
}
......@@ -109,6 +156,9 @@ void UGrabBehavior::StartGrab(USceneComponent* TriggerComponent)
}
OnGrabStartEvent.Broadcast(CurrentAttachParent, MyPhysicsComponent);
// Add to ignore list for collision handling movement, if it exists
HandleCollisionHandlingMovement(CurrentAttachParent, InteractionStart);
}
void UGrabBehavior::EndGrab(USceneComponent* TriggerComponent)
......@@ -136,4 +186,7 @@ void UGrabBehavior::EndGrab(USceneComponent* TriggerComponent)
Interactable->ResetRestrictInteraction();
}
}
// If our attach parent has a collision handling component, remove
HandleCollisionHandlingMovement(CurrentAttachParent, InteractionEnd);
}
......@@ -74,6 +74,9 @@ void UInteractableComponent::HandleOnHoverEvents(USceneComponent* TriggerCompone
return;
}
InteractorComponent->RequestHoverBehaviourReplicationStart(HoverBehaviour, EventType, HitResult);
// Broadcast local callback
HoverBehaviour->OnHoverReplicationStartedOriginatorEvent.Broadcast(TriggerComponent, EventType, HitResult);
}
else if (HoverBehaviour->bExecuteOnAllClients)
{
......@@ -122,6 +125,9 @@ void UInteractableComponent::HandleOnActionEvents(USceneComponent* TriggerCompon
return;
}
InteractorComponent->RequestActionBehaviourReplicationStart(ActionBehaviour, EventType, Value);
// Broadcast local callback
ActionBehaviour->OnActionReplicationStartedOriginatorEvent.Broadcast(TriggerComponent, EventType, Value);
}
else if (ActionBehaviour->bExecuteOnAllClients)
{
......
#include "Pawn/Navigation/CollisionHandlingMovement.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Logging/StructuredLog.h"
#include "Utility/RWTHVRUtilities.h"
UCollisionHandlingMovement::UCollisionHandlingMovement(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
......@@ -32,6 +34,13 @@ void UCollisionHandlingMovement::BeginPlay()
void UCollisionHandlingMovement::TickComponent(float DeltaTime, enum ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
if (ShouldSkipUpdate(DeltaTime))
return;
const AController* Controller = PawnOwner->GetController();
if (Controller && Controller->IsLocalController())
{
SetCapsuleColliderToUserSize();
......@@ -89,6 +98,7 @@ void UCollisionHandlingMovement::TickComponent(float DeltaTime, enum ELevelTick
// just remove whatever input is there
ConsumeInputVector();
}
}
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
......@@ -102,6 +112,34 @@ void UCollisionHandlingMovement::SetHeadComponent(USceneComponent* NewHeadCompon
CapsuleColliderComponent->SetWorldLocation(FVector(0.0f, 0.0f, -HalfHeight));
}
bool UCollisionHandlingMovement::AddActorToIgnore(AActor* ActorToIgnore)
{
if (ActorToIgnore && IsValid(ActorToIgnore))
{
ActorsToIgnore.AddUnique(ActorToIgnore);
return true;
}
else
{
UE_LOGFMT(Toolkit, Warning, "UCollisionHandlingMovement::AddActorToIgnore: Cannot add invalid actor");
return false;
}
}
bool UCollisionHandlingMovement::RemoveActorFromIgnore(AActor* ActorToIgnore)
{
if (ActorToIgnore && IsValid(ActorToIgnore))
{
const int32 NumRemoved = ActorsToIgnore.Remove(ActorToIgnore);
return NumRemoved > 0;
}
else
{
UE_LOGFMT(Toolkit, Warning, "UCollisionHandlingMovement::RemoveActorFromIgnore: Cannot remove invalid actor");
return false;
}
}
void UCollisionHandlingMovement::SetCapsuleColliderToUserSize() const
{
// the collider should be placed
......
......@@ -25,6 +25,17 @@ public:
UPROPERTY(BlueprintAssignable)
FOnActionBegin OnActionEventEvent;
/**
* Replication specific:
* This is only executed on the local client which processed the interaction and requested the replication process
* to be started. Can be used e.g. for local effects or things that should be done both on the server and local
* client. Broadcast by UInteractableComponent when the originating client sends a server rpc to start the
* interaction replication.
*/
UPROPERTY(BlueprintAssignable)
FOnActionBegin OnActionReplicationStartedOriginatorEvent;
protected:
UFUNCTION()
virtual void OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
......
......@@ -20,11 +20,14 @@ class RWTHVRTOOLKIT_API UGrabBehavior : public UActionBehaviour
GENERATED_BODY()
public:
UGrabBehavior();
UPROPERTY(EditAnywhere, Category = "Grabbing")
bool bBlockOtherInteractionsWhileGrabbed = true;
virtual void OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
const FInputActionValue& Value) override;
UPROPERTY(EditAnywhere, Category = "Grabbing")
bool bIgnoreGrabbedActorInCollisionMovement = true;
/**
* Called after the object was successfully attached to the hand
......@@ -38,13 +41,24 @@ public:
UPROPERTY(BlueprintAssignable)
FOnGrabEnd OnGrabEndEvent;
UPROPERTY()
UPrimitiveComponent* MyPhysicsComponent;
virtual void BeginPlay() override;
UPrimitiveComponent* GetFirstComponentSimulatingPhysics(const AActor* TargetActor);
// recursively goes up the hierarchy and returns the highest parent simulating physics
UPrimitiveComponent* GetHighestParentSimulatingPhysics(UPrimitiveComponent* Comp);
UPROPERTY()
UPrimitiveComponent* MyPhysicsComponent;
UFUNCTION()
void ReplicationOriginaterClientCallback(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
const FInputActionValue& Value);
void HandleCollisionHandlingMovement(const USceneComponent* CurrentAttachParent, const EInteractionEventType EventType);
virtual void OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
const FInputActionValue& Value) override;
UFUNCTION(BlueprintPure)
bool IsObjectGrabbed() const { return bObjectGrabbed; }
......@@ -65,4 +79,6 @@ private:
bool bObjectGrabbed = false;
bool bWasSimulatingPhysics;
bool bWasAddedToIgnore = false;
};
......@@ -24,6 +24,16 @@ public:
UPROPERTY(BlueprintAssignable)
FOnHoverEvent OnHoverEventEvent;
/**
* Replication specific:
* This is only executed on the local client which processed the interaction and requested the replication process
* to be started. Can be used e.g. for local effects or things that should be done both on the server and local
* client. Broadcast by UInteractableComponent when the originating client sends a server rpc to start the
* interaction replication.
*/
UPROPERTY(BlueprintAssignable)
FOnHoverEvent OnHoverReplicationStartedOriginatorEvent;
protected:
UFUNCTION()
virtual void OnHoverEvent(const USceneComponent* TriggerComponent, EInteractionEventType EventType,
......
......@@ -37,16 +37,43 @@ public:
* Replication part
*/
/**
* Requests the start of the replication process for the given HoverBehaviour, EventType and Hit.
* Depending on authority, this executes the replication of the behaviour directly or requests it via a server RPC.
*/
void RequestHoverBehaviourReplicationStart(UHoverBehaviour* Behaviour, const EInteractionEventType EventType,
const FHitResult& Hit);
/**
* This is executed on the server/authority. The behaviour is actually executed directly on the server, or
* multicast to all connected clients. The multicast then executes the behaviour.
*/
void HoverBehaviourReplicationStart(UHoverBehaviour* Behaviour, const EInteractionEventType EventType,
const FHitResult& Hit);
/**
* This is only executed on the local client which processed the interaction and requested the replication process
* to be started. Can be used e.g. for local effects or things that should be done both on the server and local client.
*/
void HoverBehaviourReplicationOriginatorCallback(UHoverBehaviour* Behaviour, const EInteractionEventType EventType,
const FHitResult& Hit);
/**
* Requests the start of the replication process for the given ActionBehaviour, EventType and the Value of the Input Action.
* Depending on authority, this executes the replication of the behaviour directly or requests it via a server RPC.
*/
void RequestActionBehaviourReplicationStart(UActionBehaviour* Behaviour, const EInteractionEventType EventType,
const FInputActionValue& Value);
/**
* This is executed on the server/authority. The behaviour is actually executed directly on the server, or
* multicast to all connected clients. The multicast then executes the behaviour.
*/
void ActionBehaviourReplicationStart(UActionBehaviour* Behaviour, const EInteractionEventType EventType,
const FInputActionValue& Value);
// RPCs
UFUNCTION(Server, Reliable)
void ServerRequestHoverBehaviourReplicationStartRpc(UHoverBehaviour* Behaviour,
......
......@@ -42,6 +42,12 @@ public:
void SetHeadComponent(USceneComponent* NewHeadComponent);
UFUNCTION(BlueprintCallable)
bool AddActorToIgnore(AActor* ActorToIgnore);
UFUNCTION(BlueprintCallable)
bool RemoveActorFromIgnore(AActor* ActorToIgnore);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement")
EVRNavigationModes NavigationMode = EVRNavigationModes::NAV_WALK;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment