Skip to content
Snippets Groups Projects
Select Git revision
  • 0d5f5bf71a15b40f111d91d1739b399cfe366707
  • 5.4 default protected
  • 5.5
  • dev/5.5
  • dev/5.4
  • dev/5.3_downgrade
  • feature/experimenttime_hack
  • 5.3 protected
  • _IntenSelect5.3
  • IntenSelect5.3
  • 4.27 protected
  • 4.26 protected
  • 5.0 protected
  • 4.22 protected
  • 4.21 protected
  • UE5.4-2024.1
  • UE5.4-2024.1-rc1
  • UE5.3-2023.1-rc3
  • UE5.3-2023.1-rc2
  • UE5.3-2023.1-rc
20 results

InteractableComponent.cpp

Blame
  • InteractableComponent.cpp 6.29 KiB
    // Fill out your copyright notice in the Description page of Project Settings.
    
    
    #include "Interaction/Interactables/InteractableComponent.h"
    #include "Interaction/Interactables/ActionBehaviour.h"
    #include "Interaction/Interactables/HoverBehaviour.h"
    #include "Interaction/Interactors/UBaseInteractionComponent.h"
    #include "Logging/StructuredLog.h"
    #include "Utility/RWTHVRUtilities.h"
    
    void UInteractableComponent::RestrictInteractionToComponents(const TArray<USceneComponent*>& Components)
    {
    	if (Components.IsEmpty())
    	{
    		bRestrictInteraction = false;
    		AllowedComponents.Empty();
    	}
    	else
    	{
    		bRestrictInteraction = true;
    		AllowedComponents = Components;
    	}
    }
    
    void UInteractableComponent::RestrictInteractionToComponent(USceneComponent* Component)
    {
    	TArray<USceneComponent*> Components;
    	Components.Add(Component);
    	RestrictInteractionToComponents(Components);
    }
    
    void UInteractableComponent::ResetRestrictInteraction()
    {
    	bRestrictInteraction = false;
    	AllowedComponents.Empty();
    }
    
    // Called when the game starts
    void UInteractableComponent::BeginPlay()
    {
    	Super::BeginPlay();
    	InitDefaultBehaviourReferences();
    }
    
    void UInteractableComponent::HandleOnHoverEvents(USceneComponent* TriggerComponent, const EInteractorType Interactor,
    												 const EInteractionEventType EventType)
    {
    	// We early return if there the InteractorFilter is set and the Interactor is allowed.
    	if (!(InteractorFilter == EInteractorType::None || InteractorFilter & Interactor))
    		return;
    
    	// We early return if the source Interactor is not part of the allowed components
    	if (!IsComponentAllowed(TriggerComponent))
    		return;
    
    	// Broadcast event to all HoverBehaviours
    	for (UHoverBehaviour* HoverBehaviour : OnHoverBehaviours)
    	{
    		// Check if we need to replicate the action to the server
    		if (HoverBehaviour->bExecuteOnServer)
    		{
    			// Request the server to execute our behaviour.
    			// As we can only execute RPCs from an owned object, and we don't necessarily own this actor, pipe it
    			// through the interactor. Because behaviours can also be triggered from non-interactors, only do this on a
    			// successful cast
    
    			auto* InteractorComponent = Cast<UUBaseInteractionComponent>(TriggerComponent);
    			if (!InteractorComponent)
    			{
    				UE_LOGFMT(Toolkit, Warning,
    						  "Interaction: TriggerComponent {TriggerComponent} is not a UUBaseInteractionComponent. Only "
    						  "UUBaseInteractionComponent TriggerComponents can be replicated.",
    						  TriggerComponent->GetName());
    				return;
    			}
    			InteractorComponent->RequestHoverBehaviourReplicationStart(HoverBehaviour, EventType, HitResult);
    
    			// Broadcast local callback
    			HoverBehaviour->OnHoverReplicationStartedOriginatorEvent.Broadcast(TriggerComponent, EventType, HitResult);
    		}
    		else if (HoverBehaviour->bExecuteOnAllClients)
    		{
    			UE_LOGFMT(Toolkit, Warning,
    					  "Interaction: Behaviour {BehaviourName} has bExecuteOnAllClients=true, which requires "
    					  "bExecuteOnServer also set to true, which is not the case.",
    					  HoverBehaviour->GetName());
    		}
    		else // skip replication altogether (default case)
    		{
    			HoverBehaviour->OnHoverEventEvent.Broadcast(TriggerComponent, EventType, HitResult);
    		}
    	}
    }
    
    // This functions dispatches the ActionStart Event to the attached Action Behaviour Components
    void UInteractableComponent::HandleOnActionEvents(USceneComponent* TriggerComponent, const EInteractorType Interactor,
    												  const EInteractionEventType EventType, const FInputActionValue& Value)
    {
    	// We early return if there the InteractorFilter is set and the Interactor is allowed.
    	if (!(InteractorFilter == EInteractorType::None || InteractorFilter & Interactor))
    		return;
    
    	// We early return if the source Interactor is not part of the allowed components
    	if (!IsComponentAllowed(TriggerComponent))
    		return;
    
    	// Broadcast event to all ActionBehaviours
    	for (UActionBehaviour* ActionBehaviour : OnActionBehaviours)
    	{
    		// Check if we need to replicate the action to the server
    		if (ActionBehaviour->bExecuteOnServer)
    		{
    			// Request the server to execute our behaviour.
    			// As we can only execute RPCs from an owned object, and we don't necessarily own this actor, pipe it
    			// through the interactor. Because behaviours can also be triggered from non-interactors, only do this on a
    			// successful cast
    
    			auto* InteractorComponent = Cast<UUBaseInteractionComponent>(TriggerComponent);
    			if (!InteractorComponent)
    			{
    				UE_LOGFMT(Toolkit, Warning,
    						  "Interaction: TriggerComponent {TriggerComponent} is not a UUBaseInteractionComponent. Only "
    						  "UUBaseInteractionComponent TriggerComponents can be replicated.",
    						  TriggerComponent->GetName());
    				return;
    			}
    			InteractorComponent->RequestActionBehaviourReplicationStart(ActionBehaviour, EventType, Value);
    
    			// Broadcast local callback
    			ActionBehaviour->OnActionReplicationStartedOriginatorEvent.Broadcast(TriggerComponent, EventType, Value);
    		}
    		else if (ActionBehaviour->bExecuteOnAllClients)
    		{
    			UE_LOGFMT(Toolkit, Warning,
    					  "Interaction: Behaviour {BehaviourName} has bExecuteOnAllClients=true, which requires "
    					  "bExecuteOnServer also set to true, which is not the case.",
    					  ActionBehaviour->GetName());
    		}
    		else // skip replication altogether (default case)
    		{
    			ActionBehaviour->OnActionEventEvent.Broadcast(TriggerComponent, EventType, Value);
    		}
    	}
    }
    
    // This function searches for Action and Hover Behaviours that are attached to the Actor iff they are not set
    // manually by the user.
    void UInteractableComponent::InitDefaultBehaviourReferences()
    {
    	// only do this if empty, otherwise the user has explicitly stated, which behaviors to include
    	if (OnHoverBehaviours.IsEmpty())
    	{
    		// Selecting
    		TInlineComponentArray<UHoverBehaviour*> AttachedHoverBehaviours;
    		GetOwner()->GetComponents(AttachedHoverBehaviours, true);
    
    		OnHoverBehaviours = AttachedHoverBehaviours;
    	}
    
    	// only do this if empty, otherwise the user has explicitly stated, which behaviors to include
    	if (OnActionBehaviours.IsEmpty())
    	{
    		// Clicking
    		TInlineComponentArray<UActionBehaviour*> AttachedClickBehaviours;
    		GetOwner()->GetComponents(AttachedClickBehaviours, true);
    
    		OnActionBehaviours = AttachedClickBehaviours;
    	}
    }
    
    bool UInteractableComponent::IsComponentAllowed(USceneComponent* Component) const
    {
    	if (bRestrictInteraction)
    	{
    		if (!AllowedComponents.Contains(Component))
    		{
    			return false;
    		}
    	}
    	return true;
    }