Replicated

[Drag Down] 맵 이동 포탈 본문

언리얼 엔진/Drag Down

[Drag Down] 맵 이동 포탈

라구넹 2025. 5. 21. 21:13
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "DDInteractable.generated.h"

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UDDInteractable : public UInterface
{
	GENERATED_BODY()
};

/**
 * 
 */
class DRAGDOWN_API IDDInteractable
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	virtual void Interact(AActor* InterActor) = 0;

	virtual bool CanInteract(AActor* InterActor) = 0;

	virtual void BeginInteract(AActor* InterActor) = 0;

	virtual void EndInteract(AActor* InterActor) = 0;

	virtual FText GetInteractionText(AActor* InterActor) = 0;
};

인터렉션 용도의 인터페이스

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "DDInteractionManagerComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DRAGDOWN_API UDDInteractionManagerComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	UDDInteractionManagerComponent();

	virtual void BeginPlay() override;

// Input
public:
	void SetInteractionInputComponent();

protected:
	void InteractionInputPressed();

	UFUNCTION(Server, Reliable)
	void ServerInteractionInputPressed();

	void HandleInteractionInputPressed();

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class UInputAction> InteractAction;

protected:
	UFUNCTION()
	void OnComponentBeginOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult);

	UFUNCTION()
	void OnComponentEndOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

	TObjectPtr<AActor> CurrentInteractionActor;
};

// Fill out your copyright notice in the Description page of Project Settings.


#include "ActorComponent/DDInteractionManagerComponent.h"
#include "Character/DDCharacterBase.h"
#include "Components/CapsuleComponent.h"
#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"
#include "Interface/DDInteractable.h"
#include "DragDown.h"

// Sets default values for this component's properties
UDDInteractionManagerComponent::UDDInteractionManagerComponent()
{
	SetIsReplicatedByDefault(true);
}


void UDDInteractionManagerComponent::BeginPlay()
{
	Super::BeginPlay();

	ADDCharacterBase* Character = Cast<ADDCharacterBase>(GetOwner());
	if ( Character && Character->GetTrigger() )
	{
		Character->GetTrigger()->OnComponentBeginOverlap.AddDynamic(this, &UDDInteractionManagerComponent::OnComponentBeginOverlapCallback);
		Character->GetTrigger()->OnComponentEndOverlap.AddDynamic(this, &UDDInteractionManagerComponent::OnComponentEndOverlapCallback);
	}
}

void UDDInteractionManagerComponent::SetInteractionInputComponent()
{
	if ( IsValid( GetOwner()->InputComponent ) )
	{
		UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(GetOwner()->InputComponent);

		EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Started, this, &UDDInteractionManagerComponent::InteractionInputPressed); 
	}
}

void UDDInteractionManagerComponent::InteractionInputPressed()
{
	if ( GetOwner()->HasAuthority() )
	{
		HandleInteractionInputPressed();
	}
	else
	{
		ServerInteractionInputPressed(); 
	}
}

void UDDInteractionManagerComponent::HandleInteractionInputPressed()
{
	if ( CurrentInteractionActor )
	{
		IDDInteractable* Interaction = Cast<IDDInteractable>(CurrentInteractionActor);
		Interaction->Interact(GetOwner());
	}
}

void UDDInteractionManagerComponent::ServerInteractionInputPressed_Implementation()
{
	HandleInteractionInputPressed(); 
}

void UDDInteractionManagerComponent::OnComponentBeginOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult)
{
	IDDInteractable* Interaction = Cast<IDDInteractable>(OtherActor);

	if ( Interaction )
	{
		Interaction->BeginInteract(GetOwner());
		CurrentInteractionActor = OtherActor;
	}
}

void UDDInteractionManagerComponent::OnComponentEndOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	IDDInteractable* Interaction = Cast<IDDInteractable>(OtherActor);

	if (Interaction)
	{
		Interaction->EndInteract(GetOwner());
		CurrentInteractionActor = nullptr;
	}
}

UDDInteractionManagerComponent를 만들어서 F를 누르면 인터렉션할 수 있게 만들었음

 

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Interface/DDInteractable.h"
#include "DDPortalActorBase.generated.h"

