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
This commit is part of merge request !91. Comments created here will be created in the context of that merge request.
...@@ -5,9 +5,24 @@ ...@@ -5,9 +5,24 @@
#include "Interaction/Interactables/InteractableComponent.h" #include "Interaction/Interactables/InteractableComponent.h"
#include "Interaction/Interactables/InteractionEventType.h" #include "Interaction/Interactables/InteractionEventType.h"
#include "Logging/StructuredLog.h" #include "Logging/StructuredLog.h"
#include "Pawn/Navigation/CollisionHandlingMovement.h"
#include "Serialization/JsonTypes.h" #include "Serialization/JsonTypes.h"
#include "Utility/RWTHVRUtilities.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) UPrimitiveComponent* UGrabBehavior::GetFirstComponentSimulatingPhysics(const AActor* TargetActor)
{ {
TArray<UPrimitiveComponent*> PrimitiveComponents; TArray<UPrimitiveComponent*> PrimitiveComponents;
...@@ -34,7 +49,38 @@ UPrimitiveComponent* UGrabBehavior::GetHighestParentSimulatingPhysics(UPrimitive ...@@ -34,7 +49,38 @@ UPrimitiveComponent* UGrabBehavior::GetHighestParentSimulatingPhysics(UPrimitive
return Comp; 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, void UGrabBehavior::OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
const FInputActionValue& Value) const FInputActionValue& Value)
{ {
...@@ -52,6 +98,7 @@ bool UGrabBehavior::TryRelease() ...@@ -52,6 +98,7 @@ bool UGrabBehavior::TryRelease()
{ {
if (!bObjectGrabbed) if (!bObjectGrabbed)
{ {
UE_LOGFMT(Toolkit, Display, "UGrabBehavior::TryRelease: bObjectGrabbed was false!");
return false; return false;
} }
...@@ -109,6 +156,9 @@ void UGrabBehavior::StartGrab(USceneComponent* TriggerComponent) ...@@ -109,6 +156,9 @@ void UGrabBehavior::StartGrab(USceneComponent* TriggerComponent)
} }
OnGrabStartEvent.Broadcast(CurrentAttachParent, MyPhysicsComponent); OnGrabStartEvent.Broadcast(CurrentAttachParent, MyPhysicsComponent);
// Add to ignore list for collision handling movement, if it exists
HandleCollisionHandlingMovement(CurrentAttachParent, InteractionStart);
} }
void UGrabBehavior::EndGrab(USceneComponent* TriggerComponent) void UGrabBehavior::EndGrab(USceneComponent* TriggerComponent)
...@@ -136,4 +186,7 @@ void UGrabBehavior::EndGrab(USceneComponent* TriggerComponent) ...@@ -136,4 +186,7 @@ void UGrabBehavior::EndGrab(USceneComponent* TriggerComponent)
Interactable->ResetRestrictInteraction(); Interactable->ResetRestrictInteraction();
} }
} }
// If our attach parent has a collision handling component, remove
HandleCollisionHandlingMovement(CurrentAttachParent, InteractionEnd);
} }
...@@ -74,6 +74,9 @@ void UInteractableComponent::HandleOnHoverEvents(USceneComponent* TriggerCompone ...@@ -74,6 +74,9 @@ void UInteractableComponent::HandleOnHoverEvents(USceneComponent* TriggerCompone
return; return;
} }
InteractorComponent->RequestHoverBehaviourReplicationStart(HoverBehaviour, EventType, HitResult); InteractorComponent->RequestHoverBehaviourReplicationStart(HoverBehaviour, EventType, HitResult);
// Broadcast local callback
HoverBehaviour->OnHoverReplicationStartedOriginatorEvent.Broadcast(TriggerComponent, EventType, HitResult);
} }
else if (HoverBehaviour->bExecuteOnAllClients) else if (HoverBehaviour->bExecuteOnAllClients)
{ {
...@@ -122,6 +125,9 @@ void UInteractableComponent::HandleOnActionEvents(USceneComponent* TriggerCompon ...@@ -122,6 +125,9 @@ void UInteractableComponent::HandleOnActionEvents(USceneComponent* TriggerCompon
return; return;
} }
InteractorComponent->RequestActionBehaviourReplicationStart(ActionBehaviour, EventType, Value); InteractorComponent->RequestActionBehaviourReplicationStart(ActionBehaviour, EventType, Value);
// Broadcast local callback
ActionBehaviour->OnActionReplicationStartedOriginatorEvent.Broadcast(TriggerComponent, EventType, Value);
} }
else if (ActionBehaviour->bExecuteOnAllClients) else if (ActionBehaviour->bExecuteOnAllClients)
{ {
......
#include "Pawn/Navigation/CollisionHandlingMovement.h" #include "Pawn/Navigation/CollisionHandlingMovement.h"
#include "Kismet/KismetSystemLibrary.h" #include "Kismet/KismetSystemLibrary.h"
#include "Logging/StructuredLog.h"
#include "Utility/RWTHVRUtilities.h"
UCollisionHandlingMovement::UCollisionHandlingMovement(const FObjectInitializer& ObjectInitializer) : UCollisionHandlingMovement::UCollisionHandlingMovement(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer) Super(ObjectInitializer)
...@@ -32,6 +34,13 @@ void UCollisionHandlingMovement::BeginPlay() ...@@ -32,6 +34,13 @@ void UCollisionHandlingMovement::BeginPlay()
void UCollisionHandlingMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, void UCollisionHandlingMovement::TickComponent(float DeltaTime, enum ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) FActorComponentTickFunction* ThisTickFunction)
{
if (ShouldSkipUpdate(DeltaTime))
return;
const AController* Controller = PawnOwner->GetController();
if (Controller && Controller->IsLocalController())
{ {
SetCapsuleColliderToUserSize(); SetCapsuleColliderToUserSize();
...@@ -89,6 +98,7 @@ void UCollisionHandlingMovement::TickComponent(float DeltaTime, enum ELevelTick ...@@ -89,6 +98,7 @@ void UCollisionHandlingMovement::TickComponent(float DeltaTime, enum ELevelTick
// just remove whatever input is there // just remove whatever input is there
ConsumeInputVector(); ConsumeInputVector();
} }
}
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
} }
...@@ -102,6 +112,34 @@ void UCollisionHandlingMovement::SetHeadComponent(USceneComponent* NewHeadCompon ...@@ -102,6 +112,34 @@ void UCollisionHandlingMovement::SetHeadComponent(USceneComponent* NewHeadCompon
CapsuleColliderComponent->SetWorldLocation(FVector(0.0f, 0.0f, -HalfHeight)); 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 void UCollisionHandlingMovement::SetCapsuleColliderToUserSize() const
{ {
// the collider should be placed // the collider should be placed
......
...@@ -25,6 +25,17 @@ public: ...@@ -25,6 +25,17 @@ public:
UPROPERTY(BlueprintAssignable) UPROPERTY(BlueprintAssignable)
FOnActionBegin OnActionEventEvent; 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: protected:
UFUNCTION() UFUNCTION()
virtual void OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType, virtual void OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType,
......
...@@ -20,11 +20,14 @@ class RWTHVRTOOLKIT_API UGrabBehavior : public UActionBehaviour ...@@ -20,11 +20,14 @@ class RWTHVRTOOLKIT_API UGrabBehavior : public UActionBehaviour
GENERATED_BODY() GENERATED_BODY()
public: public:
UGrabBehavior();
UPROPERTY(EditAnywhere, Category = "Grabbing") UPROPERTY(EditAnywhere, Category = "Grabbing")
bool bBlockOtherInteractionsWhileGrabbed = true; bool bBlockOtherInteractionsWhileGrabbed = true;
virtual void OnActionEvent(USceneComponent* TriggerComponent, const EInteractionEventType EventType, UPROPERTY(EditAnywhere, Category = "Grabbing")
const FInputActionValue& Value) override; bool bIgnoreGrabbedActorInCollisionMovement = true;
/** /**
* Called after the object was successfully attached to the hand * Called after the object was successfully attached to the hand
...@@ -38,13 +41,24 @@ public: ...@@ -38,13 +41,24 @@ public:
UPROPERTY(BlueprintAssignable) UPROPERTY(BlueprintAssignable)
FOnGrabEnd OnGrabEndEvent; FOnGrabEnd OnGrabEndEvent;
UPROPERTY()
UPrimitiveComponent* MyPhysicsComponent;
virtual void BeginPlay() override;
UPrimitiveComponent* GetFirstComponentSimulatingPhysics(const AActor* TargetActor); UPrimitiveComponent* GetFirstComponentSimulatingPhysics(const AActor* TargetActor);
// recursively goes up the hierarchy and returns the highest parent simulating physics // recursively goes up the hierarchy and returns the highest parent simulating physics
UPrimitiveComponent* GetHighestParentSimulatingPhysics(UPrimitiveComponent* Comp); UPrimitiveComponent* GetHighestParentSimulatingPhysics(UPrimitiveComponent* Comp);
UPROPERTY() UFUNCTION()
UPrimitiveComponent* MyPhysicsComponent; 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) UFUNCTION(BlueprintPure)
bool IsObjectGrabbed() const { return bObjectGrabbed; } bool IsObjectGrabbed() const { return bObjectGrabbed; }
...@@ -65,4 +79,6 @@ private: ...@@ -65,4 +79,6 @@ private:
bool bObjectGrabbed = false; bool bObjectGrabbed = false;
bool bWasSimulatingPhysics; bool bWasSimulatingPhysics;
bool bWasAddedToIgnore = false;
}; };
...@@ -24,6 +24,16 @@ public: ...@@ -24,6 +24,16 @@ public:
UPROPERTY(BlueprintAssignable) UPROPERTY(BlueprintAssignable)
FOnHoverEvent OnHoverEventEvent; 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: protected:
UFUNCTION() UFUNCTION()
virtual void OnHoverEvent(const USceneComponent* TriggerComponent, EInteractionEventType EventType, virtual void OnHoverEvent(const USceneComponent* TriggerComponent, EInteractionEventType EventType,
......
...@@ -37,16 +37,43 @@ public: ...@@ -37,16 +37,43 @@ public:
* Replication part * 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, void RequestHoverBehaviourReplicationStart(UHoverBehaviour* Behaviour, const EInteractionEventType EventType,
const FHitResult& Hit); 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, void HoverBehaviourReplicationStart(UHoverBehaviour* Behaviour, const EInteractionEventType EventType,
const FHitResult& Hit); 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, void RequestActionBehaviourReplicationStart(UActionBehaviour* Behaviour, const EInteractionEventType EventType,
const FInputActionValue& Value); 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, void ActionBehaviourReplicationStart(UActionBehaviour* Behaviour, const EInteractionEventType EventType,
const FInputActionValue& Value); const FInputActionValue& Value);
// RPCs // RPCs
UFUNCTION(Server, Reliable) UFUNCTION(Server, Reliable)
void ServerRequestHoverBehaviourReplicationStartRpc(UHoverBehaviour* Behaviour, void ServerRequestHoverBehaviourReplicationStartRpc(UHoverBehaviour* Behaviour,
......
...@@ -42,6 +42,12 @@ public: ...@@ -42,6 +42,12 @@ public:
void SetHeadComponent(USceneComponent* NewHeadComponent); void SetHeadComponent(USceneComponent* NewHeadComponent);
UFUNCTION(BlueprintCallable)
bool AddActorToIgnore(AActor* ActorToIgnore);
UFUNCTION(BlueprintCallable)
bool RemoveActorFromIgnore(AActor* ActorToIgnore);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement")
EVRNavigationModes NavigationMode = EVRNavigationModes::NAV_WALK; 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