Replicated

[Drag Down] GAS관리 Actor Component로 분리 본문

언리얼 엔진/Drag Down (캡스톤 디자인)

[Drag Down] GAS관리 Actor Component로 분리

라구넹 2025. 4. 30. 17:26
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "AbilitySystemInterface.h"
#include "DDGASManagerComponent.generated.h"


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

public:	
	UDDGASManagerComponent();
	
	virtual class UAbilitySystemComponent* GetAbilitySystemComponent() const override;

	void SetASC();

	void SetGASAbilities();

	void SetupGASInputComponent();

protected:

	// Input Pressed RPC **************************************
	void GASInputPressed(int32 InputId);

	UFUNCTION(Server, Reliable)
	void ServerGASInputPressed(int32 InputId);

	void HandleGASInputPressed(int32 InputId);
	// ********************************************************

	// Input Released RPC *************************************
	void GASInputReleased(int32 InputId);

	UFUNCTION(Server, Reliable)
	void ServerGASInputReleased(int32 InputId);

	void HandleGASInputReleased(int32 InputId);
	// ********************************************************
	
	UPROPERTY(EditAnywhere, Category = GAS)
	TObjectPtr<class UAbilitySystemComponent> ASC;

	UPROPERTY(EditAnywhere, Category = GAS)
	TArray< TSubclassOf<class UGameplayAbility> > StartAbilities;

	UPROPERTY(EditAnywhere, Category = GAS)
	TMap< int32, TSubclassOf<class UGameplayAbility> > StartInputAbilities;

// Action
protected:
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<class UInputAction> PushingAction;

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

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


#include "ActorComponent/DDGASManagerComponent.h"
#include "AbilitySystemComponent.h"
#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"
#include "Player/DDPlayerState.h"
#include "Character/DDCharacterBase.h"
#include "DragDown.h"

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

	ASC = nullptr;

	static ConstructorHelpers::FObjectFinder<UInputAction> InputActionPushingRef(TEXT("/Script/EnhancedInput.InputAction'/Game/Input/Actions/IA_Pushing.IA_Pushing'"));
	if (nullptr != InputActionPushingRef.Object)
	{
		PushingAction = InputActionPushingRef.Object;
	}

	static ConstructorHelpers::FObjectFinder<UInputAction> InputActionDodgeRef(TEXT("/Script/EnhancedInput.InputAction'/Game/Input/Actions/IA_Dodge.IA_Dodge'"));
	if (nullptr != InputActionDodgeRef.Object)
	{
		DodgeAction = InputActionDodgeRef.Object;
	}
}

UAbilitySystemComponent* UDDGASManagerComponent::GetAbilitySystemComponent() const
{
	return ASC;
}

void UDDGASManagerComponent::SetASC()
{
	if (ASC) return;

	AActor* Owner = GetOwner();
	ensure(Cast<APawn>(Owner));

	ADDPlayerState* CSPS = Cast<APawn>(Owner)->GetPlayerState<ADDPlayerState>();
	if (CSPS)
	{
		ASC = CSPS->GetAbilitySystemComponent();
		ASC->InitAbilityActorInfo(CSPS, Owner);
		ASC->ReplicationMode = EGameplayEffectReplicationMode::Mixed;
		UE_LOG(LogDD, Log, TEXT("*** [NetMode : %d] SetASC, %s, %s"), GetWorld()->GetNetMode(), *GetName(), *Cast<APawn>(Owner)->GetPlayerState()->GetName());
	}
	else
	{
		UE_LOG(LogDD, Log, TEXT("[NetMode %d] SetASC - ASC Not Found"), GetWorld()->GetNetMode());
	}
}