UCLASS()
class DRAGDOWN_API ADDPortalActorBase : public AActor, public IDDInteractable
{
	GENERATED_BODY()
	
public:	
	ADDPortalActorBase();

protected:
	virtual void BeginPlay() override;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	TObjectPtr<class UStaticMeshComponent> StaticMeshComp;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	TObjectPtr<class USphereComponent> Trigger;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	TObjectPtr<class UWidgetComponent> InteractionPromptComponent;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	TObjectPtr<class UDDPromptWidget> PromptWidget;

public:
	virtual void Interact(AActor* InterActor) override;

	virtual bool CanInteract(AActor* InterActor) override;

	virtual void BeginInteract(AActor* InterActor) override;

	virtual void EndInteract(AActor* InterActor) override;

	virtual FText GetInteractionText(AActor* InterActor) override;

protected:
	const FText DefaultDescription = FText::FromString( TEXT("Press < F >") );
};


// Fill out your copyright notice in the Description page of Project Settings.


#include "Actor/DDPortalActorBase.h"
#include "Components/StaticMeshComponent.h"
#include "Components/SphereComponent.h"
#include "Components/WidgetComponent.h"
#include "UI/DDPromptWidget.h"
#include "Physics/DDCollision.h"
#include "DragDown.h"

ADDPortalActorBase::ADDPortalActorBase()
{
	StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComp"));
	StaticMeshComp->SetIsReplicated(true);
	StaticMeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	RootComponent = StaticMeshComp;

	Trigger = CreateDefaultSubobject<USphereComponent>(TEXT("Trigger"));
	Trigger->SetSphereRadius(80.0f, true);
	Trigger->SetCollisionProfileName( CPROFILE_OVERLAPALL );
	Trigger->SetupAttachment(StaticMeshComp);

	InteractionPromptComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("InteractionPromptComponent"));
	InteractionPromptComponent->SetupAttachment(StaticMeshComp);
	InteractionPromptComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 130.0f));
	InteractionPromptComponent->SetWidgetSpace(EWidgetSpace::Screen);
	InteractionPromptComponent->SetDrawSize(FVector2D(500.0f, 30.0f));
	InteractionPromptComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	InteractionPromptComponent->SetVisibility(false);
}

void ADDPortalActorBase::BeginPlay()
{
	Super::BeginPlay();
	
	UUserWidget* UserWidget = InteractionPromptComponent->GetWidget(); 

	if ( UserWidget ) 
	{
		PromptWidget = Cast<UDDPromptWidget>(UserWidget);
	}
}

void ADDPortalActorBase::Interact(AActor* InterActor)
{
	if (!CanInteract(InterActor)) return;
}

bool ADDPortalActorBase::CanInteract(AActor* InterActor)
{
	return true;
}

void ADDPortalActorBase::BeginInteract(AActor* InterActor)
{
	APawn* Pawn = Cast<APawn>(InterActor);
	if ( Pawn && !Pawn->IsLocallyControlled() )
	{
		return;
	}

	if ( InteractionPromptComponent && PromptWidget )
	{
		PromptWidget->SetDescription( GetInteractionText(InterActor) );
		InteractionPromptComponent->SetVisibility(true);
	}
	else
	{
		UE_LOG(LogDD, Error, TEXT("BeginInteract is failed"));
	}
}

void ADDPortalActorBase::EndInteract(AActor* InterActor)
{
	APawn* Pawn = Cast<APawn>(InterActor);
	if (Pawn && !Pawn->IsLocallyControlled())
	{
		return;
	}

	if ( InteractionPromptComponent )
	{
		InteractionPromptComponent->SetVisibility(false);
	}
}

FText ADDPortalActorBase::GetInteractionText(AActor* InterActor)
{
	return DefaultDescription;
}

포탈의 베이스가 되는 클래스

로컬에서만 Visibility 갱신을 시킬 수 있도록 했다,

InteractionManagerComponent가 트리거에서 겹치고 나가는 것을 관리하여 설명 위젯을 보이게 하거나 안보이게 한다

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "DDPromptWidget.generated.h"