void UDDGASManagerComponent::SetGASAbilities()
{
	if ( ASC && GetOwner()->HasAuthority() )
	{
		for (const auto& StartAbility : StartAbilities)
		{
			FGameplayAbilitySpec StartSpec(StartAbility); 
			ASC->GiveAbility(StartSpec); 
		}

		for (const auto& StartInputAbility : StartInputAbilities) 
		{
			FGameplayAbilitySpec StartSpec(StartInputAbility.Value); 
			StartSpec.InputID = StartInputAbility.Key; 
			ASC->GiveAbility(StartSpec); 
		}
	}
}

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

		//추가 gas 액션
		EnhancedInputComponent->BindAction(PushingAction, ETriggerEvent::Triggered, this, &UDDGASManagerComponent::GASInputPressed, 1);
		EnhancedInputComponent->BindAction(PushingAction, ETriggerEvent::Completed, this, &UDDGASManagerComponent::GASInputReleased, 1);

		EnhancedInputComponent->BindAction(DodgeAction, ETriggerEvent::Triggered, this, &UDDGASManagerComponent::GASInputPressed, 2);
		EnhancedInputComponent->BindAction(DodgeAction, ETriggerEvent::Completed, this, &UDDGASManagerComponent::GASInputReleased, 2);

		UE_LOG(LogDD, Log, TEXT("SetupGASInputComponent Succeed"));
	}
	else if (!IsValid(ASC))
	{
		UE_LOG(LogDD, Log, TEXT("Invalid ASC"));
	}
	else
	{
		UE_LOG(LogDD, Log, TEXT("Invalid InputComponent"));
	}
}

void UDDGASManagerComponent::GASInputPressed(int32 InputId)
{
	ensure(Cast<ADDCharacterBase>(GetOwner()));
	if ( !Cast<ADDCharacterBase>(GetOwner())->GetActionEnabled() ) return;

	HandleGASInputPressed(InputId);

	if (!GetOwner()->HasAuthority())
	{
		ServerGASInputPressed(InputId);
	}
}

void UDDGASManagerComponent::HandleGASInputPressed(int32 InputId)
{
	if (!ASC)
	{
		return;
	}

	FGameplayAbilitySpec* Spec = ASC->FindAbilitySpecFromInputID(InputId);
	if (Spec)
	{
		if (Spec->InputPressed) return;
		UE_LOG(LogDD, Log, TEXT("InputPressed"));
		Spec->InputPressed = true;
		if (Spec->IsActive())
		{
			ASC->AbilitySpecInputPressed(*Spec);
		}
		else
		{
			ASC->TryActivateAbility(Spec->Handle, true);
		}
	}
}

void UDDGASManagerComponent::GASInputReleased(int32 InputId)
{
	HandleGASInputReleased(InputId);

	if (!GetOwner()->HasAuthority())
	{
		ServerGASInputReleased(InputId);
	}
}

void UDDGASManagerComponent::HandleGASInputReleased(int32 InputId)
{
	if (!ASC)
	{
		return;
	}

	FGameplayAbilitySpec* Spec = ASC->FindAbilitySpecFromInputID(InputId);
	if (Spec)
	{
		Spec->InputPressed = false;
		if (Spec->IsActive())
		{
			ASC->AbilitySpecInputReleased(*Spec);
		}
	}
}

void UDDGASManagerComponent::ServerGASInputPressed_Implementation(int32 InputId)
{
	if (GetOwner()->HasAuthority())
	{
		HandleGASInputPressed(InputId);
	}
}

void UDDGASManagerComponent::ServerGASInputReleased_Implementation(int32 InputId)
{
	if ( GetOwner()->HasAuthority() )
	{
		HandleGASInputReleased(InputId);
	}
}

기존 캐릭터의 GAS 구조를 분리했다

이제 캐릭터는 ASC조차 가지지 않는다!

(이제 캐릭터에서 Ability System Blueprint로 가져오는 건 불가능, 캐릭터에 IAbilitySystemComponent의 GetAbilitySystemComponent 있어야 됨)

 

void ADDCharacterPlayer::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);

	GASManagerComponent->SetASC();
	GASManagerComponent->SetGASAbilities();
	BuffManagerComponent->Initailize();
}

void ADDCharacterPlayer::OnRep_PlayerState()
{
	Super::OnRep_PlayerState();

	GASManagerComponent->SetASC();
	BuffManagerComponent->Initailize();
}

초기화는 기존 방식에서 함수 콜만 해주자