/**
 * 
 */
UCLASS()
class DRAGDOWN_API UDDPromptWidget : public UUserWidget
{
	GENERATED_BODY()
	
public:
	UFUNCTION(BlueprintCallable)
	void SetDescription( const FText& InDescription );

protected:
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<class UTextBlock> Description;

};

// Fill out your copyright notice in the Description page of Project Settings.


#include "UI/DDPromptWidget.h"
#include "Components/TextBlock.h"

void UDDPromptWidget::SetDescription(const FText& InDescription)
{
	if ( Description )
	{
		Description->SetText(InDescription);
	}
}

인터렉션 설명을 해줄 위젯

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Actor/DDPortalActorBase.h"
#include "DDWaitingToGamePortalActor.generated.h"

/**
 * 
 */
UCLASS()
class DRAGDOWN_API ADDWaitingToGamePortalActor : public ADDPortalActorBase
{
	GENERATED_BODY()
	
public:
	virtual void BeginPlay() override;

	virtual void Interact(AActor* InterActor) override;

	virtual bool CanInteract(AActor* InterActor) override;

	virtual void BeginInteract(AActor* InterActor) override;

	virtual FText GetInteractionText(AActor* InterActor) override;

	void UpdateDescription();

	UFUNCTION()
	void UpdateAndSetDescription();

protected:
	FText CurrentDescription = FText::FromString(TEXT("Not Initialized"));
	const FText StartDescription = FText::FromString(TEXT("Press F To Start"));
	const FText NotHostDescription = FText::FromString(TEXT("Only Host Can Start"));
	const FText NotReadyDescription = FText::FromString(TEXT("Not All Players Are Ready"));
};


// Fill out your copyright notice in the Description page of Project Settings.


#include "Actor/DDWaitingToGamePortalActor.h"
#include "Game/DDWaitingGameState.h"
#include "UI/DDPromptWidget.h"

void ADDWaitingToGamePortalActor::BeginPlay()
{
	Super::BeginPlay();

	ADDWaitingGameState* GameState = Cast<ADDWaitingGameState>(GetWorld()->GetGameState());
	if ( GameState )
	{
		GameState->OnGameInfoChanged.AddDynamic(this, &ADDWaitingToGamePortalActor::UpdateAndSetDescription);
	}
}

void ADDWaitingToGamePortalActor::Interact(AActor* InterActor)
{
	Super::Interact(InterActor);

	if ( CanInteract(InterActor) )
	{
		GetWorld()->ServerTravel("L_Map_Game");
	}
}

bool ADDWaitingToGamePortalActor::CanInteract(AActor* InterActor)
{
	if ( !InterActor->HasAuthority() )
	{
		return false;
	}

	ADDWaitingGameState* GameState = Cast<ADDWaitingGameState>(GetWorld()->GetGameState());

	if (GameState)
	{
		if (GameState->AreAllPlayerReady())
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	return false;
}

void ADDWaitingToGamePortalActor::BeginInteract(AActor* InterActor)
{
	UpdateDescription();

	Super::BeginInteract(InterActor);
}

FText ADDWaitingToGamePortalActor::GetInteractionText(AActor* InterActor)
{
	return CurrentDescription;
}

void ADDWaitingToGamePortalActor::UpdateDescription()
{
	if ( !HasAuthority() )
	{
		CurrentDescription = NotHostDescription;
		return;
	}

	ADDWaitingGameState* GameState = Cast<ADDWaitingGameState>(GetWorld()->GetGameState());

	if (GameState)
	{
		if (GameState->AreAllPlayerReady())
		{
			CurrentDescription = StartDescription;
		}
		else
		{
			CurrentDescription = NotReadyDescription;
		}
	}
}

void ADDWaitingToGamePortalActor::UpdateAndSetDescription()
{
	UpdateDescription();
	
	if ( PromptWidget )
	{
		PromptWidget->SetDescription(CurrentDescription);
	}
}

대기방 -> 게임 이동시키는 포탈

플레이어가 호스트인지 아닌지, 모든 플레이어가 레디 상태인지 체크하여 다른 안내를 한다